FPGA project : sdram

本文围绕SDRAM读写控制器展开,目标是实现PC机通过串口向SDRAM读写数据并回传显示。介绍了框图设计,包括sdram_ctrl、sdram_top等模块;给出了各模块的指令;详细阐述代码设计,含sdram_init、sdram_aref等模块的设计思路、时序图、代码及仿真;最后进行了上板验证。

sdram读写控制器

实验目标:

设计并实现一个 SDRAM 数据读写控制器,使用 PC 机通过串口向 SDRAM 写入 10 字
节数据,并将写入的 10 字节数据读出,通过串口回传至 PC 机,在串口助手上位机上打印
显示回传数据。

框图设计:

第一部分:

sdram基本操作实的实现sdram_ctrl

要实现数据的读写,还要有初始化和刷新操作。所以该模块要有分别产生这四条指令的模块。

由于时序冲突问题,刷新,和读写指令存在优先级的问题。所以还应有仲裁模块。

如下:

指令产生模块应该有指令端口,bank地址端口,行列地址端口,结束标志信号。

刷新模块的请求信号传向仲裁模块,反馈给刷新模块一个使能信号。

 写指令模块的写数据,写地址,写突发长度。必不可少。

 

 

需要注意的是有几个输入端口是外界给的,有几个输出端口直接输出出去。

整体:

 

 第二部分:

sdram控制器sdram_top

fifo控制模块,实现sdram读写数据的跨时钟域处理。

 

 

 第三部分:

uart_sdram。uart与sdram的数据交互与最顶层模块设计。

 指令:

1,取消设备选择:1XXX;

2,无操作指令:0111;

3,配置模式指令:0000;+ addr A12~A0

4,预充电指令:0010;+ addr

5,自动刷新与自刷新指令:0001;

6,激活指令:0011;+addr/bank

7,写指令:0100;+bank/col;

8,读指令:0101;+bank/col;

明天完成初始化模块,加油!!!

这个sdram读写控制器工程确实挺大的。

代码设计:

sdram_ctrl:

sdram_init:

设计思路:

1 预充电指令,在写入0010指令+A10 == 1 之前,先等待200us,加载稳定时钟,并传递空操作指令。

2 写入预充电之后要等待TRP = 2  ,并传递空操作指令0111。

3 TRP时间结束后,写入自动刷新指令0001,并等待TRC = 7 ,并传递空操作指令。

4 TRC时间结束后,再次刷新指令。

5 TRC时间结束后,模式配置寄存器指令0000。+A11~A0;并等待TMRD = 时间,并传递空操作。

6 等待TMRD结束后,完成初始化。

