xidianyuan的个人空间 https://blog.eetop.cn/862406 [收藏] [复制] [分享] [RSS]

空间首页 动态 记录 日志 相册 主题 分享 留言板 个人资料

日志

SDRAM控制器V2版

已有 1596 次阅读| 2012-9-8 22:49

看看V1版,实在太凌乱,还是先把风格整理一下,再升级功能吧。相对于V1版,本次V2.3版采用两段式状态机,同时做到同步输出,占用资源大大减小,可读性也大幅提升。另有V2.2版,区别于V2.3就是采用独热编码,其他一样。把自己写的代码贴到这里实在是献丑了,主要是希望得到大家的点评和建议

/**
  ******************************************************************************
  * @file     sdram_v2_3.v
  * @author   西殿源
  * @version  V2.3
  * @date     09/08/2012 最后修改
  * @brief    SDRAM控制器
  * @info  1. 本版本实现SDRAM最基本功能
     2. 将bank1和bank0 接到地址总线最底两位,地址线错两位接到地址总线的高位
     3. 顺序寻址,避开页错失的情况,tRCD + CL
     4. 不考虑突发读写
     5. 不考虑页直接命中的情况
     6. 状态机两段式+同步输出
     7. 砍掉POWER_ON,
  ******************************************************************************
  */

module sdram_v2_3(
 //-------------------时钟复位-------------------------------------//
 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_ro   //SDRAM Mask
 
 );
 
 
 parameter YES   = 1'b1;
 parameter NO    = 1'b0;
 parameter HI    = 1'b1;
 parameter LO    = 1'b0;
 
  parameter MODEL_SET = 5'd0;  //模式寄存器设置
 parameter AUTO_FREH = 5'd1;  //自动刷新
 parameter SELF_ENT = 5'd2;  //进入自刷新,就是刷新操作不在依靠clk
 parameter SELF_EXT = 5'd3;  //推出自刷新,就是刷新操作不在依靠clk
 parameter ROW_ACTV = 5'd4;  //行激活,行地址
 parameter COLUM_RD = 5'd5;  //列激活,列地址
 parameter COLUM_WR = 5'd6;  //列激活,写数据
 parameter BST_STOP = 5'd7;  //突发停
 parameter PRE_CHAG_A = 5'd8;  //所有bank预充电
 parameter PRE_CHAG_S = 5'd9;  //选中bank预充电
 parameter SSPD_ENT = 5'd10; //进入挂起
 parameter SSPD_EXT = 5'd11; //退出挂起
 parameter POWR_ENT = 5'd12; //进入
 parameter POWR_EXT = 5'd13; //退出
 parameter DQM   = 5'd14; //数据掩码
 parameter NOP_OPTN = 5'd15; //空操作
 parameter WR_OK  = 5'd16; //写入成功
 parameter RD_OK  = 5'd17; //读出成功
 parameter POWER_ON = 5'd18; //上电初始化
 parameter CHK_BEGN = 5'd19; //上电初始化
 
 reg wr_ok_temp;      //=1写数据成功,用于组合逻辑,
 reg rd_ok_temp;      //=1读数据成功,用于组合逻辑,
 reg cs_temp;       //SDRAM片选,用于组合逻辑,
 reg cke_temp;       //SDRAM时钟使能,用于组合逻辑,
 reg[12:0] addr_temp;     //SDRAM地址,用于组合逻辑,
 reg[1:0] bank_temp;     //SDRAM块选,用于组合逻辑,
 reg ras_temp;       //SDRAM行激活,用于组合逻辑,
 reg cas_temp;       //SDRAM列激活,用于组合逻辑,
 reg we_temp;       //SDRAMD读写信号,用于组合逻辑,
 reg[1:0] dqm_temp;     //SDRAM Mask 
 
 reg chk_en;        //=1时,处于检测模式,=0时处于状态跳转模式;用于时序逻辑
 reg chk_en_temp;      //作为chk_en的赋值,用于组合逻辑
 
 
 reg[15:0] dly_cnt;     //延时次数装载寄存器,例如某操作延时2个clk再操作,可以赋以2
 reg[15:0] dly_cnt_temp;    //作为dly_cnt的赋值,用于组合逻辑
 reg[15:0] dly_cnt1;     //延时计数器,时序逻辑,用于延时always块
 
 reg dly_en;        //=1时使能延时定时器,与dly_cnt配合使用,有效宽度为一个clk周期
 reg dly_en_temp;      //作为dly_en的赋值,用于组合逻辑
 reg dly_en1;       //延时计数使能,与dly_cnt1配合使用,
 reg dly_ok;        //延时到时,=1,脉宽为一个clk周期
 
 
