关于SDRAM存储器

一、对SDRAM的初步认识

1.1 什么是SDRAM

SDRAM(Synchronous Dynamic Random Access Memory),同步动态随机存取存储器。

同步:工作频率与对应控制器的系统时钟频率相同,且内存内部的命令以及数据的传输都以此为基准

动态:SDRAM利用电容存储数据,掉电数据丢失,因此存储序列需要通过不断刷新来保证数据不丢失

随机:数据不是线性存储的(fifo),可随机指定地址进行读写。


1.2 SDRAM的行列地址

首先给出一位数据存取电路如下:

要想将这一位数据读取出,我们需要先打开行地址(激活/active),然后打开列地址,则电容的电平状态即可呈现在data_bit上,从而实现了一位数据的读取。或者说,数据线上的电平状态被送到了电容上,实现一位数据的写入。
在这里插入图片描述

从打开行地址到能够打开列地址之间有一个时间间隔——tRCD(器件不同,该值也不同)
.
列地址打开,到数据呈现在data_bit上也有一定的时钟延迟,称为列选通潜伏期(CL)


由此我们可得到SDRAM内部行列地址的简化模型,其中每一行列,只展示1bit的数据。
当我们指定行列地址线,即可得到对应的存储单元。
在这里插入图片描述


如图中指定行地址=3,列地址=5,可找到对应的黑色处的存储单元。
在这里插入图片描述
上图中的存储单元可存放宽位为8/16/32的数据。对于整个行列组成的块来说,称为逻辑Bank(L-Bank)

若采用一个Bank可能导致寻址冲突,因此大部分SDRAM内部都以4个逻辑bank设计,数据读写的流程:

指定逻辑Bank地址——指定行地址——指定列地址——确定寻址存储单元。

注意:一次只能对一个 Bank 的一个存储单元进行操作。


1.3 SDRAM容量的计算

一个存储单元的容量等于数据总线的位宽,单位是bit。因此总的SDRAM存储容量:

SDRAM总存储容量 = L-Bank的数量×行数×列数×存储单元的容量

若SDRAM的行地址13bit,列地址9bit,4个bank,16bit数据,则

SDRAM容量 = 4 × 2^13 × 2^9 × 16 = 268435456 bit / 8 = 33554432 (B )= 33554432 / 1024 (KB) = 32768 /1024 MB = 32MB


1.4 SDRAM引脚与定义

x4 - x16:分别表示4、8、16位宽
#:表示低电平有效;
— :表示 x8 和 x4 引脚功能与 x16 引脚功能相同。
在这里插入图片描述
定义如下:
CLK:SDRAM工作时钟,时钟上升沿进行输入采样
BAn[1:0]:Bank地址线
DQn [15:0]:双向数据总线
An[12:0]:地址线,当选择某个Bank的行地址时,要用到A0-A11;当选择列地址的时候,用A0-A8,A10信号可以用来控制Auto-precharge自动预充电
在这里插入图片描述
跟着大佬学习SDRAM

1.5 SDRAM内部结构框图

通常SDRAM有四个Bank,如Bank0 ~ Bank3;
为实现寻址及其他功能,其内部还有一系列控制器,命令解码器(通过发送来的命令操作内存条,命令操作如:SDRAM初始化及读写命令等)、逻辑控制单元、地址寄存器、数据寄存器等。

详细来说:外部通过CS_N、RAC_N、CAS_N、WE_N 这四个信号构成命令信号以及地址总线(片选信号确定bank,行列选通信号给定行列地址,WE给予写使能),然后命令经过命令解码器进行译码后,将控制参数保存到模式寄存器中,逻辑控制单元就能控制逻辑运行。
在这里插入图片描述

二、SDRAM操作命令

基本操作命令如下:

#:表示低电平有效
NOP:空操作
预充电:就是将数据写回到电容,因此要打开行地址,为保证下一次读的时候数据未丢失
在这里插入图片描述
2.1 空命令

不论 SDRAM 处于何种状态,此命令均可被写 入,该命令能给被选中的SDRAM 传递一个空操作,目的是为了防止 SDRAM 处于空闲或等待状态时,其他命令被写入。如上图中的前两个,没有选中bank以及选中bank,但没有给与行列及写使能的情况下,都为NOP命令。


2.2 加载模式寄存器命令

1、内部结构框图中我们看到有mode register ,在SDRAM上电初始化的时候(所有的Bank都处于空闲状态),执行配置模式寄存器命令后,SDRAM 必须等待相应的响应时间 tMRD(Register Set Cycle)后,才可执行新的命令。

2、模式寄存器命令通过地址总线 A0-A11 配置,不同的赋值对应寄存器配置的不同模式,未使用的地址总线A12设置为低电平,同时模式寄存器将会一直保存配置信息,直到下一次编程或者掉电为止。

如下是模式寄存器地址总线定义A12-A0 :
在这里插入图片描述

A0-A2 控制突发长度,Brust Length:

突发是同一行中相邻存储单元连续对数据传输,其中连续的数据量就是突发长度BL。

典型的突发方式是1,2,4,8,Full Page,其中全页突发传输是指逻辑Bank里一行中的所有存储单元从头到尾的传输,因此全页一次可传输一整行的数据量。


A3控制突发类型,BT:

A3 = 0:顺序突发;A3 = 1:隔行突发


A6,A5,A4控制列选通潜伏期,CAS Latency

从读命令被寄存,到 数据出现在数据总线上的时间间隔称为列选通给潜伏期,可被设置为 2 或 3 个时钟周期
在这里插入图片描述
可看到,读命令给出后,在T2的时候数据总线上出现第一个有效数据,因此CL = 2;
在这里插入图片描述
同理,上图CL = 3 ;


A7,A8控制运行模式,Operating Mode

SDRAM 存在标准模式、测试模式等多种模式,{A7,A8} = {0,0} ,进入标准模式.。


A9控制写模式,WB

当 A9 =0,SDRAM 的读/写均采用突发方式,突发长度由突发长度寄存器(A0-A2)设定;
当 A9 =1 时, SDRAM 的读采用突发,突发长度由突发长度寄存器(A0-A2)设定,但 写不使用突发方式,每一个写命令只能写入一个数据。


