码迷,mamicode.com
首页 > 其他好文 > 详细

力战SDRAM(一)

时间:2015-10-28 19:32:58      阅读:310      评论:0      收藏:0      [点我收藏+]

标签:fpga sdram

SDRAM的时序可以说比I2C通信更为复杂,除了要遵循严格的时序外,还要在每步操作前都要指定命令才行。而实现这个目的的核心就是状态机。我们先来了解一下 SDRAM的读写时序:

1.上电复位200us后进入预充电状态;

2.八次自刷新;

3.进入模式寄存器设置;

4.读写地址;

5.读写数据。

需要说明的是:1.读写地址和数据之间也要穿插一些其他的刷新命令,上面就不写了。

        2.关于每个时序的原理,我也不做详细介绍,只说明两点:

                     1.每步操作都有个延时等待时间。

                     2.关于模式寄存器设置:SDRAM上有一个模式寄存器,用户通过它来设置突发长度、突发类型,读潜伏期。上电后必须对它进行设置来初始化器件,这个命令只有在所有的bank都处于空闲时才可以发送,且CKE在该命令发送之前,必须至少保持一个时钟周期的高电平。这个命令的发送是在RASCASCSWE为低电时钟的上长沿触发,且数据口设置为输入。数据写入模式寄存器需要两个时钟,这写时间任何命令都不能被发送。

-----------------------------------------------------------------------------------------------

为了实现复杂的时序,我们设计了三段状态机:

1.CMD命令状态机:负责每个步骤来临前的的命令控制;

2.初始化操作状态机:负责上电后的初始化和自刷新;

3.读写数据状态机:用于读写数据状态控制。

-----------------------------------------------------------------------------------------------

我们阅读状态机的时候,要严格遵守FPGA并行的特性,否则很难看懂,当然Verilog整个程序的阅读也是这样,这里只是强调一下。

CMD命令:


case (init_state)

`I_NOP,`I_TRP,`I_TRF1,`I_TRF2,`I_TRF3,`I_TRF4,`I_TRF5,`I_TRF6,`I_TRF7,`I_TRF8,`I_TMRD: begin

sdram_cmd_r <= `CMD_NOP;`CMD_NOP是命令码)

sdram_ba_r <= 2‘b11;

sdram_addr_r <= 12‘hfff;        

end

`I_PRE: begin

sdram_cmd_r <= `CMD_PRGE;

sdram_ba_r <= 2‘b11;

sdram_addr_r <= 12‘hfff;

end

`I_AR1,`I_AR2,`I_AR3,`I_AR4,`I_AR5,`I_AR6,`I_AR7,`I_AR8: begin

sdram_cmd_r <= `CMD_A_REF;

sdram_ba_r <= 2‘b11;

sdram_addr_r <= 12‘hfff;                                                

end                                  

`I_MRS: begin        //模式寄存器设置,可根据实际需要进行设置

sdram_cmd_r <= `CMD_LMR;

sdram_ba_r <= 2‘b00;        //操作模式设置

sdram_addr_r <= {

                            2‘b00,                        //操作模式设置

                            1‘b0,                        //操作模式设置(这里设置为A9=0,即突发读/突发写)

                            2‘b00,                        //操作模式设置({A8,A7}=00),当前操作为模式寄存器设置

                            3‘b011,                        // CAS潜伏期设置(这里设置为3{A6,A5,A4}=011)

                            1‘b0,                        //突发传输方式(这里设置为顺序,A3=b0)

                            3‘b011                        //突发长度(这里设置为8{A2,A1,A0}=011)

};

end        

`I_DONE:

case (work_state)

`W_IDLE,`W_TRCD,`W_CL,`W_TRFC,`W_RD,`W_WD,`W_TDAL: begin

sdram_cmd_r <= `CMD_NOP;

sdram_ba_r <= 2‘b11;

sdram_addr_r <= 12‘hfff;

end

`W_ACTIVE: begin

sdram_cmd_r <= `CMD_ACTIVE;

sdram_ba_r <= sys_addr[21:20];        //L-Bank地址

sdram_addr_r <= sys_addr[19:8];        //行地址

end

`W_READ: begin

sdram_cmd_r <= `CMD_READ;

sdram_ba_r <= sys_addr[21:20];        //L-Bank地址

sdram_addr_r <= {

4‘b0100,                // A10=1,设置写完成允许预充电

sys_addr[7:0]        //列地址 

};

end

`W_WRITE: begin

