1.SDRAM芯片容量计算:
2^(行地址线宽)*2^(列地址线宽)*2^(bank地址线宽)*数据位宽=xMbit
2.初始化操作:
首先明确控制信号有哪些,
包括输入信号包括Clk时钟,Cke时钟使能,Cs_n片选,Ras_n行选通,Cas_n列选通,We_n写使能,Ba(bank地址),SA(地址总线),DQM数据掩码(一位控制8位);DQ(数据输入输出复用)
对于SDRAM的操作,是通过命令来控制的,Command={Cs_n,Ras_n,Cas_n,We_n},同时地址总线Sa和DQM作辅助信号,来设置相应的模式寄存器,在设置模式寄存器命令的时候,地址总线提供了模式寄存器的值。
在对SDRAM进行操作之前,SDRAM必须被初始化。初始化操作为:
VDD和VDDQ上电,首先需要延时等待100us,在此期间执行空操作NOP,延时完成后首先执行一次预充电命令,所有BANK(A10为高电平)都必须被预充电,使得器件所有BANK处于空闲状态。
进入空闲状态后执行两次自动刷新命令,之后就可以进行加载模式寄存器,在模式加载完成后的第二个时钟周期就可以执行激活命令了。
使用一个线性序列机即可实现初始化操作
再设置一个时钟的计数器
之后用一个case对计数值判断,在相应时刻给出相应命令。
可以设置一个初始化完成标识信号,方便其他模块调用
至此,初始化操作完成。
------------------------------------------------------------------------------------------------------------------------------------------------------------
控制器设计:
行列地址输入进来后先进行寄存,防止操作过程中发生改变:
主状态机
可以看到刷新操作,读操作和写操作是有优先级的,刷新>写操作>读操作
同时设置了一个标志位FF,只有当FF=0时才执行任务,执行完任务置1.(任务包括刷新任务,读数据,写数据)。
下面对三个模块进行实现:
一、
对于自动刷新,首先要设置一个刷新定时器ref_time_cnt,保证<64ms刷新一次,此代码中设置1560个clk_period,复位时ref_tine_cnt计数器清零,否则计数到1560时置1,否则如果初始化完成或者计数值大于0,计数器自加1,其他时候保持。
在设置一个刷新定时标志位ref_time_flag,当刷新定时到就置1.
然后就可以写自动刷新操作任务了,用task实现
对自动刷新过程时间设置计数器ref_cnt,复位时ref_cnt清零,否则如果计数到刷新结束清零,否则如果刷新请求来临或者刷新计数值>0,计数器自加1,其他时候保持
二、一次突发写
localparam wr_ACT_TIME=1'b1,//激活时刻 wr_WRITE_TIME=1+SC_RCD,//+激活到读命令或写命令延时tRCD wr_PRE_TIME=1+SC_RCD+SC_BL+WR_PRE,//+突发长度,写操作完成到预充电时间间隔 wr_END_TIME=SC_RCD+SC_BL+WR_PRE+REF_PRE;//+等待时间tRP reg [15:0]wr_cnt; reg wr_opt,wr_opt_done; reg Wr_data_vaild; wire Wdata_done; //突发写操作过程时间计数器 always@(posedge clk or negedge rst_n) if(!rst_n) wr_cnt<=16'd0; else if(wr_cnt==wr_END_TIME) wr_cnt<=16'd0; else if(wr_req||wr_cnt>0) wr_cnt<=wr_cnt+16'd1; else wr_cnt<=wr_cnt; //突发写操作任务 task write_data; begin case(wr_cnt) wr_ACT_TIME:begin Command<=C_ACT; Sa<=raddr_r;//激活行 Ba<=Baddr; end wr_WRITE_TIME:begin Command<=C_WR; Sa<={1'b0,caddr_r[8:0]};//激活列 Ba<=Baddr; end wr_PRE_TIME:begin Command<=C_PRE;//预充电 Sa<=1'b1; end wr_END_TIME:begin Command<=C_NOP; FF<=1'b1; end default:Command<=C_NOP; endcase end endtask //写操作完成标志位 always@(posedge clk or negedge rst_n) begin if(!rst_n) wr_opt_done<=1'b0; else if(wr_cnt==wr_END_TIME) wr_opt_done<=1'b1; else wr_opt_done<=1'b0; end //写操作过程标识 always@(posedge clk or negedge rst_n) begin if(!rst_n) wr_opt<=1'b0; else if(wr_req) wr_opt<=1'b1; else if(wr_opt_done==1'b1) wr_opt<=1'b0; else wr_opt<=wr_opt; end //写数据操作,数据写入时刻有效区间 always@(posedge clk or negedge rst_n) begin if(!rst_n) Wr_data_vaild<=1'b0; else if((wr_cnt>SC_RCD)&&(wr_cnt<=SC_RCD+SC_BL)) Wr_data_vaild<=1'b1; else Wr_data_vaild<=1'b0; end //写操作数据写完成标志位 assign Wdata_done=(wr_cnt==SC_RCD+SC_BL)?1'b1:1'b0; //sDRAM数据线三态控制 assign Dq=Wr_data_vaild?Wr_data:16'bz;
三、一次突发读
localparam rd_ACT_TIME=1'b1, rd_READ_TIME=1+SC_RCD, rd_PRE_TIME=1+SC_RCD+SC_BL, rd_END_TIME=SC_RCD+SC_BL+SC_CL; reg [15:0]rd_cnt; reg rd_opt,rd_opt_done; reg Rd_data_vaild; //突发读操作过程时间计数器 always@(posedge clk or negedge rst_n) begin if(!rst_n) rd_cnt<=16'd0; else if(rd_cnt==rd_END_TIME) rd_cnt<=16'd0; else if(rd_req||rd_cnt>0) rd_cnt<=rd_cnt+16'd1; else rd_cnt<=rd_cnt; end //突发读任务 task read_data; begin case(rd_cnt) rd_ACT_TIME:begin Command<=C_ACT; Sa<=raddr_r; Ba<=Baddr; end rd_READ_TIME:begin Command<=C_RD; Sa<={1'b0,caddr_r[8:0]};//突发模式设置和突发长度设置 Ba<=Baddr; end rd_PRE_TIME:begin Command<=C_PRE; Sa[10]<=1'b1; end rd_END_TIME:begin Command<=C_NOP; FF<=1'b1; end default:Command<=C_NOP; endcase end endtask //突发读完成标志 always@(posedge clk or negedge rst_n) begin if(!rst_n) rd_opt_done<=1'b0; else if(rd_cnt==rd_END_TIME) rd_opt_done<=1'b1; else rd_opt_done<=1'b0; end //突发读过程标志 always@(posedge clk or negedge rst_n) begin if(!rst_n) rd_opt<=1'b0; else if(rd_req==1'b1) rd_opt<=1'b1; else if(rd_opt_done==1'b1) rd_opt<=1'b0; else rd_opt<=rd_opt; end //突发读过程数据读完标志位 assign Rdata_done=(rd_cnt==rd_END_TIME)?1'b1:1'b0; //读数据操作,数据有效区 always@(posedge clk or negedge rst_n) begin if(!rst_n) Rd_data_vaild<=1'b0; else if((rd_cnt>SC_RCD+SC_CL)&&(rd_cnt<=SC_RCD+SC_CL+SC_BL)) Rd_data_vaild<=1'b1; else Rd_data_vaild<=1'b0; end //读数据 assign Rd_data=Dq;
四、还有一些穿插在主状态机
//*****************************************************************// //写操作过程 刷新到 记住刷新信号// assign ref_break_wr=(ref_time_flag&&wr_opt)?1'b1:((!wr_opt)?1'b0:ref_break_wr); //读操作过程 刷新到 记住刷新信号// assign ref_break_rd=(ref_time_flag&&rd_opt)?1'b1:((!rd_opt)?1'b0:ref_break_rd); //刷新请求信号 always@(*) begin case(main_state) AREF:begin if(ref_time_flag) ref_req=1'b1; else ref_req=1'b0; end WRITE:begin if(ref_break_wr&&wr_opt_done) ref_req=1'b1; else ref_req=1'b0; end READ:begin if(ref_break_rd&&rd_opt_done) ref_req=1'b1; else ref_req=1'b0; end default:ref_req=1'b0; endcase end //**************************************************************// //刷新过程 外部写使能到 记住写使能信号 assign wr_break_ref=(Wr&&ref_opt)?1'b1:((!ref_opt)?1'b0:wr_break_ref); //写操作请求信号 always@(*) begin case(main_state) AREF:begin if(Wr&&(!wr_break_ref)&&!ref_time_flag) wr_req=1'b1; else if(wr_break_ref&&ref_opt_done) wr_req=1'b1; else wr_req=1'b0; end WRITE:begin if(wr_opt_done&&Wr&&!ref_break_wr) wr_req=1'b1; else wr_req=1'b0; end READ:begin if(rd_opt_done&&Wr&&!ref_break_rd) wr_req=1'b1; else wr_req=1'b0; end default:wr_req=1'b0; endcase end //刷新过程 外部读使能到 记住读使能信号 assign rd_break_ref=(Rd&&ref_opt)?1'b1:((!rd_opt)?1'b0:rd_break_ref); //读操作请求信号 always@(*) begin case(main_state) AREF:begin if(Rd&&(!rd_break_ref)&&(!wr_break_ref)&&(!ref_time_flag)) rd_req=1'b1; else if(ref_opt_done&&(!wr_break_ref)&&rd_break_ref) rd_req=1'b1; else rd_req=1'b0; end WRITE:begin if(wr_opt_done&&(!ref_break_wr)&&!Wr&&Rd) rd_req=1'b1; else rd_req=1'b0; end READ:begin if(rd_opt_done&&(!ref_break_rd)&&!Wr&&Rd) rd_req=1'b1; else rd_req=1'b0; end default:rd_req=1'b0; endcase end