// reg[4:0] stat_temp;
 reg stat_cnt_en;      //状态重复执行使能,用于时序逻辑
 reg stat_cnt_en_temp;    //状态重复执行使能,用于组合逻辑,作为stat_cnt_en的赋值
 reg[3:0] stat_cnt;     //状态重复次数计数器
// reg[4:0] nxt_stat_clk;    //??ò???×′ì?£???±eóú??ò???2ù×÷ ,ó?óúê±Dò???-
 reg[4:0] nxt_stat;     //下一个状态,组合逻辑状态
 reg[4:0] nxt_opt;      //下一个操作,例如无论读写,在行选通之后要紧随列的读选通或写选通,将此值付给nxt_stat
 reg[4:0] cur_stat;     //当前状态,时序逻辑
// reg[15:0] nop_cnt;     //??2ù×÷??êy
 
 reg[23:0] addr_r;      //地址锁存,
 
 reg init_en;
 
 always @(posedge clk,negedge rst_n) begin
  if(!rst_n) begin
   addr_r <= 0;
   
   wr_ok <= NO;
   rd_ok <= NO;
   
   {cs_ro,cke_ro,ras_ro,cas_ro,we_ro} <= 5'b00000;
   addr_ro <= 0;
   bank_ro <= 0;
   dqm_ro <= 0;
   
   init_en <= NO;
   
   dly_cnt <= 10;        //上电后稳定200us 定位10000;
   dly_en <= YES;
   stat_cnt_en <= NO;
    chk_en <= NO;
   
   cur_stat <= PRE_CHAG_A;
   nxt_opt <= PRE_CHAG_A;
   
   end
  else if(chk_en) begin         //chk_en=1时,每个clk上升沿检测读写请求位
   case({wr,rd})
    2'b01 : begin
     addr_r <= addr_in;        //锁存地址
     cur_stat <= ROW_ACTV;       //跳转到行激活状态
     nxt_opt <= COLUM_WR;        //行激活状态完毕后跳转到列写激活状态
     chk_en <= NO;          //关闭读写检测,切入状态跳转模式。下一个clk就切入
     init_en <= YES;         //触发状态跳转,就在下一个clk
     end
    2'b10 : begin
     addr_r <= addr_in;        //锁存地址
     cur_stat <= ROW_ACTV;       //跳转到行激活状态
     nxt_opt <= COLUM_RD;        //行激活状态完毕后跳转到列读激活状态
     chk_en <= NO;          //关闭读写检测,切入状态跳转模式。下一个clk就切入
     init_en <= YES;
     end
    default : ;
   endcase
   end
  else begin              //chk_en=0,状态跳转模式
   if((init_en == YES)||(dly_ok == YES)) begin  //,或延时时间到执行状态跳转
    init_en <= NO;           //进来后关掉init_en,保证下一次状态跳转是在延时到
    
    cur_stat <= nxt_stat;        //状态切换
    
    wr_ok <= wr_ok_temp;         //同步输出
    rd_ok <= rd_ok_temp;         //同步输出
    
    {cs_ro,cke_ro,ras_ro,cas_ro,we_ro} <= {cs_temp,cke_temp,ras_temp,cas_temp,we_temp};
    addr_ro <= addr_temp;        //同步输出
    bank_ro <= bank_temp;        //同步输出
    dqm_ro <= dqm_temp;         //同步输出
    
    stat_cnt_en <= stat_cnt_en_temp;     //同步输出
    dly_en <= dly_en_temp;        //同步输出
    dly_cnt <= dly_cnt_temp;       //同步输出
     chk_en <= chk_en_temp;        //同步输出
    end
   else begin             //不满足(init_en == YES)||(dly_ok == YES),执行空操作
    {cs_ro,cke_ro,ras_ro,cas_ro,we_ro} <= 5'b01111; //空操作
    wr_ok <= NO;            //清除写成功标志,保证wr_ro脉宽为一个clk周期
    rd_ok <= NO;
    dly_en <= NO;            //清除写成功标志,保证dly_en脉宽为一个clk周期
    end
   
   end
 end

 
 
 always @(cur_stat,stat_cnt) begin
  case(cur_stat)