sdram_cmd_r <= `CMD_WRITE;

sdram_ba_r <= sys_addr[21:20];        //L-Bank地址

sdram_addr_r <= {

4‘b0100,                // A10=1,设置写完成允许预充电

sys_addr[7:0]        //列地址 

};

end                                                        

`W_AR: begin

sdram_cmd_r <= `CMD_A_REF;

sdram_ba_r <= 2‘b11;

sdram_addr_r <= 12‘hfff;        

end

default: begin

sdram_cmd_r <= `CMD_NOP;

sdram_ba_r <= 2‘b11;

sdram_addr_r <= 12‘hfff;        

end

endcase

default: begin

sdram_cmd_r <= `CMD_NOP;

sdram_ba_r <= 2‘b11;

sdram_addr_r <= 12‘hfff;        

end

endcase

end

 

endmodule

                              

读写操作

case (init_state_r)

`I_NOP:         init_state_r <= done_200us ? `I_PRE:`I_NOP;                //上电复位后200us结束则进入下一状态

`I_PRE:         init_state_r <= (TRP_CLK == 0) ? `I_AR1:`I_TRP;        //预充电状态

`I_TRP:         init_state_r <= (`end_trp) ? `I_AR1:`I_TRP;                        //预充电等待TRP_CLK个时钟周期

`I_AR1:         init_state_r <= (TRFC_CLK == 0) ? `I_AR2:`I_TRF1;        //1次自刷新

`I_TRF1:        init_state_r <= (`end_trfc) ? `I_AR2:`I_TRF1;                        //等待第1次自刷新结束,TRFC_CLK个时钟周期

`I_AR2:         init_state_r <= (TRFC_CLK == 0) ? `I_AR3:`I_TRF2; //2次自刷新        

`I_TRF2:        init_state_r <= (`end_trfc) ? `I_AR3:`I_TRF2;                 //等待第2次自刷新结束,TRFC_CLK个时钟周期

`I_AR3:         init_state_r <= (TRFC_CLK == 0) ? `I_AR4:`I_TRF3; //3次自刷新        

`I_TRF3:        init_state_r <= (`end_trfc) ? `I_AR4:`I_TRF3;                        //等待第3次自刷新结束,TRFC_CLK个时钟周期

`I_AR4:         init_state_r <= (TRFC_CLK == 0) ? `I_AR5:`I_TRF4; //4次自刷新

`I_TRF4:        init_state_r <= (`end_trfc) ? `I_AR5:`I_TRF4;                 //等待第4次自刷新结束,TRFC_CLK个时钟周期

`I_AR5:         init_state_r <= (TRFC_CLK == 0) ? `I_AR6:`I_TRF5; //5次自刷新

`I_TRF5:        init_state_r <= (`end_trfc) ? `I_AR6:`I_TRF5;                        //等待第5次自刷新结束,TRFC_CLK个时钟周期

`I_AR6:         init_state_r <= (TRFC_CLK == 0) ? `I_AR7:`I_TRF6; //6次自刷新

`I_TRF6:        init_state_r <= (`end_trfc) ? `I_AR7:`I_TRF6;                        //等待第6次自刷新结束,TRFC_CLK个时钟周期

`I_AR7:         init_state_r <= (TRFC_CLK == 0) ? `I_AR8:`I_TRF7; //7次自刷新        

`I_TRF7:         init_state_r <= (`end_trfc) ? `I_AR8:`I_TRF7;                        //等待第7次自刷新结束,TRFC_CLK个时钟周期

`I_AR8:         init_state_r <= (TRFC_CLK == 0) ? `I_MRS:`I_TRF8;        //8次自刷新

`I_TRF8:        init_state_r <= (`end_trfc) ? `I_MRS:`I_TRF8;                        //等待第8次自刷新结束,TRFC_CLK个时钟周期

`I_MRS:                init_state_r <= (TMRD_CLK == 0) ? `I_DONE:`I_TMRD;//模式寄存器设置(MRS        

`I_TMRD:        init_state_r <= (`end_tmrd) ? `I_DONE:`I_TMRD;                //等待模式寄存器设置完成,TMRD_CLK个时钟周期

`I_DONE:        init_state_r <= `I_DONE;                // SDRAM的初始化设置完成标志

default: init_state_r <= `I_NOP;

Endcase

 

---------------------------------------------------

case (work_state_r)

                         `W_IDLE:        if(sdram_ref_req & sdram_init_done) begin

work_state_r <= `W_AR;                 //定时自刷新请求

sys_r_wn <= 1‘b1;

end                 

else if(sdram_wr_req & sdram_init_done) begin

work_state_r <= `W_ACTIVE;//SDRAM

