新闻  |   论坛  |   博客  |   在线研讨会
基于FPGA的SDRAM控制器的设计_SDRAM设计源码_明德扬资料
billow兔 | 2017-08-03 09:09:58    阅读:595   发布文章

至简设计法纯逻辑实现SDARM控制器

1、设计目的:本项目展示如何用“至简设计法”设计SDARM,具体功能要求如下。

1)读写仲裁机制是:当如果同时出现读写请求时,如果上次执行了读操作,则此次执行写操作;如果上一次执行了写操作,则此次执行读操作。如果不是同时出现读写请求,则是什么请求就执行什么操作。

2)采用全页模式的读写操作,该模式在读、写完成时,需要给出预充电命令才能结束。

3)刷新请求始终优于读、写请求。

DDR的时序与SDRAM是相似的,学好SDRAM后,理解DDR2和DDR3就非常容易了。

2、至简设计代码实现(附录部分代码)

下面是使用至简设计法实现的SDRAM控制器,该控制器使用了四段式状态机,其他信号根据状态机对齐而设计,结构相当清晰,无论是设计还是调试,都非常容易,相信有一定基础的工程师,能感觉到这样设计的精简、奇妙之处,欢迎借鉴、学习。

网络上亦有相当多的SDRAM实现代码,欢迎您拿来与明德扬对比,我们对自身的代码设计就是有这样的自信。

 

//四段式状态机

 

//第一段:同步时序always模块,格式化描述次态寄存器迁移到现态寄存器

always@(posedge clk or negedge rst_n)begin

    if(!rst_n)begin

        state_c <= IDL;

    end

    else begin

        state_c <= state_n;

    end

end

 

//第二段:组合逻辑always模块,描述状态转移条件判断

//只需要思考转移到哪里去

always@(*)begin

    case(state_c)

        NOP:begin

            if(nop2pre_start)begin

                state_n = PRE;

            end

            else begin

                state_n = state_c;

            end

        end

        PRE:begin

            if(pre2aut_start)begin

                state_n = AUT;

            end

            else if(pre2idl_start)begin

                 state_n = IDL;

             end

 

            else begin

                state_n = state_c;

            end

        end

        AUT:begin

            if(aut2aut_start)begin

                state_n = AUT;

            end

            else if(aut2lmd_start)begin

                state_n = LMD;

            end

            else if(aut2idl_start)begin

                state_n = IDL;

            end

        end

        LMD:begin

            if(lmd2idl_start)begin

                state_n = IDL;

            end

            else begin

                state_n = state_c;

            end

         end

         IDL:begin

             if(idl2act_start)begin

                 state_n = ACT;

             end

             else if(idl2aut_start)begin

                 state_n = AUT;

             end

 

            else begin

                state_n = state_c;

            end

        end

        ACT:begin

             if(act2red_start)begin

                 state_n = RED;

             end

             else if(act2wrt_start)begin

                 state_n = WRT;

             end

 

            else begin

                state_n = state_c;

            end

        end

        RED:begin

            if(red2pre_start)begin

                state_n = PRE;

            end

            else begin

                state_n = state_c;

            end

        end

        WRT:begin

            if(wrt2pre_start)begin

                state_n = PRE;

            end

            else begin

                state_n = state_c;

            end

        end

        default:begin

            state_n = IDL;

        end

    endcase

end

//第三段:设计转移条件

//相同现态的要放在一起,条件互斥,方便比较

assign nop2pre_start = state_c==NOP && end_cnt;

 

assign pre2aut_start = state_c==PRE && init_flag==1 && end_cnt;

assign pre2idl_start = state_c==PRE && init_flag==0 && end_cnt;

 

assign aut2aut_start = state_c==AUT && init_flag==1 && init_auto_flag==1 && end_cnt;

assign aut2lmd_start = state_c==AUT && init_flag==1 && init_auto_flag==0 && end_cnt;

 

assign aut2idl_start = state_c==AUT && init_flag==0 && end_cnt;

 

assign lmd2idl_start = state_c==LMD && end_cnt;

 

assign idl2act_start = state_c==IDL && ref_req==0&& (wr_req||rd_req);

assign idl2aut_start = state_c==IDL && ref_req ==1 ; 

 

assign act2red_start = state_c==ACT && rd_flag==1 && end_cnt;

assign act2wrt_start = state_c==ACT && rd_flag==0 && end_cnt;

 

assign red2pre_start = state_c==RED && end_cnt;

assign wrt2pre_start = state_c==WRT && end_cnt;

 

