一、对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
参考