sys_r_wn <= 1‘b0;        

end                                                                                        

else if(sdram_rd_req && sdram_init_done) begin

work_state_r <= `W_ACTIVE;//SDRAM

sys_r_wn <= 1‘b1;        

end

else begin

work_state_r <= `W_IDLE;

sys_r_wn <= 1‘b1;

end                

//行有效状态

`W_ACTIVE:         if(TRCD_CLK == 0)

 if(sys_r_wn) work_state_r <= `W_READ;

 else work_state_r <= `W_WRITE;

else work_state_r <= `W_TRCD;

`W_TRCD:         if(`end_trcd)

          if(sys_r_wn) work_state_r <= `W_READ;

          else work_state_r <= `W_WRITE;

else work_state_r <= `W_TRCD;

// SDRAM读数据状态

`W_READ:        work_state_r <= `W_CL;        

`W_CL:                work_state_r <= (`end_tcl) ? `W_RD:`W_CL;        

`W_RD:                work_state_r <= (`end_tread) ? `W_RWAIT:`W_RD;        //后面需要添加一个读完成后的预充电等待状态

`W_RWAIT:        work_state_r <= (`end_trwait) ? `W_IDLE:`W_RWAIT;

// SDRAM写数据状态

`W_WRITE:        work_state_r <= `W_WD;                

`W_WD:                work_state_r <= (`end_twrite) ? `W_TDAL:`W_WD;

`W_TDAL:                work_state_r <= (`end_tdal) ? `W_IDLE:`W_TDAL;

// SDRAM自动刷新状态

`W_AR:                work_state_r <= (TRFC_CLK == 0) ? `W_IDLE:`W_TRFC;

`W_TRFC:        work_state_r <= (`end_trfc) ? `W_IDLE:`W_TRFC;

default:         work_state_r <= `W_IDLE;

endcase

end

 

 

通过并行阅读可以看到,每次自刷新或者读写之前都在CMD命令端,写入命令。通过这种方法来达到操作目的。SDRAM的总的操作思想就是这样,再加入数据的写入和读取操作就好了:

//将待写入数据送到SDRAM数据总线上

always @ (posedge clk or negedge rst_n)

if(!rst_n) sdr_din <= 16‘d0;        //突发数据写寄存器复位

else if((work_state == `W_WRITE) | (work_state == `W_WD)) sdr_din        <=        sys_data_in;        //连续写入存储在wrFIFO中的25616bit数据

 

//产生双向数据线方向控制逻辑

always @ (posedge clk or negedge rst_n)

if(!rst_n) sdr_dlink <= 1‘b0;

   else if((work_state == `W_WRITE) | (work_state == `W_WD)) sdr_dlink <= 1‘b1;

else sdr_dlink <= 1‘b0;

 

assign sdram_data = sdr_dlink ? sdr_din:16‘hzzzz;

 

//------------------------------------------------------------------------------

//数据读出控制

//------------------------------------------------------------------------------

reg[15:0] sdr_dout;        //突发数据读寄存器        

 

//将数据从SDRAM读出

always @ (posedge clk or negedge rst_n)

if(!rst_n) sdr_dout <= 16‘d0;        //突发数据读寄存器复位

else if((work_state == `W_RD) & (cnt_clk > 9‘d0) & (cnt_clk < 9‘d10)) sdr_dout <= sdram_data;        //连续读出256B16bit数据存储到rdFIFO

 

assign sys_data_out = sdr_dout;

 

//------------------------------------------------------------------------------

 

endmodule

---------------------------------------------------------------------------------------------------------------------------------------

再来说明一下,我们整个系统的目的:写入数据到FIFO1缓存,然后写入SDRAM,再由SDRAM写入FIFO2缓存,最后经由串口发送出去。看一下我们的RTL图:

技术分享

 

来看一下运行流程:uut_datagene模块写数据,经由wrf_din送入FIFO1(图中的FIFO模块里面包含FIFO1FIFO2,在quartus里双击可以看到),FIFO1通过sys_data_in送入SDRAM通过sys_data_out送入FIFO2FIFO2通过rdf_dout送给串口模块uut_uartctrl,然后经由rs323_tx发送出去。


附件:特权的源码 http://down.51cto.com/data/2111926

本文出自 “Hi,jiashuo!” 博客,请务必保留此出处http://jiashuo.blog.51cto.com/10830673/1707380

力战SDRAM(一)

标签:fpga sdram

原文地址:http://jiashuo.blog.51cto.com/10830673/1707380

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!