A10-A12预留位,Reserver

对模式寄存器的配置不起作用,赋值为 0 即可。


有关命令的解释:

激活命令(active)

控制激活命令 {CS_N,RAS_N,CAS_N,WE_N} = 4‘b0011.

激活命令是用来打开一个特定的Bank(由BA0和BA1决定)和指定的行(由A0-A12决定)。
该行会一直保持激活状态并可进行读写,当执行一个预充电命令后,该行才被关闭。


读命令(read)

控制激活命令 {CS_N,RAS_N,CAS_N,WE_N} = 4‘b0101.
特定的Bank(由BA0和BA1决定)和指定的列(由A0-A9决定,A10控制是否在突发读取完成后立即自动执行预充电操作,低表示不执行)
读命令用来启动对一个已经激活行的突发读取操作
在这里插入图片描述
上图看出,当出现读命令,经过2个clk的列选通潜伏期后,数据总线上出现第一个有效数据,突发写长度我们指定为4,因此写了n~n+3,又因为第二个读命令出现的时候,同样在2个clk后,数据总线上可出现第一个有效数据b……


写命令(write)

控制激活命令 {CS_N,RAS_N,CAS_N,WE_N} = 4‘b0100.
写命令用来启动对一个已经激活行的突发写操作。(A10控制是否在突发写入完成后立即自动执行预充电操作,低电平不执行)
在这里插入图片描述
可看到,写突发长度为2,因此当写命令出现,同时指定相应bank和列,因此此时即可向数据总线上写入数据。


预充电命令(Precharge)

控制激活命令 {CS_N,RAS_N,CAS_N,WE_N} = 4‘b0010.

预充电命令是关闭指定的bank或者全部的bank(单一或者全部bank由A10决定,高电平表示对所有bank行预充电,低电平表示对指定的bank中的行进行预充电)

该命令执行后,必须等待对应的**tRP(**预充电命令周期),相对应的Bank才可以被重新操作。

预充电命令在初始化过程、自动刷新和读写操作中都会用到。


自动预充电命令(Auto Precharge)
不增加额外执行指令的前提下,达到预充电指令一样的效果。该功能是在对 SDRAM 发出读写命令时,使用 A10 指明是
否在突发读写完成后立即自动执行预充电操作来实现的


突发终止命令(BURST TERMINATE)

控制激活命令 {CS_N,RAS_N,CAS_N,WE_N} = 4‘b0110.

突发中断命令用来截断固定长度或者整页长度的突发。(不会关闭行,只是会中断)
在这里插入图片描述


自动刷新命令(Auto Refresh)

SDRAM 掉电数据丢失,只有通过刷新操作才能保证数据的可靠性。刷新包括自动刷新和自刷新。发送命令后CKE时钟为有效时,使用自动刷新操作,否则使用自刷新(自我刷新)操作(主要用于休眠模式低功耗状态下的数据保存)。

SDRAM 的刷新操作是周期性的,在两次刷新的间隔可以进行数据的相关操作。我们在看SDRAM芯片参数时,经常会看到4096 Refresh Cycles/64ms的标识,这里的4096就代表芯片中每个Bank的行数。

刷新命令一次仅对一行有效,也就是说在64ms内芯片需要完成4096次刷新操作,因此平均15.625μs刷新一次,也可以一次全部刷新完,却决于你的数据读写时序。


数据掩码

数据掩码用来屏蔽不需要的数据,当突发长度为4的时候,说明连续传输4bit数据,若其中第二bit数据不需要,那我们就可采用数据掩码DQM技术,每个DQM信号线对应一个字节,通过DQM,内存可控制I/O端口对输入或者输出数据取消。


三、SDRAM上电初始化

SDRAM芯片要想正常使用,必须在上电后进行初始化操作,否则不能正常使用。
如下是初始化流程:

上电后首先等待至少100us,一般直接200us,等待期间给予空命令操作。(不同芯片时间不同)
.
延时200us后,对所有bank预充电,从而关闭所有bank的所有行(A10为高电平),使得所有bank进入空闲状态
.
进入空闲状态后,至少需要执行两个周期的自动刷新命令,完成后,进行加载模式寄存器命令

在这里插入图片描述


分析初始化时序:

关于等待时间:

tRP:发出预充电命令后需要等待的时间,一般是2个clk
tRFC:发出自动刷新命令后需要等待的时间,一般是7个clk
tMRD:发送设置模式寄存器命令后需要等待的时间,一般是3个clk


在这里插入图片描述

通过时序图,我们对初始化进行如下的总结:
1、首先上电且CKE设置为低,等待100us,同时在100us的某时刻将CKE信号变为高电平,同时发送空命令(防止对SDRAM产生误操作)。
2、第二个clk,A10拉高,对所有的Bank进行预充电。
3、进行预充电后等待tRP时间,在此期间仍然发送空命令。
4、经过tRP时间后,在第3个clk,进入到自动刷新命令,经过tRFC时间,此过程也发送空命令。
5、经过tRFC时间后,再次进行自动刷新操作(不同芯片的刷新次数不同,至少两个clk的自动刷新命令)。
6、重复自动刷新并经过tRFC后,进入到加载模式寄存器命令,地址位A0-11来写入模式寄存器的值。
7、等待tMRD时间,且期间发送空命令,tMRD时间后ACTIVE,—— SDRAM初始化完成


SDRAM初始化的状态图:

八根据上面分析得到的如下的八个状态及跳转条件,此时我们即可对SDRAM初始化模块进行设计。
在这里插入图片描述
初始化模块的输入输出信号:
在这里插入图片描述


四、SDRAM自动刷新

在这里插入图片描述
通过时序图,我们对自动刷新进行如下的总结:

1、初始化完成之后,CKE拉高,仲裁模块发送自动刷新使能信号,并进入预充电命令
2、等待tRP时间,期间发送空命令
3、tRP时间过后,发送自动刷新命令等待tRFC时间,期间发送空命令
4、tRFC时间后,发送自动刷新命令,再等待tRFC时间,期间发送空命令
5、经过tRFC时间后,自动刷新操作完成
在这里插入图片描述
自动刷新模块的输入输出信号:
在这里插入图片描述

