电子大神的日记本,供应链专家的功夫茶盘,在这里记录、分享与共鸣。

登录以开始

SDRAM控制器,V1版,最基本功能,仍在升级中。。。。

 

以上是上电200us后,对SDRAM的初始化时序图:左数第一条黄线是对所有bank预充电,第二条到第三条黄线是8个刷新周期,第四条黄线是模式寄存器设置。

以上是对SDRAM读写操作的时序图:左数第一条黄线是行选通,行地址;第二条列选通,列地址,写数据;第三条发出标志写结束;第四条行选通,行地址;第五条列选通,列地址;第六条发出标志读结束,数据已放到数据总线。

 

/**
  ******************************************************************************
  * @file     sdram_v1.v
  * @author   西殿源
  * @version  V1
  * @date     09/03/2012 最后修改,硬件验证通过
  * @brief    SDRAM控制器
  * @info  1. 本版本实现SDRAM最基本功能
     2. 将bank1和bank0 接到地址总线最底两位,地址线错两位接到地址总线的高位
     3. 顺序寻址,避开页错失的情况,tRCD + CL
     4. 不考虑突发读写
     5. 不考虑页直接命中的情况
     6. 代码风格上尚未做到组合逻辑与时序逻辑的分离
  ******************************************************************************
  */

module sdram_v1(
 //-------------------时钟复位-------------------------------------//
 input     clk,
 input     rst_n,
 //-------------------片内端口-------------------------------------//
 input[23:0]   addr_in,  //内部输入地址总线
 input     wr,   //内部写内存信号,低电平有效,脉宽一个clk周期即可
 input     rd,   //内部读内存信号,低电平有效,脉宽一个clk周期即可
 output reg   wr_ok,  //写SDRAM,=1标志数据已写入SDRAM,脉宽一个clk周期
 output reg   rd_ok,  //读SDRAM,=1标志数据已送到总线上,脉宽一个clk周期
 //-------------------片外端口-------------------------------------//
 output reg   cs_ro,  //SDRAM片选,寄存器输出
 output reg   cke_ro,  //SDRAM时钟使能
 output reg[12:0] addr_ro,  //SDRAM地址总线
 output reg[1:0] bank_ro,  //SDRAM块片选
 output reg   ras_ro,  //SDRAM行激活
 output reg   cas_ro,  //SDRAM列激活
 output reg   we_ro,  //SDRAM写使能
 output reg[1:0]  dqm   //SDRAM Mask
 );
 
 
 parameter YES   = 1'b1;
 parameter NO    = 1'b0;
 parameter HI    = 1'b1;
 parameter LO    = 1'b0;
 parameter INIT_200  = 3'd0;
 parameter INIT_CHG  = 3'd1;
 parameter INIT_FRE  = 3'd2;
 parameter INIT_MRS  = 3'd3;
 parameter INIT_END  = 3'd4;
 parameter INIT   = 3'd0;
 parameter IDLE   = 3'd1;
 parameter WR_SG   = 3'd2;
 parameter WR_BU   = 3'd3;
 parameter RD_SG   = 3'd4;
 parameter RD_BU   = 3'd5;
 
 
 reg[1:0] next_init;
 reg[2:0] cur_init; //SDRAM状态之初始化状态寄存器
 reg[15:0] dly_cnt; //延时周期数设置寄存器,君位
 reg[15:0] dly_cnt1; //延时周期计数器,臣位
 
 reg[2:0] next_stat; 
 reg[2:0] cur_stat; //SDRAM状态寄存器
 
 reg dly_en;    //延时器使能,与dly_cnt一起使用,君位
 reg dly_en1;   //延时器使能,与dly_cnt1一起使用,臣位
 wire dly_ok;    //延时到
 
 reg exe_enr;
 wire exe_enw;
 
 reg[1:0] step;
 
 reg[7:0] i;
 reg nop_yes;
 
 
 assign exe_enw = (exe_enr == YES) || (dly_ok == YES);
 
 
 always @(posedge clk,negedge rst_n) begin
  if(!rst_n) begin
   cur_stat <= INIT;       //SDRAM工作状态寄存器初始化
   cur_init <= INIT_200;     //SDRAM初始化状态寄存器
   dly_en <= NO;
   dly_cnt <= 0;
   exe_enr <= YES;
   step <= 0;         //SDRAM某工作状态的子状态初始化
   nop_yes <= NO;
   i <= 0;
   
   wr_ok <= LO;
   rd_ok <= LO;
   
   {cs_ro,cke_ro,ras_ro,cas_ro,we_ro} <= 5'b11111;
   addr_ro <= 0;
   bank_ro <= 0;
   dqm <= 0;
   end
  else begin
   case(cur_stat)
    INIT : begin       //初始化未完毕不能转到其他状态
     if(exe_enw == YES) begin  //由exe_enr或dly_ok驱动本条件块;刚进入此状态时,由exe_enr驱动;之后由dly_ok驱动
      exe_enr <= NO;     // 进入条件块应清除exe_enr,使得只有延时时刻到达时才进入下一子状态
      case(cur_init)
       INIT_200 : begin   //上电后200us稳定期
        cs_ro <= LO;
        dly_cnt <= 10000;  //用于延时块
        dly_en <= YES;   //用于延时块
        nop_yes <= NO;
        cur_init <= INIT_CHG;
        end
        
       INIT_CHG : begin   //所有bank预充电
        {cs_ro,cke_ro,ras_ro,cas_ro,we_ro} <= 5'b01010;
        addr_ro[10] <= HI; //所有bank预充电,持续时间要大于20ns
        dly_cnt <= 2;   //用于延时块
        dly_en <= YES;   //用于延时块
        nop_yes <= YES;
        i <= 7;
        cur_init <= INIT_FRE;
        end
        
       INIT_FRE : begin   //8个刷新周期
        {cs_ro,cke_ro,ras_ro,cas_ro,we_ro} <= 5'b01001;
        dly_cnt <= 9;   //用于延时块
        dly_en <= YES;   //用于延时块
        i <= i - 1;
        nop_yes <= YES;
        if(i==0) cur_init <= INIT_MRS;
        else ; 
        end
        
       INIT_MRS : begin   //模式寄存器设置
        {cs_ro,cke_ro,ras_ro,cas_ro,we_ro} <= 5'b01000;
        bank_ro <= 2'd0;
        addr_ro <= {
             3'b000,
             1'b1,
             2'b00,
             3'b010,
             1'b0,
             3'b000
            };
        dly_cnt <= 2;   //用于延时块  设定模式寄存器后,等2个clk才能接受新的命令
        dly_en <= YES;   //用于延时块
        nop_yes <= YES;
        cur_init <= INIT_END;
        end
        
       INIT_END : begin   //清除操作
        {cs_ro,cke_ro,ras_ro,cas_ro,we_ro} <= 5'b01111; //空操作
        cur_stat <= IDLE;
        end
        
       default : cur_init <= INIT_200;
      endcase
      end//if
     else begin
      dly_en <= NO;
      if(nop_yes == YES) {cs_ro,cke_ro,ras_ro,cas_ro,we_ro} <= 5'b01111;
      else ;
      end
     end
     
    IDLE : begin       //空闲状态检测wr,rd位,判断是否有数据读写请求
     case({wr,rd})      //
      2'b01 : begin
       cur_stat <= WR_SG;  //有写数据请求,跳转到写状态,单蹦个寻址
       step <= 0;
       end
       
      2'b10 : begin
       cur_stat <= RD_SG;  //有读数据请求,跳转到读状态,单蹦个寻址
       step <= 0;
       end
       
      default : ;     //空操作
     endcase
     exe_enr <= YES;
     wr_ok <= LO;
     rd_ok <= LO;
     end
     
    WR_SG : begin       //单蹦个的写状态
     if(exe_enw == YES) begin  //由exe_enr或dly_ok驱动本条件块;刚进入此状态时,由exe_enr驱动;之后由dly_ok驱动
      exe_enr <= NO;     //进入条件块后清除exe_enr,使得只有延时时刻到达时才进入下一子状态
      case(step)
       0 : begin
        {cs_ro,cke_ro,ras_ro,cas_ro,we_ro} <= 5'b01011;  //行激活
        bank_ro <= addr_in[1:0];   //将bank1和bank0 接到地址总线最底两位,地址线错两位接到地址总线的高位
        addr_ro <= addr_in[23:11];
        
        dly_cnt <= 2;  //用于延时块 tRCD=2
        dly_en <= YES;  //用于延时块
        nop_yes <= YES;
        step <= 1;
        end
        
       1 : begin
        {cs_ro,cke_ro,ras_ro,cas_ro,we_ro} <= 5'b01100;  //列激活
        addr_ro[8:0] <= addr_in[10:2]; //将bank1和bank0 接到地址总线最底两位,地址线错两位接到地址总线的高位
        addr_ro[10] <= HI;  //自动预充
        
        dly_cnt <= 2;      //用于延时块
        dly_en <= YES;      //用于延时块
        nop_yes <= YES;
        step <= 2;
        end
        
       2 : begin   //清除操作
        {cs_ro,cke_ro,ras_ro,cas_ro,we_ro} <= 5'b01111;
        cur_stat <= IDLE;     //本状态任务完毕,转到IDLE状态_ok <=
        wr_ok <= HI;      //=1标志数据已写入SDRAM,脉宽一个clk周期
        end
        
       default : ;
       
      endcase
      end//if
     else begin
      dly_en <= NO;
      {cs_ro,cke_ro,ras_ro,cas_ro,we_ro} <= 5'b01111;
      end
     end//WR_SG
    
    RD_SG : begin        //单蹦个的读状态
     if(exe_enw == YES) begin   //由exe_enr或dly_ok驱动本条件块;刚进入此状态时,由exe_enr驱动;之后由dly_ok驱动
      exe_enr <= NO;      //进入条件块后清除exe_enr,使得只有延时时刻到达时才进入下一子状态
      case(step)
       0 : begin
        {cs_ro,cke_ro,ras_ro,cas_ro,we_ro} <= 5'b01011;  //行激活
        bank_ro <= addr_in[1:0]; //将bank1和bank0 接到地址总线最底两位,地址线错两位接到地址总线的高位
        addr_ro <= addr_in[23:11];
        
        dly_cnt <= 2;    //用于延时块 tRCD=2
        dly_en <= YES;    //用于延时块
        nop_yes <= YES;
        step <= 1;
        end
        
       1 : begin
        {cs_ro,cke_ro,ras_ro,cas_ro,we_ro} <= 5'b01101;
        addr_ro <= addr_in[10:2]; //将bank1和bank0 接到地址总线最底两位,地址线错两位接到地址总线的高位
        addr_ro[10] <= HI;   //自动预充 CL=2
        
        dly_cnt <= 2;  //用于延时块
        dly_en <= YES;  //用于延时块
        nop_yes <= YES;
        step <= 2;
        end
        
       2 : begin    //清除操作
        {cs_ro,cke_ro,ras_ro,cas_ro,we_ro} <= 5'b01111;
        cur_stat <= IDLE;     //本状态任务完毕,转到IDLE状态d_
        rd_ok <= HI;      //=1标志数据已送到总线上,脉宽一个clk周期
        end
         
       default : ;
       
      endcase
      end
     else begin
      dly_en <= NO;
      {cs_ro,cke_ro,ras_ro,cas_ro,we_ro} <= 5'b01111;
      end
     end//RD_SG 
     
    default : cur_stat <= INIT;
   endcase
   end//else
 end//always

 //延时进程块
 always @(posedge clk,negedge rst_n) begin
  if(!rst_n) begin
   dly_en1 <= NO;
   dly_cnt1 <= 1;  //初始化要赋以非零值,防止dly_ok拉高
   end
  else if(dly_en == YES) begin
   dly_en1 <= YES;
   dly_cnt1 <= dly_cnt - 2; //延时周期数修正
   end
  else if(dly_en1 == YES) begin
   if(dly_cnt1 == 0) begin
    dly_en1 <= NO;
    dly_cnt1 <= 1; //随便赋以非零值,保证dly_ok脉宽为一个clk周期
    end
   else begin
    dly_cnt1 <= dly_cnt1 - 1;
    end
   end
  else ;     //空操作
 end//always
 
 assign dly_ok = (dly_cnt1 == 0)?YES : NO;
 
 
 endmodule
 
  

博主
xd_jiangning@163.com
静远阁
斗胆写写技术随笔
点击跳转