时序图:
代码: 
// 初始化模块,产生控指令时序,预充电时序,刷新时序,模式配置时序。sys_clk == 100mHz
module sdram_init(
    input       wire            sys_clk     , 
    input       wire            sys_rst_n   ,

    output      reg     [3:0]   init_cmd    ,
    output      reg     [1:0]   init_ba     ,
    output      reg     [12:0]  init_addr   ,
    output      reg             init_end
);

    // parameter
    parameter   T_POWER   = 200_00       , // 等待时钟稳定。200us。
                T_RP      = 2            , // 2个时钟周期的预充电时间。
                T_RFC     = 7            , // 7个时钟周期的刷新时间。
                T_MRD     = 3            , // 3个时钟周期的模式配置时间。
                MAX_CNT_AR= 8            ; // 刷新次数。
    localparam  INIT_NOP  = 4'b0111      , // 空指令
                INIT_PREC = 4'b0010      , // 预充电
                INIT_REF  = 4'b0001      , // 自动刷新
                INIT_MOD  = 4'b0000      ; // 模式配置
    localparam  INIT_IDLE = 8'b0000_0001 ,
                INIT_PRE  = 8'b0000_0010 , // 预充电
                INIT_TRP  = 8'b0000_0100 ,
                INIT_AR   = 8'b0000_1000 , // 刷新
                INIT_TFR  = 8'b0001_0000 ,
                INIT_MRS  = 8'b0010_0000 , // 模式配置
                INIT_TMRD = 8'b0100_0000 ,
                INIT_ENDS = 8'b1000_0000 ; // 完成
    // reg signal define
    reg     [7:0]       state_c  ;
    reg     [7:0]       state_n  ;
    reg     [15:0]      cnt_init ;
    reg     [3:0]       cnt_ar   ; // 记录刷新次数,以产生状态跳转条件。
    // wire signal define
    wire                INIT_IDLEtoINIT_PRE ;
    wire                INIT_TRPtoINIT_AR   ;  
    wire                INIT_TFRtoINIT_MRS  ; 
    wire                INIT_TFRtoINIT_AR   ;
    wire                INIT_TMRDtoINIT_ENDS;
    /************************************************************************/
    // reg     [7:0]       state_c  ;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) 
            state_c <= INIT_IDLE ;
        else
            state_c <= state_n ;
    end
    // reg     [7:0]       state_n  ;
    always @(*) begin
        case(state_c)
        INIT_IDLE : if(INIT_IDLEtoINIT_PRE)
                        state_n = INIT_PRE ;
                    else 
                        state_n = INIT_IDLE ;
        INIT_PRE  : state_n = INIT_TRP ;
        INIT_TRP  : if(INIT_TRPtoINIT_AR)
                        state_n = INIT_AR ;
                    else 
                        state_n = INIT_TRP ;
        INIT_AR   : state_n = INIT_TFR ;
        INIT_TFR  : if(INIT_TFRtoINIT_MRS)
                        state_n = INIT_MRS ;
                    else if(INIT_TFRtoINIT_AR) // 没有达到规定的刷新次数,回到刷新指令。
                        state_n = INIT_AR ;
                    else 
                        state_n = INIT_TFR ;
        INIT_MRS  : state_n = INIT_TMRD ;
        INIT_TMRD : if(INIT_TMRDtoINIT_ENDS)
                        state_n = INIT_ENDS ;
                    else 
                        state_n = INIT_TMRD ;
        INIT_ENDS : state_n = INIT_ENDS ; // 保持在此状态。
        default   : state_n = INIT_IDLE ;
        endcase
    end
    assign INIT_IDLEtoINIT_PRE  = (state_c == INIT_IDLE) && (cnt_init == T_POWER - 1) ; 
    assign INIT_TRPtoINIT_AR    = (state_c == INIT_TRP ) && (cnt_init == T_RP    - 1) ;
    assign INIT_TFRtoINIT_MRS   = (state_c == INIT_TFR ) && ((cnt_ar == MAX_CNT_AR - 1) && (cnt_init == T_RFC - 1)) ;
    assign INIT_TFRtoINIT_AR    = (state_c == INIT_TFR ) && ((cnt_ar != MAX_CNT_AR - 1) && (cnt_init == T_RFC - 1)) ;
    assign INIT_TMRDtoINIT_ENDS = (state_c == INIT_TMRD) && (cnt_init == T_MRD    - 1) ;
    // reg     [15:0]       cnt_init ;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) 
            cnt_init <= 16'd0 ;
        else 
        case (state_c)
        INIT_IDLE : if(cnt_init == T_POWER - 1)
                        cnt_init <= 16'd0 ;
                    else 
                        cnt_init <= cnt_init + 1'b1 ;
        INIT_PRE  : cnt_init <= 16'd0 ;
        INIT_TRP  : if(cnt_init == T_RP    - 1)
                        cnt_init <= 16'd0 ;
                    else 
                        cnt_init <= cnt_init + 1'b1 ;
        INIT_AR   : cnt_init <= 16'd0 ;
        INIT_TFR  : if(cnt_init == T_RFC - 1)
                        cnt_init <= 16'd0 ;
                    else 
                        cnt_init <= cnt_init + 1'b1 ;
        INIT_MRS  : cnt_init <= 16'd0 ;
        INIT_TMRD : if(cnt_init == T_MRD    - 1)
                        cnt_init <= 16'd0 ;
                    else 
                        cnt_init <= cnt_init + 1'b1 ;
        INIT_ENDS : cnt_init <= 16'd0 ;
        default   : cnt_init <= 16'd0 ;
        endcase
    end
    // reg     [3:0]       cnt_ar   ;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) 
            cnt_ar <= 4'd0 ;
        else if((state_c == INIT_TFR) && (cnt_init == T_RFC - 1) && (cnt_ar == MAX_CNT_AR - 1))
            cnt_ar <= 4'd0 ;
        else if((state_c == INIT_TFR) && (cnt_init == T_RFC - 1))
            cnt_ar <= cnt_ar + 1'b1 ;
        else
            cnt_ar <= cnt_ar ;  
    end
    // reg     [3:0]   init_cmd    ,
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) 
            init_cmd <= INIT_NOP ;
        else 
        case (state_c)
        INIT_IDLE : init_cmd <= INIT_NOP ;
        INIT_PRE  : init_cmd <= INIT_PREC;
        INIT_TRP  : init_cmd <= INIT_NOP ;
        INIT_AR   : init_cmd <= INIT_REF ;
        INIT_TFR  : init_cmd <= INIT_NOP ;
        INIT_MRS  : init_cmd <= INIT_MOD ;
        INIT_TMRD : init_cmd <= INIT_NOP ;
        INIT_ENDS : init_cmd <= INIT_NOP ;
        default   : init_cmd <= INIT_NOP ;
        endcase
    end
    // reg     [1:0]   init_ba     ,
    // reg     [12:0]  init_addr   ,
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) begin
            init_ba <= 2'b11 ;
            init_addr <= 13'h1FFF ;
        end
        else if(state_c == INIT_MRS) begin
            init_ba <= 2'b00 ;
            init_addr <= 13'b000_0_00_011_0_111 ;
        end
        else begin
            init_ba <= 2'b11 ;
            init_addr <= 13'h1FFF ;
        end
    end
    // reg             init_end
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) 
            init_end <= 1'b0 ;
        else if(state_c == INIT_ENDS)
            init_end <= 1'b1 ;
        else 
            init_end <= init_end ;
    end