五、页突发读时序

在这里插入图片描述

通过时序图,我们对突发读进行如下的总结:

1、发送激活ACTIVE命令,同时发送对应的bank地址以及行地址
2、等待tRCD时间,期间发送空命令
3、tRCD时间过后,发送读命令,指定bank地址以及列地址
4、等待列选通潜伏期(CL = 2个clk),期间发送空命令
5、2个clk后看到读到的数据依次呈现在了数据总线上,完成全页读操作。
6、Tn+2时刻,发出了突发终止命令,因此全页读被中断,由此,可采用突发终止命令控制突发长度。
7、突发读终止后,发送预充电命令(手动预充电A10拉高),开关闭所有bank的行,等待tRP时间,期间发送空命令,tRP时间过后,一次突发读操作完成

注意:绝大部分SDRAM都不支持在全页突发的情况下自动预充电,所以我们的读操作时序中包含手动预充电,也就是上述总结中的7
.
思考1:为什么依次读数据,会由m,m+1,m+2,到了m-1?
因此数据的读写的回环的,比如从0开始读,一行是512,那么就是0-511,如果从100开始读,那么就是100-511,再就是0-99
.
思考2:为什么Dout m-1后,又出现了m?
因为全页突发后不能自动停止,因此我们要想让它停止操作,需要采用突发终止命令。

在这里插入图片描述
突发读模块的输入输出端口:
在这里插入图片描述

六、页突发写时序

在这里插入图片描述
1、发送激活ACTIVE命令,同时发送对应的bank地址以及行地址
2、等待tRCD时间,期间发送空命令
3、tRCD时间过后,发送写命令,指定写入的bank地址以及列地址
4、写的时候无列选通潜伏期,直因此写命令发出的时候,数据总线上依次输出写入的数据。
5、不断写入,完成全页写操作。
6、但在Tn+2时刻,发出了突发终止命令,因此全页写被中断。
7、突发写终止后,发送预充电命令(手动预充电A10拉高),开关闭所有bank的行,等待tRP时间,期间发送空命令,tRP时间过后,一次突发写操作完成

状态转换图如下:
在这里插入图片描述
在这里插入图片描述


七、初始化模块的verilog代码实现

//--SDRAM初始化模块

module	sdram_init(
	input				init_clk		  ,	       //时钟信号,100M
	input				init_rst_n	  ,	

	
	output	reg	[12:0]	init_addr,  	//13位SDRAM地址
	output	reg	[3:0]	init_cmd		,	   //4位SDRAM命令,组成{CS#,RAS#,CAS#,WE#}
	output	reg	[1:0]	init_bank	,  	//4个BANK
	output	reg			init_end			   //初始化完成信号
);

//定义计数器最大值、刷新次数
localparam	T_WAIT = 15'd20_000;		   //100M时钟频率10ns,上电后等待200us,计数200us/10ns = 20000次
localparam	AR_MAX = 4'd8		;			//自动刷新次数8次

//等待时间参数定义	
localparam	TRP  = 3'd2			;			//发送预充电指令后等待的时间
localparam	TRFC = 3'd7			;			//发送自动刷新指令后等待的时间
localparam	TMRD = 3'd3			;			//发送设置模式寄存器指令后等待的时间

//命令指令参数				
localparam 	PRECHARGE = 4'b0010 , 			//预充电指令
			   AT_REF    = 4'b0001 , 			//自动刷新指令
			   NOP       = 4'b0111 , 			//空指令
			   MREG_SET  = 4'b0000 ; 			//模式寄存器设置指令
			
//状态机状态格雷码编码
localparam	INIT_WAIT = 3'b000,				  //上电后等待状态
			   INIT_PRE  = 3'b001,				  //预充电状态
			   INIT_TRP  = 3'b011,				  //预充电等待状态
			   INIT_AR   = 3'b010,				  //自动刷新状态
			   INIT_TRFC = 3'b110,             //自动刷新等待状态
			   INIT_MRS  = 3'b111,             //模式寄存器设置状态
			   INIT_TMRD = 3'b101,             //模式寄存器设置等待状态
			   INIT_END  = 3'b100;             //初始化完成状态

reg	[14:0]  cnt_wait		;				//200us延时等待计数器,20000次
reg	[2:0]	  state		;				   
reg	[2:0]	  next_state	;				
reg	[3:0]	  cnt_ar			;				//自动刷新次数计数器,记录刷新次数
reg	[3:0]	  cnt_fsm		;				//状态机计数器,用于计数各个状态以实现状态跳转
reg			  cnt_fsm_reset;				//状态机计数器复位信号,高电平有效
 

wire		wait_end_flag	;				//上电等待时间结束标志
wire		trp_end_flag	;				//预充电等待时间结束标志
wire		trfc_end_flag	;				//自动刷新等待时间结束标志
wire		tmrd_end_flag	;				//模式寄存器配置等待时间结束标志
 

