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

参考

关于SDRAM存储器相关推荐

  1. 串口读写SDRAM存储器

    SDRAM同步动态随机存储器 SDRAM概念 SDRAM与SRAM区别 SDRAM:同步动态存储器. 同步:需要时钟信号,数据与时钟信号的上升沿同步变化. 动态:每64ms内动态刷新存储器中的数据.电 ...

  2. fpga控制sdram存储器的读写2:sdram初始化

    一.理论知识 sdram芯片上电后,执行读写操作之前,首先要做的第一件事就是初始化,这里解释下初始化的目的. 1.在使用sdram资源的时候,要先给出芯片的地址,这个地址就是行/列地址.初始化第一步, ...

  3. FPGA之旅设计99例之第二十例---SDRAM存储器实现

    一. 简介 本例将介绍SDRAM的使用.SDRAM是一个存储器件,存储容量大,存储速度比较快,速度可达100M,特别适合用来当中视频或者音频中的存储器件. 在采集到OV5640传输过来的图像数据的时候 ...

  4. SDRAM控制器设计

    SDRAM控制器设计 SDRAM 器件引脚示意图和功能框图如下: SDRAM 器件有如下的特性 通常情况下, SDRAM 存储器工作在 3.3V 的电压下(需要注意的是 DDR DRAM工作电压是 2 ...

  5. DDR SDRAM原理介绍

    DDR SDRAM原理介绍 转载请注明出处:http://blog.csdn.net/kevin_hee/article/details/78020929. 这篇博文对DDR SDRAM做了粗略的介绍 ...

  6. 物联网ARM开发- 5协议 FSMC控制器外扩SRAM存储器

    前言:STM32F407ZGT6 自带了 192K 字节的 SRAM,对一般应用来说,已经足够了,不过在一 些对内存要求高的场合,STM32F4 自带的这些内存就不够用了.比如跑算法或者跑 GUI 等 ...

  7. FMC——扩展外部SDRAM

    STM32控制器芯片内部有一定大小的SRAM及FLASH作为内存和程序存储空间,但当程序较大,内存和程序空间不足时,就需要在STM32芯片的外部扩展存储器了. 市场上SDRAM的价格普遍比SRAM性价 ...

  8. 第26章 FMC—扩展外部SDRAM—零死角玩转STM32-F429系列

    转载:http://blog.csdn.net/flyleaf91/article/details/52325516 第26章     FMC-扩展外部SDRAM 全套200集视频教程和1000页PD ...

  9. 【FPGA】基于Avalon_MM接口的SDRAM读写

    1.SDRAM 1.1 SDRAM简介 C4开发板上的SDRAM芯片是海力士生产,有256Mbits容量. SDRAM是同步动态随机存储器(存储阵列不断刷新). SDRAM寻址基本原理:行列寻址 SD ...

最新文章

  1. 对口令协议的几种攻击方式
  2. python多变量非线性拟合_python实现多变量线性回归(Linear Regression with Multiple Variables)...
  3. 冒泡 MS Azure 不便宜。。。
  4. 多行文本溢出显示省略号(…)
  5. EF Core下利用Mysql进行数据存储在并发访问下的数据同步问题
  6. 6月Unity技术路演华东站报名启动!
  7. 华语乐坛趋势报告(2022)
  8. java的outputstream_Java OutputStream类
  9. 基于微信小程序的学习网站源码
  10. 英语拾遗之基本的量词
  11. 『杭电1726』God’s cutter
  12. linux 搭建mqtt服务
  13. 综合布线施工工艺--
  14. 自媒体适合多平台发布,30+自媒体平台都可以一键发布!
  15. VB.net应用技巧5: VB.net 除法运算
  16. [机器学习]Lasso,L1范数,及其鲁棒性
  17. 概率论基础-泊松分布计算近似概率
  18. Linux inotify
  19. 2008最佳显微照片公布
  20. 无法勾选远程连接到计算机,客户端无法连接到远程计算机错误的解决方法

热门文章

  1. 无线物联网技术,在智能门禁系统的应用
  2. Mendix低代码平台,唯快不破
  3. 罗切斯特计算机官网,罗切斯特
  4. 大数据工程师学习路线
  5. unity 纹理压缩 内存优化
  6. RVT贴片铝电解电容规格
  7. C语言自定义函数的调用
  8. 鸿蒙系统研究之五:替换 AOSP 预编译库,关闭 SELinux
  9. 音视频之渲染yuv图片
  10. sa结构组网方式_5G独立组网SA模式下的驻网流程浅析