//   POWER_ON  : begin
//    {cs_temp,cke_temp,ras_temp,cas_temp,we_temp} = 5'b01111; //??2ù×÷
//    addr_temp = 0;  //3?ê??μ
//    bank_temp = 0;  //3?ê??μ
//    dqm_temp = 0;  //3?ê??μ
//    
//    stat_cnt_en_temp = NO;           //±?2ù×÷ 2? Dèòa???′?′DD
////    stat_temp = POWER_ON;           
//    
//    wr_ok_temp = NO;  //3?ê??μ
//    rd_ok_temp = NO;  //3?ê??μ
//    
//    chk_en_temp = NO;  //?′DD±?2ù×÷oó2??êDí?ì2a?áD′???ó
//    
//    dly_cnt_temp = 10; //é?μ??è?¨200us
//    dly_en_temp = YES;  //′ò?a?¨ê±?÷
//    
//    nxt_stat = PRE_CHAG_A; //??2ù×÷íêá???oóê??¤3?μ?2ù×÷£?1ê3???ò???2ù×÷?£2ù×÷ó?2ù×÷????£?ò???2ù×÷?a??×′ì?×÷?a1y?é
//    end
//   
//    
   PRE_CHAG_A : begin               //预充电
    {cs_temp,cke_temp,ras_temp,cas_temp,we_temp} = 5'b01010;  //预充电
    addr_temp = 13'b0010000000000;          //A[10]=1
    bank_temp = 0;                //
    dqm_temp = 0;                //
    
    stat_cnt_en_temp = NO;             //本状态 不 需要重复执行
//    stat_temp = PRE_CHAG_A;  //ó?±?2ù×÷?T1???±??3?μ£?ò??aunsafe behavior?ˉ?? 
    
    wr_ok_temp = NO;               //
    rd_ok_temp = NO;               //
    
    chk_en_temp = NO;               //          
    
    dly_cnt_temp = 2;               //延时2个clk周期再跳转到下一个操作状态
    dly_en_temp = YES;              //时钟到来时更新到dly_en,dly_en=1 启动定时计数器,随后的一个clk使dly_en=0
    
    nxt_stat = AUTO_FREH; //??2ù×÷íêá???oóê?×??ˉ?¢D?2ù×÷£?1ê3???ò???2ù×÷?£2ù×÷ó?2ù×÷????£?ò???2ù×÷?a??×′ì?×÷?a1y?é
    end
    
   AUTO_FREH : begin              //8???¢D??ü?ú
    {cs_temp,cke_temp,ras_temp,cas_temp,we_temp} = 5'b01001; //8???¢D??ü?ú
    addr_temp = 0;                //
    bank_temp = 0;                //
    dqm_temp = 0;                //
    
    stat_cnt_en_temp = YES;             //本状态 需要 重复执行
//    stat_temp = AUTO_FREH;   //±?2ù×÷Dèòa???′?′DD
    
    wr_ok_temp = NO;
    rd_ok_temp = NO;
    
    chk_en_temp = NO;          
    
    dly_cnt_temp = 9;               //延时9个clk周期再跳转到下一个操作状态
    dly_en_temp = YES;              //时钟到来时更新到dly_en,dly_en=1 启动定时计数器,随后的一个clk使dly_en=0
    
    if(stat_cnt == 7) nxt_stat = MODEL_SET;
    else nxt_stat = AUTO_FREH;
    
    end
    
   MODEL_SET : begin               //模式寄存器设置
    {cs_temp,cke_temp,ras_temp,cas_temp,we_temp} = 5'b01000;  //模式寄存器设置
    addr_temp = {
         3'b000,
         1'b1,
         2'b00,
         3'b010,
         1'b0,
         3'b000
        };
    bank_temp = 0;                //
    dqm_temp = 0;                //
    
    stat_cnt_en_temp = NO;             //本状态 不 需要重复执行