always  @(posedge clk or negedge rst_n)begin

    if(rst_n==1'b0)begin

        init_flag <= 1;

    end

    else if(lmd2idl_start)begin

        init_flag <= 0;

    end

end

 

always  @(posedge clk or negedge rst_n)begin                

     if(rst_n==1'b0)begin

         init_auto_flag <= 1;

     end

     else if(aut2aut_start)begin

         init_auto_flag <= 0;

     end

end

 

//刷新请求

always  @(posedge clk or negedge rst_n)begin

    if(rst_n==1'b0)begin

        ref_req <= 0;

    end

    else if(end_cnt_self)begin

        ref_req <= 1;

    end

    else if(idl2aut_start)begin

        ref_req <= 0;

    end

end

 

//写响应

assign wr_ack = idl2act_start==1&& (wr_req==1&&((rd_flag==1)||(rd_flag==0&&rd_req==0)));

//读响应

assign rd_ack = idl2act_start==1&& (rd_req==1&&((rd_flag==0)||(rd_flag==1&&wr_req==0)));

 

always  @(posedge clk or negedge rst_n)begin

    if(rst_n==1'b0)begin

        rd_flag <= 0;

    end

    else if(rd_flag==0&&rd_ack==1)begin

        rd_flag <= 1;

    end

    else if(rd_flag==1&&wr_ack==1)begin

        rd_flag <= 0;

    end

end

 

 

//刷新时间计算

always @(posedge clk or negedge rst_n)begin

    if(!rst_n)begin

        cnt_self <= 0;

    end

    else if(add_cnt_self)begin

        if(end_cnt_self)

            cnt_self <= 0;

        else

            cnt_self <= cnt_self + 1;

    end

end

 

assign add_cnt_self = init_flag==0;      

assign end_cnt_self = add_cnt_self && cnt_self==1562-1 ;    

 

//命令时间计数,此处使用了变量法,并且复用了一个计数器

always @(posedge clk or negedge rst_n)begin

    if(!rst_n)begin

        cnt <= 0;

    end

    else if(add_cnt)begin

        if(end_cnt)

            cnt <= 0;

        else

            cnt <= cnt + 1;

    end

end

 

assign add_cnt = state_c!=IDL;      

assign end_cnt = add_cnt && cnt==x-1 ;     

 

//各个命令所需要的时间,在下面列出来就好了

always  @(*)begin

      if(state_c==NOP)begin

          x =  INITIATE;

      end

      else if(state_c==PRE)begin

          x =  TRP;

      end

      else if(state_c==AUT)begin

          x = TRC;

      end

      else if(state_c==LMD)begin

          x = TMRD;

      end

      else if(state_c==ACT)begin

          x = TRCD ;

      end

      else begin

          x = 256 ;

      end

end

 

always  @(posedge clk or negedge rst_n)begin

    if(rst_n==1'b0)begin

        cke <= 1;

    end

    else begin

        cke <= 1;

    end

end

 

assign command = {cs,ras,cas,we};

 

//下面是产生命令的代码,由于状态机结构简单,要产生COMMAND是非常方便的

//仅从名字下就可以看出代码是否正确

always  @(posedge clk or negedge rst_n)begin

    if(rst_n==1'b0)begin

        command <= 0;

    end

    else if(nop2pre_start||wrt2pre_start||red2pre_start)begin

        command <= precharge;

    end

    else if(pre2aut_start||aut2aut_start||idl2aut_start)begin

        command <= autorefresh;

    end

    else if(aut2lmd_start)begin

        command <= loadmode;

    end

    else if(idl2act_start)begin

        command <= active;

    end

    else if(act2red_start)begin

        command <= read;

    end

    else if(act2wrt_start)begin

        command <= write;

    end

    else begin

        command <= nop;

    end

 

end

 

always  @(posedge clk or negedge rst_n)begin

    if(rst_n==1'b0)begin

        dqm <= 0;

    end

    else if(init_flag==1)begin       

        dqm <= 2'b11;

    end

    else begin

        dqm <= 0;

    end

end

 

always  @(posedge clk or negedge rst_n)begin

    if(rst_n==1'b0)begin

        dq_out <= 0;

    end

    else begin

        dq_out <= wdata;

    end

end

 

always  @(posedge clk or negedge rst_n)begin

    if(rst_n==1'b0)begin

        dq_out_en <= 0;

    end

    else if(act2wrt_start)begin

        dq_out_en <= 1;

    end

    else if(wrt2pre_start)begin

        dq_out_en <= 0;

    end

end

 

 

always  @(posedge clk or negedge rst_n)begin             //256拍

    if(rst_n==1'b0)begin

        rdata_flag <= 0;

    end

    else if(act2red_start)begin

        rdata_flag <= 1;

    end

    else if(red2pre_start)begin

        rdata_flag <= 0;

    end

end

 

always  @(posedge clk or negedge rst_n)begin

    if(rst_n==1'b0)begin

        rdata_flag_ff0 <= 0;

        rdata_flag_ff1 <= 0;

        rdata_flag_ff2 <= 0;

    end

    else begin

        rdata_flag_ff0 <= rdata_flag;

        rdata_flag_ff1 <= rdata_flag_ff0;

        rdata_flag_ff2 <= rdata_flag_ff1;

   

    end

end

 

 

always  @(posedge clk or negedge rst_n)begin

    if(rst_n==1'b0)begin

        rdata <= 0;

    end

    else begin

        rdata <= dq_in;

    end

 

end

 

always  @(posedge clk or negedge rst_n)begin

    if(rst_n==1'b0)begin

        rdata_vld <= 0;

    end

    else begin

        rdata_vld <= rd_flag_ff2;

    end

end

 

always  @(posedge clk or negedge rst_n)begin   //锁存地址waddr;

    if(rst_n==1'b0)begin

        waddr_ff0 <= 0;

    end

    else if(idl2act_start)begin

        waddr_ff0 <= waddr;

    end

end

 

always  @(posedge clk or negedge rst_n)begin

    if(rst_n==1'b0)begin

        addr <= 0;

    end

    else if(nop2pre_start)begin

        addr <=12'b0100_0000_0000;

    end

    else if(aut2lmd_start)begin

        addr <= 12'b00_1_00_010_0_111;//latency Mode 为010 的情况

    end

    else if(idl2act_start)begin

        addr[7:0] <=  waddr[19:8];    //行地址

    end

    else if(act2wrt_start||act2red_start)begin

        addr[7:0] <=  waddr_ff0[7:0];     //列地址

    end

end

 

always  @(posedge clk or negedge rst_n)begin

    if(rst_n==1'b0)begin

        bank <=0;

    end

    else if(nop2pre_start)begin

        bank <= 2'b11;

    end

    else if(idl2act_start)begin

        bank <= waddr[21:20];

    end

    else if(act2red_start||act2wrt_start)begin

        bank < waddr_ff0[21:20];

    end

    else begin

        bank <= 0;

    end

end

 

 

 

 


*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。

参与讨论
登录后参与讨论
推荐文章
最近访客