FPGA之旅设计99例之第二十例---SDRAM存储器实现(下)

简介: 笔记

四. 刷新模块


由于sdram的特殊结构,sdram在使用的过程中,需要每隔一段时间,对所有的存储区域进行一次刷新操作(充电),否则内部存储的数据会丢失,这将会成为后面设计的一大难点。


根据手册得知每64ms需要完成8192次刷新操作,也就是下面的时序图需要在64ms内运行8192次,平均下来7us就要进行一次,这个时间需要记住非常重要。


同样也是根据手册给出的时序图进行编写代码,一共需要发送三个命令

1.png



代码实现如下,也是非常容易实现的。

2.png



从图中可以看出,每个7090ns进行一次刷新,满足要求。

3.png


至此,初始化和刷新模块编写完成,这两部分只需要按照手册上给出的时序图来编写代码即可,比较容易即可完成,后面的读写模块会复杂一些。


五. 写模块


读模块的时序图如下,截取的是没有auto precharge操作的,也就是在数据写完后,需要手动发送一个precharge命令。同样可以让sdram自动完成这个操作,只需要在发送write命令的时候,将A10拉高即可,这样在发送完数据后,就可以直接结束了,不用发送precharge命令。本次介绍的是需要发送precharge命令。


设计是需要清楚以下两个问题


发送过程中,需要切换行地址或者bank的时候,应该怎样操作

发送过程中,突然来了刷新请求时,该如何处理

4.png


先对第一个问题进行说明一下,在sdram中,行地址和bank是发送ACTIVE命令时指定的,发送write命令时,就可以指定列地址了,如下BL=1。也就是说切换行地址或bank时需要重新发送ACTIVE命令。ps: 写操作是没有潜伏期的。

5.png



手册中也给出了这部分的时序图,如下。需要注意的一点是,它这是使能了auto precharge,所以数据发送完成后,没有发送precharge命令,就发送了ACTIVE命令来切换行地址或bank了。没有使能的情况下,需要加上precharge地址,然后再延时tRCD,发送ACTIVE命令,这点需要注意。


6.png


第二个问题,当刷新请求来时,这个时候当然是要暂停发送数据,需要保存已经发送数据的个数,以及当前发送的地址和bank。然后在刷新结束后,继续发送数据。


模块框图和状态机如下。在write_data_en使能的情况下,外部输入数据进来,其余时刻输入的数据无效,相当于一个握手信号。sdram模式寄存器配置的是突发长度为1,所以这里单次写突发长度是没有大小限制的。


