请选择 进入手机版 | 继续访问电脑版

明德扬论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

微信扫一扫,快捷登录!

查看: 4744|回复: 0

【原创分享】基于FPGA的SDRAM控制器设计 —自动刷新设计

[复制链接]
发表于 2020-3-20 11:51:56 | 显示全部楼层 |阅读模式

马上注册,看完整文章,学更多FPGA知识。

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
基于FPGASDRAM控制器设计(原创分享)
—自动刷新设计   
作者:小周
本文为明德扬原创及录用文章,转载请注明出处

     SDRAM控制器设计的主要功能是能对SDRAM进行读写操作,本工程实现了SDRAM的初始化和自动刷新两个功能。

   初始化功能在前一章的分享中已经进行了比较详细的描述,感兴趣的同学可以搜索学习下,这里不再赘述。今天我们主要讨论SDRAM的自动刷新的功能以及实现。

1.1 原理功能1.1.1 为什么刷新
    我们都知道SDRAM是使用电容保存信息的,随着使用时间的增长,电容的电量会有损失,因此在操作SDRAM时要进行刷新。SDRAM的刷新分为两种,分别是Auto Refresh和Self Refresh本次实验采用的是Auto Refresh

1.1.2 刷新间隔
      查询器件手册得到(64ms, 8192-cycle (commercial and industrial)),对8192行全部进行一次刷新时间是64ms。一次刷新操作是对4bank的同一行进行刷新,所以一次刷新间隔是64ms/8192=7.813us。但是当刷新时间到来时,SDRAM可能正在进行读写,那么需要本次读突发或者写突发完成之后才能进行刷新操作;那么一次读突发为7拍,写突发为6拍,时间是60ns或者70ns SDRAM工作时钟是100MHz1拍是10ns),同时考虑到读写命令都是仲裁模块发出,会有一定的延时,所以本次实验刷新间隔设为7.5us,留出足够的时间。


1.1.3 刷新时序
80.png

      刷新时序如上图所示。这里需要注意,此时序图发了两次Auto Refresh命令这种被称为背靠背技术;但其实背靠背技术并不是必须的,可以只发一次命令。

1.2 FPGA实现

1.2.1 模块架构
81.png