//    stat_temp = MODEL_SET;  //ó?±?2ù×÷?T1???±??3?μ£?ò??aunsafe behavior?ˉ??           
    
    wr_ok_temp = NO;               //
    rd_ok_temp = NO;               //
    
    chk_en_temp = NO;            
    
    dly_cnt_temp = 2;               //延时2个clk周期再跳转到下一个操作状态
    dly_en_temp = YES;              //时钟到来时更新到dly_en,dly_en=1 启动定时计数器,随后的一个clk使dly_en=0
    
    nxt_stat = CHK_BEGN;
    end
    
   ROW_ACTV  : begin
    {cs_temp,cke_temp,ras_temp,cas_temp,we_temp} = 5'b01011;  //行激活
    addr_temp = addr_r[23:11];
    bank_temp = addr_r[1:0];            //??bank1oíbank0 ?óμ?μ??·×ü??×?μ×á???£?μ??·??′íá????óμ?μ??·×ü??μ?????
    dqm_temp = 0;                //
    
    stat_cnt_en_temp = NO;             //本状态 不 需要重复执行
//    stat_temp = ROW_ACTV;  //ó?±?2ù×÷?T1???±??3?μ£?ò??aunsafe behavior?ˉ??         
    
    wr_ok_temp = NO; 
    rd_ok_temp = NO; 
    
    chk_en_temp = NO;              
    
    dly_cnt_temp = 2;               //延时2个clk周期再跳转到下一个操作状态
    dly_en_temp = YES;              //时钟到来时更新到dly_en,dly_en=1 启动定时计数器,随后的一个clk使dly_en=0
    
    nxt_stat = nxt_opt;
    end
    
   COLUM_WR : begin
    {cs_temp,cke_temp,ras_temp,cas_temp,we_temp} = 5'b01100;  //列写激活
    addr_temp = {4'b0010,addr_r[10:2]};         //A[10]=1 列地址
    bank_temp = addr_r[1:0];            //模块内部的地址总线最地两位接到bank上             
    dqm_temp = 0;                //
    
    stat_cnt_en_temp = NO;             //本状态 不 需要重复执行
//    stat_temp = COLUM_WR;   //ó?±?2ù×÷?T1???±??3?μ£?ò??aunsafe behavior?ˉ??           
    
    wr_ok_temp = NO;               //
    rd_ok_temp = NO;               //
    
    chk_en_temp = NO;              
    
    dly_cnt_temp = 2;               //延时2个clk周期再跳转到下一个操作状态
    dly_en_temp = YES;              //时钟到来时更新到dly_en,dly_en=1 启动定时计数器,随后的一个clk使dly_en=0
    
    nxt_stat = WR_OK;
    end
    
   WR_OK  : begin
    {cs_temp,cke_temp,ras_temp,cas_temp,we_temp} = 5'b01111;  //发出置位写成功标志
    addr_temp = 0;                //
    bank_temp = 0;                //
    dqm_temp = 0;                //
    
    stat_cnt_en_temp = NO;             //本状态 不 需要重复执行
//    stat_temp = WR_OK;  //ó?±?2ù×÷?T1???±??3?μ£?ò??aunsafe behavior?ˉ??            
    
    wr_ok_temp = YES;               //时钟到来时就更新到wr_ok,置位写成功,随后的clk再清除wr_ok。
    rd_ok_temp = NO;  
    
    chk_en_temp = NO;             
    
    dly_cnt_temp = 2;               //延时2个clk周期再跳转到下一个操作状态
    dly_en_temp = YES;              //时钟到来时更新到dly_en,dly_en=1 启动定时计数器,随后的一个clk使dly_en=0
    
    nxt_stat = CHK_BEGN;
    end
    
   COLUM_RD : begin
    {cs_temp,cke_temp,ras_temp,cas_temp,we_temp} = 5'b01101;  //áD?¤??£??áêy?Y
    addr_temp = {4'b0010,addr_r[10:2]};         //??bank1oíbank0 ?óμ?μ??·×ü??×?μ×á???£?μ??·??′íá????óμ?μ??·×ü??μ?????
    bank_temp = addr_r[1:0];
    dqm_temp = 0;                //
    
    stat_cnt_en_temp = NO;             //本状态 不 需要重复执行
//    stat_temp = COLUM_RD;   //ó?±?2ù×÷?T1???±??3?μ£?ò??aunsafe behavior?ˉ??          
    
    wr_ok_temp = NO; 
    rd_ok_temp = NO; 
    
    chk_en_temp = NO;              
    
    dly_cnt_temp = 2;               //延时2个clk周期再跳转到下一个操作状态
    dly_en_temp = YES;              //时钟到来时更新到dly_en,dly_en=1 启动定时计数器,随后的一个clk使dly_en=0
    
    nxt_stat = RD_OK;
    end
    
   RD_OK  : begin
    {cs_temp,cke_temp,ras_temp,cas_temp,we_temp} = 5'b01111;  //áD?¤??£??áêy?Y
    addr_temp = 0;                //
    bank_temp = 0;                //
    dqm_temp = 0;                //
    
    stat_cnt_en_temp = NO;             //本状态 不 需要重复执行
//    stat_temp = RD_OK;  //ó?±?2ù×÷?T1???±??3?μ£?ò??aunsafe behavior?ˉ??            
    
    wr_ok_temp = NO;  
    rd_ok_temp = YES;               //时钟到来时就更新到rd_ok,置位写成功,随后的clk再清除rd_ok。
    
    chk_en_temp = NO;              
    
    dly_cnt_temp = 2;               //延时2个clk周期再跳转到下一个操作状态
    dly_en_temp = YES;              //时钟到来时更新到dly_en,dly_en=1 启动定时计数器,随后的一个clk使dly_en=0
    
    nxt_stat = CHK_BEGN;
    end
    
   CHK_BEGN : begin
    {cs_temp,cke_temp,ras_temp,cas_temp,we_temp} = 5'b01111;  //áD?¤??£??áêy?Y
    addr_temp = 0;                //
    bank_temp = 0;                //
    dqm_temp = 0;                //
   
    stat_cnt_en_temp = NO;             //本状态 不 需要重复执行
//    stat_temp = CHK_BEGN;  //ó?±?2ù×÷?T1???±??3?μ£?ò??aunsafe behavior?ˉ??
    
    wr_ok_temp = NO;
    rd_ok_temp = NO;
    
    chk_en_temp = YES;              //时钟到来时更新到chk_en,准备转到读写请求检测模式
    
    dly_cnt_temp = 2;               //延时2个clk周期再跳转到下一个操作状态
    dly_en_temp = NO;               //这个打开或关闭都可以
    
    nxt_stat = CHK_BEGN;
    end
   
  endcase
 end
 
 
 //??Dèòa2ù×÷???′μ?×′ì???DD??êy
 always @(posedge clk,negedge rst_n) begin
  if(!rst_n) begin
   stat_cnt <= 0;
   end
  else if(stat_cnt_en == YES) begin
   if(dly_cnt1 == 1) begin
    stat_cnt <= stat_cnt + 1;
    end
   else ;
   end
  else stat_cnt <= 0;
 end
  
  
 
 //?óê±??3ì?é
 always @(posedge clk,negedge rst_n) begin
  if(!rst_n) begin
   dly_en1 <= NO;
   dly_ok <= NO;
   dly_cnt1 <= 0;     
   end
  else if(dly_en == YES) begin
   if((dly_cnt - 2)==0) begin
    dly_ok <= YES;
    dly_en1 <= NO;
    end
   else begin
    dly_cnt1 <= dly_cnt - 3;    //?óê±?ü?úêyDT?y
    dly_en1 <= YES;
    end
   end
  else if(dly_en1 == YES) begin
   if(dly_cnt1 == 0) begin
    dly_en1 <= NO;
    dly_ok <= YES;
    end
   else begin
    dly_cnt1 <= dly_cnt1 - 1;
    end
   end
  else dly_ok <= NO;          //??2ù×÷
 end//always
 
 endmodule
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 


点赞

评论 (0 个评论)

facelist

您需要登录后才可以评论 登录 | 注册

  • 关注TA
  • 加好友
  • 联系TA
  • 0

    周排名
  • 0

    月排名
  • 0

    总排名
  • 0

    关注
  • 1

    粉丝
  • 0

    好友
  • 0

    获赞
  • 0

    评论
  • 访问数
关闭

站长推荐 上一条 /2 下一条


小黑屋| 手机版| 关于我们| 联系我们| 在线咨询| 隐私声明| EETOP 创芯网
( 京ICP备:10050787号 京公网安备:11010502037710 )

GMT+8, 2024-11-23 12:14 , Processed in 0.026651 second(s), 17 queries , Gzip On, Redis On.

eetop公众号 创芯大讲堂 创芯人才网
返回顶部