endmodule
仿真: 
`timescale 1ns/1ns
module test_sdram_init ();
    reg              sys_clk     ; // 100mHz
    reg              sys_rst_n   ;

    wire     [3:0]   init_cmd    ;
    wire     [1:0]   init_ba     ;
    wire     [12:0]  init_addr   ;
    wire             init_end    ;

sdram_init sdram_init_inst(
    .sys_clk    ( sys_clk  ) , 
    .sys_rst_n  ( sys_rst_n) ,

    .init_cmd   ( init_cmd ) ,
    .init_ba    ( init_ba  ) ,
    .init_addr  ( init_addr) ,
    .init_end   ( init_end )
);

    parameter CYCLE = 10 ; // 10ns
    initial begin
        sys_clk = 1'b1 ;
        sys_rst_n <= 1'b0 ;
        #(CYCLE) ;
        sys_rst_n <= 1'b1 ;
        #(CYCLE * 250_00) ;
        $stop ;
    end
    always #(CYCLE / 2) sys_clk = ~sys_clk ;
endmodule

sdram_aref:

设计思路: 

分析时序图可知,刷新模块要做的是先传预充电时序,然后传刷新时序>=2次。简化版的初始化。

自动刷新周期为7.5us。设参数MAX_CNT_REF = 750.

在初始化结束信号拉高后,cnt_750us开始计数,计满后aref_req拉高,等待仲裁模块给出aref_en拉高,开始进行刷新状态机跳转。当跳转到预充电命令时,说明已经开始刷新了,aref_req拉低。

根据时序,绘制状态跳转。

当进入结束状态时,发出一个周期的aref_end==1信号,仲裁模块把aref_en拉低。完成一次刷新。

cnt_750us计数器是在init_end==1后循环计数的。

cnt_ar记录刷新次数,要刷新两次才可以。

时序图:

 代码:
// 模块说明:刷新模块,在初始化完成后,以750us为周期进行刷新时序的输出(要受仲裁模块控制),但刷新周期不会大于750us。
module sdram_aref(
    input       wire                sys_clk     ,
    input       wire                sys_rst_n   ,
    input       wire                init_end    ,
    input       wire                aref_en     ,

    output      reg                 aref_req    ,
    output      reg     [3:0]       aref_cmd    ,
    output      wire    [1:0]       aref_ba     ,
    output      wire    [12:0]      aref_addr   ,
    output      wire                aref_end    
);
    // parameter    
    parameter   MAX_CNT_750US   = 750        ;
    // localparam
    localparam  T_RP            = 2          , // 2个时钟周期的预充电时间。
                T_RFC           = 7          , // 7个时钟周期的刷新时间。
                MAX_CNT_AR      = 2          , // 刷新次数。
                AREF_NOP        = 4'b0111    , // 空指令
                AREF_PREC       = 4'b0010    , // 预充电
                AREF_REFC       = 4'b0001    ; // 自动刷新,指令与状态机命名冲突了,后面加个C。

    localparam  AREF_IDLE       = 6'b00_0001 ,
                AREF_PCHA       = 6'b00_0010 ,
                AREF_TRP        = 6'b00_0100 ,
                AREF_REF        = 6'b00_1000 ,
                AREF_TRF        = 6'b01_0000 ,
                AREF_END        = 6'b10_0000 ;
    // reg signal define
    reg     [5:0]       state_c   ;
    reg     [5:0]       state_n   ;
    reg     [9:0]       cnt_750us ;
    reg     [3:0]       cnt_aref  ;
    reg                 cnt_ar    ;
    // wire signal define 
    wire                AREF_IDLEtoAREF_PCHA;    
    wire                AREF_TRPtoAREF_REF  ;      
    wire                AREF_TRFtoAREF_REF  ;      
    wire                AREF_TRFtoAREF_END  ;      
    /*************************************************************/
    // reg     [5:0]       state_c   ;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) 
            state_c <= AREF_IDLE ;
        else 
            state_c <= state_n ;
    end
    // reg     [5:0]       state_n   ;
    always @(*) begin
        case(state_c)
        AREF_IDLE : if(AREF_IDLEtoAREF_PCHA)
                        state_n = AREF_PCHA ;
                    else 
                        state_n = AREF_IDLE ;
        AREF_PCHA : state_n = AREF_TRP ;
        AREF_TRP  : if(AREF_TRPtoAREF_REF)
                        state_n = AREF_REF ;
                    else 
                        state_n = AREF_TRP ;
        AREF_REF  : state_n = AREF_TRF ;
        AREF_TRF  : if(AREF_TRFtoAREF_END)
                        state_n = AREF_END ;
                    else if(AREF_TRFtoAREF_REF)
                        state_n = AREF_REF ;
                    else 
                        state_n = AREF_TRF ;
        AREF_END  : state_n = AREF_IDLE ;
        default   : state_n = AREF_IDLE ;
        endcase
    end
    assign  AREF_IDLEtoAREF_PCHA    = (state_c == AREF_IDLE) && ( aref_en  ) ; 
    assign  AREF_TRPtoAREF_REF      = (state_c == AREF_TRP ) && ( cnt_aref ) ; 
    assign  AREF_TRFtoAREF_REF      = (state_c == AREF_TRF ) && ( cnt_aref == 6 && ~cnt_ar ) ; 
    assign  AREF_TRFtoAREF_END      = (state_c == AREF_TRF ) && ( cnt_aref == 6 &&  cnt_ar ) ; 
    // reg     [9:0]       cnt_750us ;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) 
            cnt_750us <= 10'd0 ;
        else if(init_end) 
            begin 
                if(cnt_750us == MAX_CNT_750US - 1)
                    cnt_750us <= 10'd0 ;
                else 
                    cnt_750us <= cnt_750us + 1'b1 ;
            end
            else
                cnt_750us <= 10'd0 ;
    end
    // reg     [3:0]       cnt_aref  ;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) 
            cnt_aref <= 4'd0 ;
        else
        case (state_c)
        AREF_IDLE : cnt_aref <= 4'd0 ;
        AREF_PCHA : cnt_aref <= 4'd0 ;
        AREF_TRP  : if(cnt_aref == T_RP -1)
                        cnt_aref <= 4'd0 ;
                    else    
                        cnt_aref <= cnt_aref + 1'b1 ;
        AREF_REF  : cnt_aref <= 4'd0 ;
        AREF_TRF  : if(cnt_aref == T_RFC - 1)
                        cnt_aref <= 4'd0 ;
                    else    
                        cnt_aref <= cnt_aref + 1'b1 ;
        AREF_END  : cnt_aref <= 4'd0 ;
        default   : cnt_aref <= 4'd0 ;
        endcase
    end
    // reg                 cnt_ar    ;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) 
            cnt_ar <= 1'b0 ;
        else if(state_c == AREF_TRF && cnt_aref == T_RFC - 1) // 由于是一位宽的,就不用写归零情况了。
            cnt_ar <= cnt_ar + 1'b1 ;
        else 
            cnt_ar <= cnt_ar ;
    end
    // output signal describe
    // reg                 aref_req  ;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) 
            aref_req <= 1'b0 ;
        else if(cnt_750us == MAX_CNT_750US - 1)
            aref_req <= 1'b1 ;
        else if(state_c == AREF_PCHA)
            aref_req <= 1'b0 ;
        else 
            aref_req <= aref_req ;
    end
    // reg     [3:0]       aref_cmd  ;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) 
            aref_cmd <= 4'd0 ;
        else
        case (state_c)
        AREF_IDLE : aref_cmd <= AREF_NOP ;
        AREF_PCHA : aref_cmd <= AREF_PREC;
        AREF_TRP  : aref_cmd <= AREF_NOP ;
        AREF_REF  : aref_cmd <= AREF_REFC;
        AREF_TRF  : aref_cmd <= AREF_NOP ;
        AREF_END  : aref_cmd <= AREF_NOP ;
        default   : aref_cmd <= AREF_NOP ;
        endcase
    end
    // wire     [1:0]       aref_ba   ;
    assign  aref_ba = 2'b11 ;
    // reg     [12:0]      aref_addr ;
    assign  aref_addr = 13'h1FFF ;
    // wire                aref_end  ;
    assign  aref_end = (state_c == AREF_END) ? 1'b1 : 1'b0 ;
endmodule
仿真:

在仿真代码中模拟了一小部分仲裁。

`timescale 1ns/1ns
module test_sdram_aref ();
    reg             sys_clk     ; // 50mHz
    reg             sys_rst_n   ;

    reg             aref_en     ; // 自刷新模块的输入,应该是仲裁模块传出。

    wire    [3:0]   init_cmd    ; // 初始化模块的输出。
    wire    [1:0]   init_ba     ;
    wire    [12:0]  init_addr   ;
    wire            init_end    ;

    wire            aref_req    ; // 自刷新模块输出
    wire    [3:0]   aref_cmd    ; 
    wire    [1:0]   aref_ba     ;  
    wire    [12:0]  aref_addr   ;
    wire            aref_end    ; 

    wire    [3:0]   sdram_cmd   ; 
    wire    [1:0]   sdram_ba    ;  
    wire    [12:0]  sdram_addr  ;
    assign          sdram_cmd   = (init_end) ? aref_cmd  : init_cmd  ; // 经过仲裁后的命令与地址。
    assign          sdram_ba    = (init_end) ? aref_ba   : init_ba   ;  
    assign          sdram_addr  = (init_end) ? aref_addr : init_addr ;

    wire            c0_sig      ; // pll的输出。
    wire            c1_sig      ;
    wire            c2_sig      ;
    wire            locked_sig  ;
    wire            rst_n       ; // 全局复位。
    assign          rst_n = sys_rst_n && (locked_sig) ;