assign		wait_end_flag = (cnt_wait == T_WAIT - 'd1)? 1'b1 : 1'b0;  //计数200us拉高
assign		trp_end_flag = ((state == INIT_TRP) && (cnt_fsm == TRP - 1'b1))? 1'b1 : 1'b0;
assign		trfc_end_flag = ((state == INIT_TRFC) && (cnt_fsm == TRFC - 1'b1))? 1'b1 : 1'b0;
assign		tmrd_end_flag = ((state == INIT_TMRD) && (cnt_fsm == TMRD - 1'b1))? 1'b1 : 1'b0;
 
//初始化完成信号
always@(posedge init_clk or negedge init_rst_n)begin
	if(!init_rst_n)
		init_end <= 1'b0;
	else if(state == INIT_END)
		init_end <= 1'b1;
	else
		init_end <= 1'b0;
end
 
//自动刷新次数计数器
always@(posedge init_clk or negedge init_rst_n)begin
	if(!init_rst_n)
		cnt_ar <= 4'd0;
	else if(state == INIT_WAIT)
		cnt_ar <= 4'd0;
	else if(state == INIT_AR)
		cnt_ar <= cnt_ar + 1'd1;
	else
		cnt_ar <= cnt_ar;
end
 
//200us计数器,计数到最大值后,计数器一直保持不变
always@(posedge init_clk or negedge init_rst_n)begin
	if(!init_rst_n)
		cnt_wait <= 15'd0;
	else if(cnt_wait == T_WAIT)
		cnt_wait <= cnt_wait;
	else
		cnt_wait <= cnt_wait + 1'd1;
end
 
 //状态机复位计数器
always@(posedge init_clk or negedge init_rst_n)begin
	if(!init_rst_n)
		cnt_fsm <= 4'd0;
	else if(cnt_fsm_reset)
		cnt_fsm <= 4'd0;
	else
		cnt_fsm <= cnt_fsm + 1'd1;
end
 
//工作状态计数器的复位信号
always@(*)begin
	case(state)
		INIT_WAIT:	cnt_fsm_reset = 1'b1; 							      //计数器清零
		INIT_TRP: 	cnt_fsm_reset = (trp_end_flag)? 1'b1 : 1'b0;    //完成TRP等待则计数器清零,否则计数 	
		INIT_TRFC: 	cnt_fsm_reset = (trfc_end_flag)? 1'b1 : 1'b0;   //完成TRFC等待则计数器清零,否则计数 
		INIT_TMRD: 	cnt_fsm_reset = (tmrd_end_flag)? 1'b1 : 1'b0; 	//完成TMRD等待则计数器清零,否则计数
		INIT_END:  	cnt_fsm_reset = 1'b1; 							      //计数器清零
		default:	cnt_fsm_reset = 1'b0; 							         //计数器清零
	endcase
end

 
//第一段状态,时序逻辑描述状态转移
always@(posedge init_clk or negedge init_rst_n)begin
	if(!init_rst_n)
		state <= INIT_WAIT;	
	else
		state <= next_state;
end
 
//第二段状态机,组合逻辑描述状态转移
always@(*)begin
	next_state = INIT_WAIT;
	case(state)
		INIT_WAIT: next_state = (wait_end_flag ) ? INIT_PRE : INIT_WAIT;	
		
		INIT_PRE:
			next_state = INIT_TRP;			
			
		INIT_TRP:next_state = (trp_end_flag ) ? INIT_AR : INIT_TRP;
		
		INIT_AR:
			next_state = INIT_TRFC;			
			
		INIT_TRFC:
			if(trfc_end_flag)begin			
				if(cnt_ar == AR_MAX)		
					next_state = INIT_MRS;
				else 						
					next_state = INIT_AR;
			end
			else
				next_state = INIT_TRFC;	
				
		INIT_MRS:
			next_state = INIT_TMRD;		
			
		INIT_TMRD:
			if(tmrd_end_flag)				
				next_state = INIT_END;
			else	
				next_state = INIT_TMRD;		
				
		INIT_END:
				next_state = INIT_END;		
		default:next_state = INIT_WAIT;		
	endcase
end
 
//第三段状态机,时序逻辑描述输出
always@(posedge init_clk or negedge init_rst_n)begin
	if(!init_rst_n)begin					//复位输出NOP指令,地址、BANK地址不关心,全拉高就行	
        init_cmd <= NOP;		
	    init_bank	<= 2'b11;
		init_addr <= 13'h1fff;		
	end
	else 	
		case(state)
			INIT_WAIT:begin					//输出NOP指令,地址、BANK地址不关心,全拉高就行	
				init_cmd <= NOP;		
				init_bank	<= 2'b11;
				init_addr <= 13'h1fff;								
			end
			INIT_PRE:begin
				init_cmd <= PRECHARGE;		//输出预充电指令,A10拉高选中,关闭所有BANK、BANK地址不关心	
				init_bank	<= 2'b11;
				init_addr <= 13'h1fff;								
			end			
			INIT_TRP:begin
				init_cmd <= NOP;			//输出NOP指令,地址、BANK地址不关心,全拉高就行	
				init_bank	<= 2'b11;
				init_addr <= 13'h1fff;								
			end 
			INIT_AR:begin
				init_cmd <= AT_REF;			//输出自动刷新指令,地址、BANK地址不关心,全拉高就行	
				init_bank	<= 2'b11;
				init_addr <= 13'h1fff;								
			end 
			INIT_TRFC:begin
				init_cmd <= NOP;			//输出NOP指令,地址、BANK地址不关心,全拉高就行
				init_bank	<= 2'b11;
				init_addr <= 13'h1fff;								
			end 
			INIT_MRS:begin
				init_cmd <= MREG_SET;		//输出模式寄存器配置指令,A0~A12地址进行模式配置、BANK地址全拉低
				init_bank	<= 2'b00;
				init_addr <= 
				{
					3'b000,				//A12-A10:预留
					1'b0	, 				//A9=0:读写方式,0:突发读&突发写,1:突发读&单写
					2'b00	, 				//{A8,A7}=00:标准模式,默认
					3'b011,				//{A6,A5,A4}=011:CAS 潜伏期,010:2,011:3,其他:保留
					1'b0	, 				//A3=0:突发传输方式,0:顺序,1:隔行
					3'b111 			   //{A2,A1,A0}=111:突发长度,000:单字节,001:2 字节
				};								
			end 
			INIT_TMRD:begin					//输出NOP指令,地址、BANK地址不关心,全拉高就行
				init_cmd <= NOP;		
				init_bank	<= 2'b11;
				init_addr <= 13'h1fff;
			end
			
		    INIT_END:begin
				init_cmd <= NOP;			//输出NOP指令,地址、BANK地址不关心,全拉高就行
				init_bank	<= 2'b11;
				init_addr <= 13'h1fff;
			end 
			
			default:begin					//默认状态输出NOP指令,地址、BANK地址不关心,全拉高就行
				init_cmd <= NOP;		
				init_bank	<= 2'b11;
				init_addr <= 13'h1fff;
			end
		endcase
end
 
endmodule

生成的状态转换图:
在这里插入图片描述

八、自动刷新模块的verilog代码实现

module  sdram_atref(

    input atref_clk,
	 input atref_rst_n, 
	 input atref_en,    //仲裁模块发出的自动刷新使能信号
	 input init_end,    //初始化完成信号
	 
	 output  reg  [1:0] atref_bank,
	 output  reg  [12:0] atref_addr,
	 output  reg  [3:0] atref_cmd,
	 output  reg  atref_end,
	 output  reg  atref_req //当不满足刷新次数时,向仲裁模块发送一次自动刷新请求信号
);

//命令定义
localparam PRECHARGE = 3'b001;  //预充电指令
localparam AT_REF = 3'b010;     //自动刷新指令
localparam NOP = 3'b100;        //空指令

//状态机定义
localparam ATREF_IDLE = 3'b000;
localparam ATREF_PRE  = 3'b001;
localparam ATREF_TRP  = 3'b011;
localparam ATREF_AR   = 3'b010;
localparam ATREF_TRFC = 3'b110;
localparam ATREF_END  = 3'b111;

//参数定义
localparam AR_MAX  = 4'd2; //自动刷新次数
localparam T_ATREF = 10'd700; //自动刷新时间间隔,100Mhz,因此周期10ns,64ms/8192= 7.812us,取7us,计数700次

localparam	TRP  = 3'd2	;					
localparam	TRFC = 3'd7	;					
  

reg [2:0]   state , next_state;
reg [1:0]   cnt_ar;
reg [3:0]   cnt_fsm;
reg [9:0]    cnt_atref;
reg cnt_fsm_reset;

wire cnt_trp_end;
wire cnt_trfc_end;
wire aref_ack;

assign cnt_trp_end  = ((state == ATREF_TRP)&&(cnt_fsm == TRP -1)) ? 1'b1 : 0;
assign cnt_trfc_end = ((state == ATREF_TRFC)&&(cnt_fsm == TRFC -1)) ? 1'b1 : 0;

//发送预充电时,即代表自动刷新模块响应了仲裁模块给出的自动刷新使能
assign		aref_ack = (state == ATREF_PRE) ? 1'b1 : 1'b0;
 
//自动刷新请求信号,每次计数到时间就发起自动刷新请求信号,发送出自动刷新指令后拉低
always@(posedge atref_clk or negedge atref_rst_n)begin
	if(!atref_rst_n)
		atref_req <= 1'b0;
	else if(cnt_atref == T_ATREF - 1'b1)	
		atref_req <= 1'b1;
	else if(aref_ack)						//已经发出刷新指令
		atref_req <= 1'b0;
	else
		atref_req <= atref_req;
end	

//自动刷新完成信号
always@(posedge atref_clk or negedge atref_rst_n)begin
	if(!atref_rst_n)
		atref_end <= 1'b0;
	else if(state == ATREF_END)
		atref_end <= 1'b1;
	else
		atref_end <= 1'b0;
end

//自动刷新计数器,每计数到一次最大值清零,表示需要进行一次刷新操作
always@(posedge atref_clk or negedge atref_rst_n)begin
	if(!atref_rst_n)
		cnt_atref <= 10'd0;
	else if(init_end)begin
		if(cnt_atref == T_ATREF -1'b1 )
			cnt_atref <= 10'd0;
		else
			cnt_atref <= cnt_atref + 1'd1;		
	end
	else
		cnt_atref <= cnt_atref;
end


//自动刷新次数计数器
always@(posedge atref_clk or negedge atref_rst_n)begin
	if(!atref_rst_n)
		cnt_ar <= 2'd0;
	else if(state == ATREF_IDLE)
		cnt_ar <= 2'd0;
	else if(state == ATREF_AR)
		cnt_ar <= cnt_ar + 1'd1;
	else
		cnt_ar <= cnt_ar;
end


//用于计数各个状态以实现状态跳转
always@(posedge atref_clk or negedge atref_rst_n)begin
	if(!atref_rst_n)
		cnt_fsm <= 4'd0;
	else if(cnt_fsm_reset)
		cnt_fsm <= 4'd0;
	else
		cnt_fsm <= cnt_fsm + 1'd1;
end

//状态计数器复位信号,cnt_fsm_reset
always@(*)begin
	case(state)
		ATREF_IDLE:	cnt_fsm_reset = 1'b1; 							     //计数器清零
		ATREF_TRP: 	cnt_fsm_reset = (cnt_trp_end)? 1'b1 : 1'b0;    //完成TRP等待则计数器清零,否则计数 	
		ATREF_TRFC:	cnt_fsm_reset = (cnt_trfc_end)? 1'b1 : 1'b0;   //完成TRFC等待则计数器清零,否则计数 
		ATREF_END:	cnt_fsm_reset = 1'b1; 							    //计数器清零
		default:	cnt_fsm_reset = 1'b0; 							       //计数器清零
	endcase
end


//第一段,描述状态寄存器
always@(posedge atref_clk or negedge atref_rst_n)
    if(!atref_rst_n)
	     state <= ATREF_IDLE;
	 else
	     state <= next_state;

//第二段,组合逻辑描述状态转移
always @ (*)begin
    next_state = ATREF_IDLE;
    case(state)
	     ATREF_IDLE: next_state = (init_end && atref_en ) ? ATREF_PRE : ATREF_IDLE;
		  ATREF_PRE : next_state = ATREF_TRP;
		  ATREF_TRP : begin 
		  if (cnt_trp_end)
		      next_state = ATREF_AR;
		  else
		      next_state = ATREF_TRP;
		  end
		  ATREF_AR  : next_state = ATREF_TRFC;
		  ATREF_TRFC: begin
		      if(cnt_trfc_end)begin
				    if(cnt_ar == AR_MAX)
					     next_state = ATREF_END;
					 else
					     next_state = ATREF_AR;
				end
				else
				    next_state = ATREF_TRFC;	    
		  end
		  ATREF_END : next_state = ATREF_IDLE;
		  default   : next_state = ATREF_IDLE;
	 endcase
end

//第三段,时序逻辑描述输出

always@(posedge atref_clk or negedge atref_rst_n)
	if(!atref_rst_n)begin				//复位输出NOP指令,地址、BANK地址不关心,全拉高就行	
        atref_cmd <= NOP;		
	    atref_bank <= 2'b11;
		atref_addr <= 13'h1fff;		
	end
	else 	
		case(state)
			ATREF_IDLE:begin				//输出NOP指令,地址、BANK地址不关心,全拉高就行	
				atref_cmd <= NOP;		
				atref_bank <= 2'b11;
				atref_addr <= 13'h1fff;								
			end
			ATREF_PRE:begin
				atref_cmd <= PRECHARGE;	 //输出预充电指令,A10控制预充电拉高,关闭所有的bank,BANK地址不关心	
				atref_bank <= 2'b11;
				atref_addr <= 13'h1fff;								
			end			
			ATREF_TRP:begin
				atref_cmd <= NOP;			//输出NOP指令,地址、BANK地址不关心,全拉高就行	
				atref_bank	<= 2'b11;
				atref_addr <= 13'h1fff;								
			end 
			ATREF_AR:begin
				atref_cmd <= AT_REF;		//输出自动刷新指令,地址、BANK地址不关心,全拉高就行	
				atref_bank <= 2'b11;
				atref_addr <= 13'h1fff;								
			end 
			ATREF_TRFC:begin
				atref_cmd <= NOP;				//输出NOP指令,地址、BANK地址不关心,全拉高就行
				atref_bank <= 2'b11;
				atref_addr <= 13'h1fff;								
			end 
		    ATREF_END:begin
				atref_cmd <= NOP;				//输出NOP指令,地址、BANK地址不关心,全拉高就行
				atref_bank <= 2'b11;
				atref_addr <= 13'h1fff;
			end 
			default:begin						//默认状态输出NOP指令,地址、BANK地址不关心,全拉高就行
				atref_cmd <= NOP;		
				atref_bank	<= 2'b11;
				atref_addr <= 13'h1fff;
			end
		endcase

endmodule 

生成的状态图如下:
在这里插入图片描述

九、突发读模块的verilog实现

module sdram_read(

   input				rd_clk		  ,	       
	input				rd_rst_n	  ,	
	input          rd_en,
	input          init_end,
	input  [23:0]  rd_addr,
	input  [15:0]  rd_data,
	input  [9:0]   rd_burst_len,

	output         rd_ack,//读SDRAM响应信号
	output         rd_end, //一次突发读完成
	output	reg	[12:0]	rd_sdram_addr,  	
	output	reg	[3:0]	   rd_sdram_cmd		,	   
	output	reg	[1:0]	   rd_sdram_bank	,  	
	output		[15:0]   rd_sdram_data			   

);

//定义时间参数
parameter TRP =  3'd2; 
parameter TCL =  3'd3;   //列潜伏期
parameter TRCD = 3'd2;   //激活等待周期

//定义命令
parameter NOP = 3'b000;
parameter ACTIVE = 3'b001;
parameter READ = 3'b011;
parameter BURST_STOP = 3'b010;
parameter PRECHARGE = 3'b110;


//定义状态
parameter   RD_IDLE     =   4'b0000 ,   //空闲
            RD_ACT	   =   4'b0001 ,   //行激活
            RD_TRCD     =   4'b0011 ,   //激活等待
            RD_READ     =   4'b0010 ,   //读操作
            RD_CL       =   4'b0100 ,   //潜伏期等待
            RD_DATA     =   4'b0101 ,   //读数据
            RD_PRE      =   4'b0111 ,   //预充电状态
            RD_TRP      =   4'b0110 ,   //预充电等待
            RD_END      =   4'b1100 ;   //一次突发读结束
				
reg [3:0]   state,next_state;
reg [9:0]   cnt_fsm;
reg [15:0]  rd_data_reg;
reg cnt_fsm_reset;

wire cnt_trcd_end;
wire cnt_cl_end ;
wire cnt_rd_end ;
wire cnt_trp_end;
wire cnt_rdburst_end;

assign cnt_trcd_end = ((state == RD_TRCD) && (cnt_fsm == TRCD ) ) ? 1'b1 : 0;
assign cnt_cl_end   = ((state == RD_CL) && (cnt_fsm == TCL - 1'b1) ) ? 1'b1 : 0;
assign cnt_rd_end   = ((state == RD_DATA) && (cnt_fsm == rd_burst_len) ) ? 1'b1 : 0; //读突发结束
assign cnt_trp_end  = ((state == RD_TRP) && (cnt_fsm == TRP ) ) ? 1'b1 : 0; 
assign cnt_rdburst_end  = ((state == RD_DATA) && (cnt_fsm == rd_burst_len - 4) ) ? 1'b1 : 0;//读突发终止

//状态机计数器
always @(posedge rd_clk or negedge rd_rst_n )
    if(!rd_rst_n)
	     cnt_fsm <= 0;
	 else if (cnt_fsm_reset)
	     cnt_fsm <= 0;
	 else
        cnt_fsm <=  cnt_fsm + 1'b1;
	 
//cnt_fsm_reset,状态机计数器复位信号,高电平复位
always@(*)begin
	case(state)
		RD_IDLE:	   cnt_fsm_reset <= 1'b1;
		RD_TRCD:    cnt_fsm_reset <= (cnt_trcd_end == 1'b1) ? 1'b1 : 1'b0;
		RD_READ:    cnt_fsm_reset <= 1'b1;
		RD_CL:      cnt_fsm_reset <= (cnt_cl_end == 1'b1) ? 1'b1 : 1'b0;
		RD_DATA:    cnt_fsm_reset <= (cnt_rd_end == 1'b1) ? 1'b1 : 1'b0; //读突发长度结束
		RD_TRP:     cnt_fsm_reset <= (cnt_trp_end == 1'b1) ? 1'b1 : 1'b0; 
		RD_END:     cnt_fsm_reset <= 1'b1;
		default:    cnt_fsm_reset <= 1'b0;
	endcase
end

				
//	一次突发读结束	
assign 	rd_end = (state == RD_END )? 1'b1 :0 ;	
	
//rd_data_reg,因为从SDRAM读出的数据与本模块的时钟存在相位差异,故需同步化处理

always@(posedge rd_clk or negedge rd_rst_n)begin
    if(!rd_rst_n)
        rd_data_reg <=  16'd0;
    else
        rd_data_reg <=  rd_data;
end


//rd_ack:读SDRAM响应信号
assign  rd_ack = (state == RD_DATA)&& (cnt_fsm >= 10'd1)&& (cnt_fsm < (rd_burst_len + 2'd1));

//rd_sdram_data:SDRAM读出的数据
assign  rd_sdram_data = (rd_ack == 1'b1) ? rd_data_reg : 16'b0;
		  

//第一段,状态寄存器

always @(posedge rd_clk or negedge rd_rst_n )
    if(!rd_rst_n)
	     state <= RD_IDLE;
    else 
	     state <= next_state;


//第二段,组合逻辑描述状态转移
always @ (*) begin
    next_state = RD_IDLE;
	 
    case(state) 
	 RD_IDLE: next_state = ((init_end)&&(rd_en)) ? RD_ACT : RD_IDLE;
	 
	 RD_ACT : next_state = RD_TRCD ;
	 
	 RD_TRCD: next_state = (cnt_trcd_end) ? RD_READ: RD_TRCD;
	 
	 RD_READ: next_state = RD_CL;
	 
	 RD_CL  : next_state = (cnt_cl_end) ? RD_DATA: RD_CL;
	 
	 RD_DATA: next_state = (cnt_rd_end) ? RD_PRE: RD_DATA;
	 
	 RD_PRE : next_state = RD_TRP;
	 
	 RD_TRP : next_state = (cnt_trp_end) ? RD_END: RD_TRP;
	 
	 RD_END : next_state = RD_IDLE;
	 
	 default: next_state = RD_IDLE;
	     
	 endcase
end



//第三段,时序逻辑描述输出

always @(posedge rd_clk or negedge rd_rst_n )
    if(!rd_rst_n)begin
		  rd_sdram_cmd  <= NOP;
		  rd_sdram_bank <= 2'b11;	
	     rd_sdram_addr <= 13'h1fff;	  
	 end
	 else
	     case(state)
		  RD_IDLE,RD_TRCD,RD_CL,RD_END,RD_TRP:begin
		      rd_sdram_cmd  <= NOP;
		      rd_sdram_bank <= 2'b11;	
	         rd_sdram_addr <= 13'h1fff;
		  end
		  
        RD_ACT :begin
		      rd_sdram_cmd  <= ACTIVE;
		      rd_sdram_bank <= rd_addr[23:22];		   //BANK地址是输入地址rd_addr的高两位	
	         rd_sdram_addr <= rd_addr[21:9];			//行地址是21-9位(13位地址总线)
		  end
        
        
        RD_READ:begin
		      rd_sdram_cmd  <= READ;
		      rd_sdram_bank <= rd_addr[23:22];		     //BANK地址是输入地址rd_addr的高两位	
	         rd_sdram_addr <=  {4'b0000,rd_addr[8:0]};//列地址只有9位,因共用13位地址总线,高4位补0
		  end

        RD_DATA:begin    	                	
				rd_sdram_bank <= 2'b11;
				rd_sdram_addr <= 13'h1fff;
				if(cnt_rdburst_end)
					rd_sdram_cmd <= BURST_STOP;			//突发传输终止指令
				else
					rd_sdram_cmd <= NOP;				     //突发读取未结束,发送空指令
            end

        RD_PRE :begin                   
				rd_sdram_cmd <= PRECHARGE;				//预充电命令
				rd_sdram_bank <= rd_addr[23:22];		//BANK地址是输入地址rd_addr的高两位
				rd_sdram_addr <= 13'h0400;				//A10拉高选中所有BANK,对所有BANK进行预充电,其他位不关心	
            end
				
        default:begin
				rd_sdram_cmd <=  NOP;					
				rd_sdram_bank <=  2'b11;
				rd_sdram_addr <=  13'h1fff;
            end
		endcase
		  
		  
endmodule 

十、突发写的verilog实现

module	sdram_write(

	input				   wr_clk	   ,		
	input				   wr_rst_n		,		
	input				   init_end		,		
	input				   wr_en			,		
	input		[23:0]	wr_addr		,		//写SDRAM地址
	input		[15:0]	wr_data		,		//待写入SDRAM的数据
	input		[9:0]  	wr_burst_len,		//SDRAM突发写长度
		
	output				wr_ack		,		//写响应信号
	output				wr_end		,		//写结束信号,写操作完成后拉高一个周期	
	
	output	reg	[12:0]wr_sdram_addr,		
	output	reg	[3:0]	wr_sdram_cmd	,		
	output	reg	[1:0]	wr_sdram_bank	,		
	output	reg			wr_sdram_en		,	//数据总线输出使能,用于后续仲裁模块输出
	output		   [15:0]wr_sdram_data		
	
);

//等待时间参数定义	
localparam	TRP  = 3'd2	,					   //预充电等待周期
			   TRCD = 3'd2	;					   //激活等待周期
			
//命令指令参数				
localparam 	NOP		  	= 4'b0111 ,			
			   PRECHARGE 	= 4'b0010 ,			
			   ACTIVE    	= 4'b0011 ,			
			   WRITE     	= 4'b0100 ,			
			   BURST_STOP  = 4'b0110 ;			//突发终止指令
			
//状态机状态格雷码编码
localparam	WR_IDLE  = 3'b000,				//写操作初始状态
			   WR_ACT   = 3'b001,				//行激活状态
			   WR_TRCD  = 3'b011,				//行激活等待状态
			   WR_WR 	= 3'b010,				//写操作状态
			   WR_DATA  = 3'b100,				//突发写操作状态,突发写入多个数据,直到突发终止
			   WR_PRE 	= 3'b101,            //预充电状态
			   WR_TRP 	= 3'b111,            //预充电状态等待状态
			   WR_END 	= 3'b110;            //结束状态
 
											
reg	[2:0]	state,next_state	;							
reg	[9:0]	cnt_fsm			;				//状态机计数器,位宽应满足最大突发长度的要求
reg			cnt_fsm_reset	;				//状态机计数器复位信号,高电平有效
 
	
wire		trp_end_flag	;				
wire		trcd_end_flag	;				//行激活等待时间结束标志
wire		wr_end_flag		;				//突发写结束标志
 
//一次突发写结束
assign	wr_end = (state == WR_END) ? 1'b1 : 1'b0;
 
//写 SDRAM 响应信号
assign	wr_ack = (state == WR_WR) || ((state == WR_DATA) && (cnt_fsm <= wr_burst_len - 2'd2));
 
//wr_sdram_data:写入 SDRAM 的数据
assign wr_sdram_data = (wr_sdram_en == 1'b1) ? wr_data : 16'b0;
 
//wr_sdram_en:数据总线输出使能
always@(posedge wr_clk or negedge wr_rst_n)begin
	if(!wr_rst_n)
		wr_sdram_en <= 1'b0;
	else
		wr_sdram_en <= wr_ack;
end
		

assign	trp_end_flag = ((state == WR_TRP) && (cnt_fsm == TRP - 1'b1))? 1'b1 : 1'b0;
assign	trcd_end_flag = ((state == WR_TRCD) && (cnt_fsm == TRCD - 1'b1))? 1'b1 : 1'b0;
assign	wr_end_flag = ((state == WR_DATA) && (cnt_fsm == wr_burst_len - 1'b1))? 1'b1 : 1'b0;
 
//用于计数各个状态以实现状态跳转
always@(posedge wr_clk or negedge wr_rst_n)begin
	if(!wr_rst_n)
		cnt_fsm <= 10'd0;
	else if(cnt_fsm_reset)
		cnt_fsm <= 10'd0;
	else
		cnt_fsm <= cnt_fsm + 1'd1;
end
 
//工作状态计数器的复位信号
always@(*)begin
	case(state)
		WR_IDLE:  	cnt_fsm_reset = 1'b1; 							      //计数器清零
		WR_TRCD: 	cnt_fsm_reset = (trcd_end_flag)? 1'b1 : 1'b0;   //完成TRCD等待则计数器清零,否则计数 	
		WR_WR: 		cnt_fsm_reset = 1'b1; 							      //完计数器清零 
		WR_DATA: 	cnt_fsm_reset = (wr_end_flag)? 1'b1 : 1'b0; 	   //完成突发数据写入则计数器清零,否则计数
		WR_TRP:		cnt_fsm_reset = (trp_end_flag)? 1'b1 : 1'b0; 	//完成TRP等待则计数器清零,否则计数 
		WR_END:  	cnt_fsm_reset = 1'b1; 							      //计数器清零
		default:	   cnt_fsm_reset = 1'b0; 							      //计数器累加计数
	endcase
end

 
//第一段状态机,状态寄存器
always@(posedge wr_clk or negedge wr_rst_n)begin
	if(!wr_rst_n)
		state <= WR_IDLE;	
	else
		state <= next_state;
end
 
//第二段状态机,组合逻辑描述状态转移
always@(*)begin
	next_state = WR_IDLE;
	case(state)
		WR_IDLE: next_state = (wr_en && init_end) ? WR_ACT : WR_IDLE;

		WR_ACT:						
			next_state = WR_TRCD;								
		WR_TRCD:	next_state = (trcd_end_flag) ? WR_WR : WR_TRCD;
					
		WR_WR:						
			next_state = WR_DATA;	
			
		WR_DATA:	next_state = (wr_end_flag) ? WR_PRE : WR_DATA;					
					
		WR_PRE:														
			next_state = WR_TRP;	
			
		WR_TRP:	next_state = (trp_end_flag) ? WR_END : WR_TRP;	
								
		WR_END:						
			next_state = WR_IDLE;	

		default:next_state = WR_IDLE;							
	endcase
end
 
//第三段状态机,描述输出
always@(posedge wr_clk or negedge wr_rst_n)begin
	if(!wr_rst_n)begin												
        wr_sdram_cmd <= NOP;								
	     wr_sdram_bank <= 2'b11;						
		  wr_sdram_addr <= 13'h1fff;								
	end						
	else 							
		case(state)						
			WR_IDLE,WR_TRCD,WR_TRP,WR_END:begin			//初始输出NOP指令,地址、BANK地址不关心,全拉高就行	
				wr_sdram_cmd <= NOP;								
				wr_sdram_bank <= 2'b11;						
				wr_sdram_addr <= 13'h1fff;														
			end						
			WR_ACT:begin						
				wr_sdram_cmd <= ACTIVE;						//输出激活指令,BANK地址是输入地址wr_addr的高两位,行地址是21-9位(13位地址总线)
				wr_sdram_bank <= wr_addr[23:22];
				wr_sdram_addr <= wr_addr[21:9];			//13位地址线		
			end			

			WR_WR:begin
				wr_sdram_cmd <= WRITE;								//输出写指令,BANK地址是输入地址wr_addr的高两位,列地址是8-0位(13位地址总线)
				wr_sdram_bank <= wr_addr[23:22];
				wr_sdram_addr <= {4'b0000,wr_addr[8:0]};	  //列地址只有9位,但共13位地址总线,所以高4位补0							
			end 
			
			WR_DATA:begin				
				wr_sdram_bank <= 2'b11;
				wr_sdram_addr <= 13'h1fff;
				if(wr_end_flag)
					wr_sdram_cmd <= BURST_STOP;				//输出突发终结指令	
				else					
					wr_sdram_cmd <= NOP;						
			end 
			
			WR_PRE:begin					
				wr_sdram_cmd <= PRECHARGE;							//输出预充电指令,BANK地址是输入地址wr_addr的高两位,地址不关心,只需要拉高A10选中所有BANK
				wr_sdram_bank <= wr_addr[23:22];					
				wr_sdram_addr <= 13'h0400;							//A10拉高,关闭所有BANK						
			end 		
			
					
			default:begin										
				wr_sdram_cmd <= NOP;		
				wr_sdram_bank <= 2'b11;
				wr_sdram_addr <= 13'h1fff;
			end
		endcase
end
 
endmodule

参考

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Fighting_FPGA

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值