sdram_write
(   
    input               sdram_clk,
    input               rst_n,
    input               sdram_write_req,            //写请求
    output              sdram_write_ack,            //写响应
    input               sdram_write_pause,          //写暂停信号,转去刷新操作
    output  reg         sdram_write_pause_ack,      //写暂停响应,成功暂停    
    input[24:0]         write_addr,                 //写入地址
    input[15:0]         write_data,                 //写入数据      {bank[1:0s],row[12:0],clo[9:0]}
    input[9:0]          write_burst_length,         //单次写突发长度    //可以为sdram大小
    output              write_data_en,              //写入数据有效输出
    //sdram接口
    output[3:0]         sdram_write_cmd,
    output[12:0]        sdram_write_addr,
    output[1:0]         sdram_write_ba,
    output[15:0]        sdram_write_data
);
localparam      S_IDEL                  =   'd0;        //空闲态
localparam      S_ACTIVE                =   'd1;        //激活态
localparam      S_WRITE                 =   'd2;        //写数据
localparam      S_PAUSE                 =   'd3;        //写暂停
localparam      S_ALTERNATE             =   'd4;        //换行换bank缓存
localparam      S_PRECHARGE             =   'd5;        //写结束后或切换行列地址,发送precharge命令
localparam      S_END                   =   'd6;        //写结束
always@(*)
begin
    case(state)
    S_IDEL:
        if( sdram_write_req == 1'b1 )
            next_state <= S_ACTIVE;
        else
            next_state <= S_IDEL;
    S_ACTIVE:
        if( sdram_write_pause == 1'b1 )     //写暂停信号,转去刷新操作
            next_state <= S_PAUSE;
        else if( time_cnt == `tRCD )
            next_state <= S_WRITE;
        else
            next_state <= S_ACTIVE;
    S_WRITE:
        if( sdram_write_pause == 1'b1 )     //写暂停信号,转去刷新操作
            next_state <= S_PAUSE;
        else if( alternating_bank_row_en == 1'b1)
            next_state <= S_PRECHARGE;
        else if( write_burst_length_cnt == write_burst_length )
            next_state <= S_PRECHARGE;
        else
            next_state <= S_WRITE;
    S_PAUSE:
        if( sdram_write_pause == 1'b0 )     //刷新操作结束
            next_state <= S_ACTIVE;
        else
            next_state <= S_PAUSE;
    S_ALTERNATE:
        if( time_cnt == `tRCD)
            next_state <= S_ACTIVE;
        else
            next_state <= S_ALTERNATE;
    S_PRECHARGE:
        if( time_cnt == `tRP + `tWR)
            if( write_burst_length_cnt >= write_burst_length)
                next_state <= S_END;
            else
                next_state <= S_ALTERNATE;
        else
            next_state <= S_PRECHARGE;
    S_END:
        next_state <= S_IDEL;
    default :  next_state <= S_IDEL;
    endcase
end


最后通过仿真,确认实现正确,第一幅图是写过程进行刷新操作,第二幅图是,写过程切换行地址

7.png8.png




六. 读模块


读模块过程的编写和写模块是一模一样的,不过需要注意的是读模块有潜伏期,命令发送和数据输出相差CL个时钟周期,读数据的时候,需要将这个延时加入其中,可以看到接口信号和写模块是一样。不过对数据进行采样的时候,需要使用输入到sdram中的时钟,这需要注意。

9.png



sdram_read
(
    input               sdram_clk,
    input               rst_n,
    input               sdram_read_req,            //读请求
    output              sdram_read_ack,            //读响应
    input               sdram_read_pause,          //读暂停信号,转去刷新操作
    output  reg         sdram_read_pause_ack,      //读暂停响应,成功暂停    
    input[24:0]         read_addr,                 //读入地址
    output[15:0]        read_data,                 //读出数据      {bank[1:0s],row[12:0],clo[9:0]}
    input[9:0]          read_burst_length,         //单次读突发长度    //可以为sdram大小
    output              read_data_en,              //读数据有效输出
    //sdram接口
    output[3:0]         sdram_read_cmd,
    output[12:0]        sdram_read_addr,
    output[1:0]         sdram_read_ba,
    input[15:0]        sdram_read_data
);


通过仿真输出,确定突发读期间,换行以及刷新完全正确

10.png

至此SDRAM模块的编写就完成了,顶层框图如下,至于在外部如何进行封装,那就看不同的需求了。


有任何设计疑问可以关注 公众号 FPGA之旅 与我交流。


关注微信 公众号 FPGA之旅 回复 FPGA之旅设计99例之第二十例 获取全部工程文件 包括sdram仿真模型。


SDRAM_TOP(
    input                       sys_clk,                    //sdram的系统时钟 100M
    input                       rst_n,                      //异步复位信号
    //读接口
    input                       read_req,
    output                      read_ack,
    input[24:0]                 read_addr,
    input[9:0]                  read_burst_length,
    output[15:0]                read_data,
    output                      read_data_en,
    //写接口
    input                       write_req,
    output                      write_ack,
    input[24:0]                 write_addr,
    input[9:0]                  write_burst_length,
    input[15:0]                 write_data,
    output                      write_data_en,
    //sdram接口
    output                      sdram_clk,         //sdram clock
  output                      sdram_cke,         //sdram clock enable
  output                      sdram_cs_n,        //sdram chip select
  output                      sdram_we_n,        //sdram write enable
  output                      sdram_cas_n,       //sdram column address strobe
  output                      sdram_ras_n,       //sdram row address strobe
  output[1:0]                 sdram_dqm,         //sdram data enable
  output[1:0]                 sdram_ba,          //sdram bank address
  output[12:0]                sdram_addr,        //sdram address
  inout[15:0]                 sdram_dq           //sdram data
);


公众号:FPGA之旅


目录
相关文章
|
5月前
|
异构计算
FPGA进阶(3):SDRAM读写控制器的设计与验证(二)
FPGA进阶(3):SDRAM读写控制器的设计与验证(二)
43 0
|
5月前
|
异构计算 内存技术
FPGA进阶(3):SDRAM读写控制器的设计与验证(一)
FPGA进阶(3):SDRAM读写控制器的设计与验证
130 0
|
Go vr&ar C语言
【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验二十一:SDRAM模块④ — 页读写 β
实验二十一:SDRAM模块④ — 页读写 β 未进入主题之前,让我们先来谈谈一些重要的体外话。《整合篇》之际,笔者曾经比拟Verilog如何模仿for循环,我们知道for循环是顺序语言的产物,如果Verilog要实现属于自己的for循环,那么它要考虑的东西除了步骤以外,还有非常关键的时钟。
886 0
|
Go vr&ar C语言
【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验十九:SDRAM模块② — 多字读写
实验十九:SDRAM模块② — 多字读写 表示19.1 Mode Register的内容。 Mode Register A12 A11 A10 A9 A8 A7 A6 A5 A4 A3...
817 0
|
Go vr&ar C语言
【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验十八:SDRAM模块① — 单字读写
实验十八:SDRAM模块① — 单字读写 笔者与SDRAM有段不短的孽缘,它作为冤魂日夜不断纠缠笔者。笔者尝试过许多方法将其退散,不过屡试屡败的笔者,最终心情像橘子一样橙。《整合篇》之际,笔者曾经大战几回儿,不过内容都是点到即止。
1055 0
|
13天前
|
算法 数据安全/隐私保护 异构计算
基于FPGA的16QAM调制+软解调系统,包含testbench,高斯信道模块,误码率统计模块,可以设置不同SNR
本项目基于FPGA实现了16QAM基带通信系统,包括调制、信道仿真、解调及误码率统计模块。通过Vivado2019.2仿真,设置不同SNR(如8dB、12dB),验证了软解调相较于传统16QAM系统的优越性,误码率显著降低。系统采用Verilog语言编写,详细介绍了16QAM软解调的原理及实现步骤,适用于高性能数据传输场景。
113 69
|
17天前
|
移动开发 算法 数据安全/隐私保护
基于FPGA的QPSK调制+软解调系统,包含testbench,高斯信道模块,误码率统计模块,可以设置不同SNR
本文介绍了基于FPGA的QPSK调制解调系统,通过Vivado 2019.2进行仿真,展示了在不同信噪比(SNR=1dB, 5dB, 10dB)下的仿真效果。与普通QPSK系统相比,该系统的软解调技术显著降低了误码率。文章还详细阐述了QPSK调制的基本原理、信号采样、判决、解调及软解调的实现过程,并提供了Verilog核心程序代码。
53 26
|
23天前
|
算法 异构计算
基于FPGA的4ASK调制解调系统,包含testbench,高斯信道模块,误码率统计模块,可以设置不同SNR
本文介绍了基于FPGA的4-ASK调制解调系统的算法仿真效果、理论基础及Verilog核心程序。仿真在Vivado2019.2环境下进行,分别测试了SNR为20dB、15dB、10dB时的性能。理论部分概述了4-ASK的工作原理,包括调制、解调过程及其数学模型。Verilog代码实现了4-ASK调制器、加性高斯白噪声(AWGN)信道模拟、解调器及误码率计算模块。
44 8