FPGA project : sdram

sdram读写控制器

实验目标:

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

框图设计:

第一部分:sdram基本操作实的实现sdram_ctrl

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

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

如下:

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

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

FPGA project : sdram_第1张图片

FPGA project : sdram_第2张图片

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

FPGA project : sdram_第3张图片

 FPGA project : sdram_第4张图片

 FPGA project : sdram_第5张图片

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

整体:

 FPGA project : sdram_第6张图片

FPGA project : sdram_第7张图片 第二部分:sdram控制器sdram_top

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

FPGA project : sdram_第8张图片

 FPGA project : sdram_第9张图片

 FPGA project : sdram_第10张图片

 第三部分:uart_sdram。uart与sdram的数据交互与最顶层模块设计。

FPGA project : sdram_第11张图片

FPGA project : sdram_第12张图片

FPGA project : sdram_第13张图片 指令:

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结束后,完成初始化。

时序图:FPGA project : sdram_第14张图片
代码: 
// 初始化模块,产生控指令时序,预充电时序,刷新时序,模式配置时序。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

FPGA project : sdram_第15张图片

sdram_aref:

FPGA project : sdram_第16张图片

设计思路: 

分析时序图可知,刷新模块要做的是先传预充电时序,然后传刷新时序>=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记录刷新次数,要刷新两次才可以。

时序图:

FPGA project : sdram_第17张图片

 代码:

你可能感兴趣的:(野火征途pro,fpga开发)