关于SDRAM存储器

news/2025/3/9 10:35:08/

一、对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		  ,	       //时钟信号,100Minput				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个BANKoutput	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)beginif(!init_rst_n)init_end <= 1'b0;else if(state == INIT_END)init_end <= 1'b1;elseinit_end <= 1'b0;
end//自动刷新次数计数器
always@(posedge init_clk or negedge init_rst_n)beginif(!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;elsecnt_ar <= cnt_ar;
end//200us计数器,计数到最大值后,计数器一直保持不变
always@(posedge init_clk or negedge init_rst_n)beginif(!init_rst_n)cnt_wait <= 15'd0;else if(cnt_wait == T_WAIT)cnt_wait <= cnt_wait;elsecnt_wait <= cnt_wait + 1'd1;
end//状态机复位计数器
always@(posedge init_clk or negedge init_rst_n)beginif(!init_rst_n)cnt_fsm <= 4'd0;else if(cnt_fsm_reset)cnt_fsm <= 4'd0;elsecnt_fsm <= cnt_fsm + 1'd1;
end//工作状态计数器的复位信号
always@(*)begincase(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)beginif(!init_rst_n)state <= INIT_WAIT;	elsestate <= next_state;
end//第二段状态机,组合逻辑描述状态转移
always@(*)beginnext_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;endelsenext_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)beginif(!init_rst_n)begin					//复位输出NOP指令,地址、BANK地址不关心,全拉高就行	init_cmd <= NOP;		init_bank	<= 2'b11;init_addr <= 13'h1fff;		endelse 	case(state)INIT_WAIT:begin					//输出NOP指令,地址、BANK地址不关心,全拉高就行	init_cmd <= NOP;		init_bank	<= 2'b11;init_addr <= 13'h1fff;								endINIT_PRE:begininit_cmd <= PRECHARGE;		//输出预充电指令,A10拉高选中,关闭所有BANK、BANK地址不关心	init_bank	<= 2'b11;init_addr <= 13'h1fff;								end			INIT_TRP:begininit_cmd <= NOP;			//输出NOP指令,地址、BANK地址不关心,全拉高就行	init_bank	<= 2'b11;init_addr <= 13'h1fff;								end INIT_AR:begininit_cmd <= AT_REF;			//输出自动刷新指令,地址、BANK地址不关心,全拉高就行	init_bank	<= 2'b11;init_addr <= 13'h1fff;								end INIT_TRFC:begininit_cmd <= NOP;			//输出NOP指令,地址、BANK地址不关心,全拉高就行init_bank	<= 2'b11;init_addr <= 13'h1fff;								end INIT_MRS:begininit_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;endINIT_END:begininit_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;endendcase
endendmodule

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

