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之旅


目录
相关文章
|
Go vr&ar C语言
【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验二十一:SDRAM模块④ — 页读写 β
实验二十一:SDRAM模块④ — 页读写 β 未进入主题之前,让我们先来谈谈一些重要的体外话。《整合篇》之际,笔者曾经比拟Verilog如何模仿for循环,我们知道for循环是顺序语言的产物,如果Verilog要实现属于自己的for循环,那么它要考虑的东西除了步骤以外,还有非常关键的时钟。
853 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...
794 0
|
Go vr&ar C语言
【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验十八:SDRAM模块① — 单字读写
实验十八:SDRAM模块① — 单字读写 笔者与SDRAM有段不短的孽缘,它作为冤魂日夜不断纠缠笔者。笔者尝试过许多方法将其退散,不过屡试屡败的笔者,最终心情像橘子一样橙。《整合篇》之际,笔者曾经大战几回儿,不过内容都是点到即止。
1008 0
|
6天前
|
机器学习/深度学习 算法 异构计算
m基于FPGA的多通道FIR滤波器verilog实现,包含testbench测试文件
本文介绍了使用VIVADO 2019.2仿真的多通道FIR滤波器设计。展示了系统RTL结构图,并简述了FIR滤波器的基本理论,包括单通道和多通道的概念、常见结构及设计方法,如窗函数法、频率采样法、优化算法和机器学习方法。此外,还提供了Verilog核心程序代码,用于实现4通道滤波器模块,包含时钟、复位信号及输入输出接口的定义。
22 7
|
2月前
|
算法 异构计算
基于FPGA的ECG信号滤波与心率计算verilog实现,包含testbench
基于FPGA的ECG信号滤波与心率计算verilog实现,包含testbench
|
1月前
|
算法 异构计算
m基于FPGA的电子钟verilog实现,可设置闹钟,包含testbench测试文件
该文介绍了基于FPGA的电子钟设计,利用Vivado2019.2平台进行开发并展示测试结果。电子钟设计采用Verilog硬件描述语言,核心包括振荡器、分频器和计数器。时间显示为2个十进制格式,闹钟功能通过存储器和比较器实现,当当前时间等于设定时间时触发。文中给出了Verilog核心程序示例,展示了时钟信号、设置信号及输出的交互。
28 2
|
3月前
|
算法 5G 数据处理
m基于FPGA的PPM光学脉位调制解调系统verilog实现,包含testbench
m基于FPGA的PPM光学脉位调制解调系统verilog实现,包含testbench
46 0
|
3月前
|
算法 异构计算 索引
m基于FPGA的Hamming汉明编译码verilog实现,包含testbench测试文件,不使用IP核
m基于FPGA的Hamming汉明编译码verilog实现,包含testbench测试文件,不使用IP核
42 1

热门文章

最新文章