sdram_aref sdram_aref_inst(
    .sys_clk                            ( c1_sig     ) ,
    .sys_rst_n                          ( rst_n      ) ,
    .init_end                           ( init_end   ) ,
    .aref_en                            ( aref_en    ) ,
  
    .aref_req                           ( aref_req   ) ,
    .aref_cmd                           ( aref_cmd   ) ,
    .aref_ba                            ( aref_ba    ) ,
    .aref_addr                          ( aref_addr  ) ,
    .aref_end                           ( aref_end   ) 
);

clk_gen	clk_gen_inst (
	.areset                             (~sys_rst_n  ) ,
	.inclk0                             ( sys_clk    ) ,

	.c0                                 ( c0_sig     ) , // 50
	.c1                                 ( c1_sig     ) , // 100
	.c2                                 ( c2_sig     ) , // 100+shift30
	.locked                             ( locked_sig )
);

sdram_model_plus sdram_model_plus_inst(
    .Dq                                 (  ) , 
    .Addr                               ( sdram_addr  ) , 
    .Ba                                 ( sdram_ba    ) , 
    .Clk                                ( c2_sig      ) , 
    .Cke                                ( 1'b1        ) , 
    .Cs_n                               ( sdram_cmd[3]) , 
    .Ras_n                              ( sdram_cmd[2]) , 
    .Cas_n                              ( sdram_cmd[1]) , 
    .We_n                               ( sdram_cmd[0]) , 
    .Dqm                                ( 2'b00       ) ,
    .Debug                              ( 1'b1        )  
);

sdram_init sdram_init_inst(
    .sys_clk                            ( c1_sig   ) , 
    .sys_rst_n                          ( rst_n    ) ,

    .init_cmd                           ( init_cmd ) ,
    .init_ba                            ( init_ba  ) ,
    .init_addr                          ( init_addr) ,
    .init_end                           ( init_end )
);

    parameter   CYCLE = 20 ; // 10ns
    defparam    sdram_model_plus_inst.addr_bits = 13 ;
    defparam    sdram_model_plus_inst.data_bits = 16 ;
    defparam    sdram_model_plus_inst.col_bits  = 9  ;
    defparam    sdram_model_plus_inst.mem_sizes = 2*1024*1024  ;
    // aref_en 仲裁
    always @(posedge c1_sig or negedge sys_rst_n) begin
        if(~sys_rst_n)
            aref_en <= 1'b0 ;
        else if(init_end && aref_req)
            aref_en <= 1'b1 ;
        else if(aref_end)
            aref_en <= 1'b0 ;
        else 
            aref_en <= aref_en ;
    end

    initial begin
        sys_clk = 1'b1    ;
        sys_rst_n <= 1'b0 ;
        #(CYCLE)          ;
        sys_rst_n <= 1'b1 ;
        #(CYCLE * 250_00) ;
    end
    always #(CYCLE / 2) sys_clk = ~sys_clk ;
endmodule

 sdram_write:

设计思路:

&n

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值