八、自动刷新模块的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)beginif(!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;elseatref_req <= atref_req;
end	//自动刷新完成信号
always@(posedge atref_clk or negedge atref_rst_n)beginif(!atref_rst_n)atref_end <= 1'b0;else if(state == ATREF_END)atref_end <= 1'b1;elseatref_end <= 1'b0;
end//自动刷新计数器,每计数到一次最大值清零,表示需要进行一次刷新操作
always@(posedge atref_clk or negedge atref_rst_n)beginif(!atref_rst_n)cnt_atref <= 10'd0;else if(init_end)beginif(cnt_atref == T_ATREF -1'b1 )cnt_atref <= 10'd0;elsecnt_atref <= cnt_atref + 1'd1;		endelsecnt_atref <= cnt_atref;
end//自动刷新次数计数器
always@(posedge atref_clk or negedge atref_rst_n)beginif(!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;elsecnt_ar <= cnt_ar;
end//用于计数各个状态以实现状态跳转
always@(posedge atref_clk or negedge atref_rst_n)beginif(!atref_rst_n)cnt_fsm <= 4'd0;else if(cnt_fsm_reset)cnt_fsm <= 4'd0;elsecnt_fsm <= cnt_fsm + 1'd1;
end//状态计数器复位信号,cnt_fsm_reset
always@(*)begincase(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;elsestate <= next_state;//第二段,组合逻辑描述状态转移
always @ (*)beginnext_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;elsenext_state = ATREF_TRP;endATREF_AR  : next_state = ATREF_TRFC;ATREF_TRFC: beginif(cnt_trfc_end)beginif(cnt_ar == AR_MAX)next_state = ATREF_END;elsenext_state = ATREF_AR;endelsenext_state = ATREF_TRFC;	    endATREF_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;		endelse 	case(state)ATREF_IDLE:begin				//输出NOP指令,地址、BANK地址不关心,全拉高就行	atref_cmd <= NOP;		atref_bank <= 2'b11;atref_addr <= 13'h1fff;								endATREF_PRE:beginatref_cmd <= PRECHARGE;	 //输出预充电指令,A10控制预充电拉高,关闭所有的bank,BANK地址不关心	atref_bank <= 2'b11;atref_addr <= 13'h1fff;								end			ATREF_TRP:beginatref_cmd <= NOP;			//输出NOP指令,地址、BANK地址不关心,全拉高就行	atref_bank	<= 2'b11;atref_addr <= 13'h1fff;								end ATREF_AR:beginatref_cmd <= AT_REF;		//输出自动刷新指令,地址、BANK地址不关心,全拉高就行	atref_bank <= 2'b11;atref_addr <= 13'h1fff;								end ATREF_TRFC:beginatref_cmd <= NOP;				//输出NOP指令,地址、BANK地址不关心,全拉高就行atref_bank <= 2'b11;atref_addr <= 13'h1fff;								end ATREF_END:beginatref_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;endendcaseendmodule 

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

九、突发读模块的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;elsecnt_fsm <=  cnt_fsm + 1'b1;//cnt_fsm_reset,状态机计数器复位信号,高电平复位
always@(*)begincase(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)beginif(!rd_rst_n)rd_data_reg <=  16'd0;elserd_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 @ (*) beginnext_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)beginrd_sdram_cmd  <= NOP;rd_sdram_bank <= 2'b11;	rd_sdram_addr <= 13'h1fff;	  endelsecase(state)RD_IDLE,RD_TRCD,RD_CL,RD_END,RD_TRP:beginrd_sdram_cmd  <= NOP;rd_sdram_bank <= 2'b11;	rd_sdram_addr <= 13'h1fff;endRD_ACT :beginrd_sdram_cmd  <= ACTIVE;rd_sdram_bank <= rd_addr[23:22];		   //BANK地址是输入地址rd_addr的高两位	rd_sdram_addr <= rd_addr[21:9];			//行地址是21-9位(13位地址总线)endRD_READ:beginrd_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位补0endRD_DATA:begin    	                	rd_sdram_bank <= 2'b11;rd_sdram_addr <= 13'h1fff;if(cnt_rdburst_end)rd_sdram_cmd <= BURST_STOP;			//突发传输终止指令elserd_sdram_cmd <= NOP;				     //突发读取未结束,发送空指令endRD_PRE :begin                   rd_sdram_cmd <= PRECHARGE;				//预充电命令rd_sdram_bank <= rd_addr[23:22];		//BANK地址是输入地址rd_addr的高两位rd_sdram_addr <= 13'h0400;				//A10拉高选中所有BANK,对所有BANK进行预充电,其他位不关心	enddefault:beginrd_sdram_cmd <=  NOP;					rd_sdram_bank <=  2'b11;rd_sdram_addr <=  13'h1fff;endendcaseendmodule 

十、突发写的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)beginif(!wr_rst_n)wr_sdram_en <= 1'b0;elsewr_sdram_en <= wr_ack;
endassign	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)beginif(!wr_rst_n)cnt_fsm <= 10'd0;else if(cnt_fsm_reset)cnt_fsm <= 10'd0;elsecnt_fsm <= cnt_fsm + 1'd1;
end//工作状态计数器的复位信号
always@(*)begincase(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)beginif(!wr_rst_n)state <= WR_IDLE;	elsestate <= next_state;
end//第二段状态机,组合逻辑描述状态转移
always@(*)beginnext_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)beginif(!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:beginwr_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选中所有BANKwr_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;endendcase
endendmodule

参考


http://www.ppmy.cn/news/884514.html

相关文章

视频存储空间计算公式

存储空间计算公式&#xff1a; 单路实时视频的存储容量 (GB) (GB)(GB)&#xff1d;【视频码流大小 &#xff1d;【视频码流大小 (Mb) 60 秒 60 分 24 小时存储天 数/8 】/1024 &#xff1b; 我们以一路视频图像在7天、15天、30天所需要的占用空间为例&#xff1a; 对于已建点…

string,list,hash,keys类型相关数据命令操作

1、 string类型数据的命令操作&#xff1a; &#xff08;1&#xff09; 设置键值&#xff1a; &#xff08;2&#xff09;读取键值&#xff1a; &#xff08;3&#xff09;数值类型自增1&#xff1a; &#xff08;4&#xff09;数值类型自减1&#xff1a; &#xff08;5…

高版本linux内核编译驱动报错记录

编译驱动报错log1:ERROR: modpost: module 8821cs uses symbol kernel_write from namespace VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver, but does not import it.问题原因:模块代码(8821cs)使用了内核命名空间 VFS_internal_I_am_really_a_filesystem_an…

Linux下查看隐藏文件夹

众所周知&#xff0c;Linux系统下.开头的文件即为隐藏文件&#xff0c;比如.git文件夹里面都是些git的所需的记录文件&#xff0c;命令行下查看隐藏文件非常简单&#xff0c;ls -a即可把当前文件夹下的所有文件&#xff08;包括隐藏和非隐藏&#xff09;都列举出来。但是在图形…

Linux查看隐藏文件和文件夹

在linux中以.开头的文件或文件夹是隐藏文件或文件夹 将文件或文件夹隐藏 mv test .test查看全部文件或文件夹包括隐藏文件或隐藏文件夹 ls -a只显示隐藏文件或隐藏文件夹 l.

Linux查看隐藏文件

Linux中查看隐藏文件&#xff08;以.开头&#xff09; 在终端中查看 键入ls -a 可视化查看 Ctrlh 我们打开文件夹&#xff0c;看不到隐藏文件&#xff0c;如下图所示&#xff1a; 按下Ctrlh后&#xff0c;就可以看到隐藏文件&#xff0c;如下图所示&#xff1a;

解决github无法拉取submodule子模块的问题

引言 当使用git clone --recursive url 拉取一个配置了子模块的仓库后&#xff0c;会卡住。 同时在使用git clone 拉去https的url时&#xff0c;同样可能会出现一直卡在cloning int reposity...本文提供一个简单的脚本来解决该问题。 前置准备 需要配置好git的相关配置&…

Windows7如何隐藏文件与查看隐藏文件

隐藏&#xff1a; 选定文件&#xff0c;右键——属性&#xff0c;在出现的对话框的最下面【属性】的隐藏前的方框打上【勾】&#xff0c;先点【应用】再点【确定】。 查看&#xff1a; 前面的步骤同我的另一篇文章《Windows7如何使文件显示拓展名》一样。 首先我们打开文件…