1.2.2 信号说明
信号
说明
clk
刷新模块工作时钟(100MHz
rst_n
复位信号
ref_en
刷新使能信号,由仲裁模块发出
ref_done
刷新完成信号
ref_bus
刷新数据总线,由SDRAM信号组成
rt_flag
计数到最大值信号
rt_clear
rt_flag清除信号
rt_en
计数器使能信号
init_done
初始化完成信号
sel_sm
选择SDRAM输出信号
ref_en
刷新使能信号
sdr_bus
顶层模块数据总线
sdr_clk
SDRAM工作时钟
sdr_cke
时钟使能
sdr_cs_n
片选信号
sdr_cas_n
行选通
sdr_ras_n
列选通
sdr_we_n
写使能
sdr_ba
bank地址
sdr_a
SDRAM地址总线



1.2.3
顶层模块参考代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
module sdram_top(
    clk    ,
    sys_rst_n  ,
    //其它信号,举例dout
    local_addr,
    local_data,
    local_q,
    local_rdreq,
    local_wrreq,
    local_reday,
    local_rdata_vaild,
    init_done,
    sdr_cke,
    sdr_cs_n,
    sdr_ras_n,
    sdr_cas_n,
    sdr_we_n,
    sdr_ba,
    sdr_a,
    sdr_dq,
    sdr_dqm,
    sdr_clk
    );

        input clk;
        input sys_rst_n;
        input [24:0] local_addr;
        input [63:0] local_data;
        output [63:0] local_q;
        input local_rdreq;
        input local_wrreq;
        output local_reday;
        output local_rdata_vaild;
        output init_done;
        output sdr_cke;
        output sdr_cs_n;
        output sdr_ras_n;
        output sdr_cas_n;
        output sdr_we_n;
        output [1:0] sdr_ba;
        output [12:0] sdr_a;
        output [15:0] sdr_dq;
        output [1:0] sdr_dqm;
        output sdr_clk;

        wire phy_clk;
        wire rst_n;
        wire rt_flag;
        wire rt_clear;
        wire rt_en;
        wire ref_en;
        wire ref_done;
        wire sel_sm;
        wire [19:0] sdr_bus;
        wire [19:0] init_bus;
        wire [19:0] ref_bus;

        assign {sdr_cke, sdr_cs_n, sdr_ras_n, sdr_cas_n, sdr_we_n, sdr_ba, sdr_a} = sdr_bus;
        assign sdr_dqm = 2'b00;

    sdram_init sdram_init_inst(
        .clk            (phy_clk)       ,
        .rst_n          (rst_n)       ,
        //其它信号,举例dout
        .init_done      (init_done)       ,
        .init_bus       (init_bus)
    );

        arbitrate arbitrate_inst(
                .clk(phy_clk),
                .rst_n(rst_n),
                .rt_en(rt_en),
                .rt_flag(rt_flag),
                .init_done(init_done),
                .ref_done(ref_done),
                .ref_en(ref_en),
                .sel_sm(sel_sm),
                .rt_clear(rt_clear)
        );

        ref_timer ref_timer_inst(
                .clk(phy_clk),
                .rst_n(rst_n),
                .rt_en(rt_en),
                .rt_clear(rt_clear),
                .rt_flag(rt_flag)
        );

        sdram_ref sdram_ref_inst(
                .clk(phy_clk),
                .rst_n(rst_n),
                .ref_en(ref_en),
                .ref_done(ref_done),
                .ref_bus(ref_bus)
        );

        sdram_mux sdram_mux_inst(
                .clk(phy_clk),
                .rst_n(rst_n),
                .init_bus(init_bus),
                .ref_bus(ref_bus),
                .sdr_bus(sdr_bus),
                .sel_sm(sel_sm)
        );

        my_pll PLL(
                .areset                (~sys_rst_n)                ,
                .inclk0                (clk)                                ,
                .c0                        (phy_clk)                        ,
                .c1                        (sdr_clk)                        ,
                .locked                (rst_n)
        );

endmodule



1.2.4 模块功能Ø PLL模块
    my_pll模块产生SDRAM和控制器工作时钟。

    输入的50M时钟,经过PLL模块后,会产生两个100M、相位相差180度的时钟。其中一个用于输出给外部SDRAM,另一个用于其它模块的工作时钟。关于此模块的原理,可以参考《基于FPGASDRAM控制器设计—初始化设计》中的“SDRAM中心对齐原则”部分进行学习。
另外,本模块锁定输入时钟后,将产生LOCK指示信号,此信号用于其它模块的复位信号。我们可以理解为,在时钟稳定之前,其它模块都处于复位状态。


Ø 仲裁模块
    arbitrate即仲裁模块,因为SDRAM控制时可能进行刷新或者读写操作(后续介绍),但是刷新时不能进行读写操作,因此需要一个仲裁模块,对这些控制命令进行管理,使刷新命令优先级最高。

    当初始化完成之后仲裁模块发出rt_en信号,当仲裁模块收刷新定时器计时到最大值时的标志信号rt_flag后,发出刷新使能信号ref_en,并发出rt_clear信号。其代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
module arbitrate(clk, rst_n, rt_en, rt_flag, init_done, ref_done, ref_en, sel_sm, rt_clear);

        input clk;
        input rst_n;
        input rt_flag;
        input init_done;
        input ref_done;
        output reg rt_en;
        output reg ref_en;
        output reg sel_sm;
        output reg rt_clear;

        localparam SM_INIT = 1'b0;
        localparam SM_REF  = 1'b1;

        always @(posedge clk or negedge rst_n)begin
                if(!rst_n)begin
                        rt_en <= 1'b0;
                end
                else if(init_done)begin
                        rt_en <= 1'b1;
                end
                else begin
                        rt_en <= rt_en;
                end
        end

        always @(posedge clk or negedge rst_n)begin
                if(!rst_n)begin
                        ref_en <= 1'b0;
                end
                else if(rt_flag)begin
                        ref_en <= 1'b1;
                end
                else if(ref_done)begin
                        ref_en <= 1'b0;
                end
                else begin
                        ref_en <= ref_en;
                end
        end

        always @(posedge clk or negedge rst_n)begin
                if(!rst_n)begin
                        sel_sm <= SM_INIT;
                end
                else if(init_done)begin
                        sel_sm <= SM_REF;
                end
                else begin
                        sel_sm <= sel_sm;
                end
        end

        always @(posedge clk or negedge rst_n)begin
                if(!rst_n)begin
                        rt_clear <= 1'b0;
                end
                else if(rt_flag)begin
                        rt_clear <= 1'b1;
                end
                else begin
                        rt_clear <= 1'b0;
                end
        end

endmodule







&#216;
刷新定时器模块

    ref_timer即刷新定时器模块,主要是计数刷新间隔时间,当计数到最大值时拉高rt_flag信号。当收到rt_clear信号时将rt_flag信号拉低。代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
module ref_timer(clk, rst_n, rt_en, rt_clear, rt_flag);

        input clk;
        input rst_n;
        input rt_en;
        input rt_clear;
        output reg rt_flag;

        parameter CNT_MAX = 750;

        reg [9:0] cnt;

        wire add_cnt;
        wire end_cnt;

    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 = rt_en;      
    assign end_cnt = add_cnt && cnt==CNT_MAX - 1 ;

        always @(posedge clk or negedge rst_n)begin
                if (!rst_n)
                        rt_flag <= 0;
                else if (add_cnt && cnt == CNT_MAX - 1)
                        rt_flag <= 1;
                else if (rt_clear)
                        rt_flag <= 0;
                else        
                        rt_flag <= rt_flag;
        end

endmodule





&#216;
初始化模块

    sdr_init初始化模块,在《基于FPGASDRAM控制器设计—初始化设计》中我们有比较详细的介绍,可以认真学习一下。



&#216;
刷新模块

    sdr_ref刷新模块,收到刷新使能信号后进行刷新操作,在前文中“刷新时序”一节有讲述原因。本代码通过一个计数器cnt对时序进行计数,并产生了两个刷新命令;刷新完成后,让ref_done信号置1个时钟的高电平,表示刷新完成。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
module sdram_ref(clk, rst_n, ref_en, ref_done, ref_bus);

        input clk;
        input rst_n;
        input ref_en;
        output reg ref_done;
        output [19:0] ref_bus;

        parameter CNT_MAX = 9;
        // parameter TRP = 2;
        // parameter TRFC = 7;
        parameter NOP = 4'b0111;
        parameter PRE = 4'b0010;
        parameter REF = 4'b0001;

        reg [3:0] cnt;
        reg [3:0] sdr_cmd;
        reg [1:0] sdr_ba;
        reg [12:0] sdr_a;

        wire add_cnt;
        wire end_cnt;

        assign sdr_cke = 1'b1;
        assign ref_bus = {sdr_cke, sdr_cmd, sdr_ba, sdr_a};

    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 = ref_en;      
    assign end_cnt = add_cnt && cnt==CNT_MAX - 1 ;

        always @(posedge clk or negedge rst_n)begin
                if(!rst_n)begin
                        sdr_cmd <= NOP;
                end
                else if(ref_en && add_cnt && cnt == 0)begin
                        sdr_cmd <= PRE;
                end
                else if(add_cnt && cnt == 2 - 1)begin
                        sdr_cmd <= REF;
                end
                else begin
                        sdr_cmd <= NOP;
                end
        end

        always @(posedge clk or negedge rst_n)begin
                if(!rst_n)begin
                        sdr_a <= 13'd0;
                end
                else if(ref_en && add_cnt && cnt == 0)begin
                        sdr_a[10] <= 1'b1;
                end
                else begin
                        sdr_a <= 13'd0;
                end
        end

        always @(posedge clk or negedge rst_n)begin
                if(!rst_n)begin
                        ref_done <= 1'b0;
                end
                else if(add_cnt && cnt == CNT_MAX - 1)begin
                        ref_done <= 1'b1;
                end
                else begin
                        ref_done <= 1'b0;
                end
        end

endmodule







&#216;
选择模块

    sdr_mux模块,由于初始化模块和刷新模块都会发出SDRAM的信号,所以需要一个多路器来进行选择。由仲裁模块的sel_sm来控制输出init_bus信号还是ref_bus;当初始化没完成时输出init_bus,初始化完成时输出ref_bus。代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

module sdram_mux(clk, rst_n, init_bus, ref_bus, sdr_bus, sel_sm);

        input clk;
        input rst_n;
        input [19:0] init_bus;
        input [19:0] ref_bus;
        output reg [19:0] sdr_bus;
        input sel_sm;

        localparam SM_INIT = 1'b0;
        localparam SM_REF  = 1'b1;

        always @(posedge clk or negedge rst_n)begin
                if(!rst_n)begin
                        sdr_bus <= init_bus;
                end
                else if(sel_sm == SM_INIT)begin
                        sdr_bus <= init_bus;
                end
                else if(sel_sm == SM_REF)begin
                        sdr_bus <= ref_bus;
                end
                else begin
                        sdr_bus <= sdr_bus;
                end
        end

endmodule





1.3
仿真测试

    最后对代码进行仿真,仿真文件参考:sdram_top_tb.v

     modelsim生成的报告如下所示,出现如下LOG信息,说明成功。
82.png

83.png


    以上就是SDRAM控制器的完整设计,更多FPGA资料可以进入明德扬论坛进行学习交流(http://www.fpgabbs.cn/),明德扬专注FPGA设计研究,对FPGA学习感兴趣的朋友快快联系我们吧!






FPGA视频课程  培训班 FPGA学习资料
吴老师 18022857217(微信同号) Q1241003385
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|小黑屋|MDYBBS ( 粤ICP备16061416号 )

GMT+8, 2024-4-19 07:34 , Processed in 0.068356 second(s), 25 queries .

Powered by Discuz! X3.4

本论坛由广州健飞通信有限公司所有

© 2001-2019 Comsenz Inc.

快速回复 返回顶部 返回列表