SDRAM的数据存储实现并对其数据进行读写操作

文章目录

  • SDRAM的数据存储实现并对其数据进行读写操作
    • 1 SDRAM
      • 1.1 简介
      • 1.2 SDRAM结构和原理
      • 1.3 SDRAM块
    • 2 SDRAM的操作
      • 2.1 SDRAM的芯片初始化
      • 2.2 SDRAM行激活
      • 2.3 SDRAM列读写
      • 2.4 数据输入输出
      • 2.5 突发长度
      • 2.6 预充电
      • 2.7 刷新
    • 3 基于SDRAM的数据读写实现
      • 3.1 主要实现过程
      • 3.2 运行结果

1 SDRAM

1.1 简介

​ SDRAM(Synchronous Dynamic Random Access Memory),即同步动态随机访问存储器。同步是指内存工作需要同步时钟,内部命令的发送和数据的传输都以它为基准;而动态是指存储阵列需要不断地刷新来保证数据不丢失;随机指数据不是依次存储的,而随机的指定地址进行数据的读写操作。SDARM的读写是一个比较复杂的流程,其中包括行激活、列读写、预充电、刷新等一系列操作。

1.2 SDRAM结构和原理

​ 1、结构:

SDRAM的内部是一个存储阵列,我们可以将其想象成一张表格

​ 2、原理:当向表格中写入数据时,需要先指定行(Row),再指定列(Column),就可以准确地找到所需要的“单元格”。SDRAM存储数据是利用了电容的充放电特性以及能够保持电荷的能力。一个大小为1bit的存储单元的结构如下图所示:

它主要由行列选通三极管,存储电容,刷新放大器组成。行地址与列地址选通使得存储电容与数据线导通,从而可进行放电(读取)与充电(写入)操作。

​ 3、特点:SDRAM具有空间存储量大、读写速度快、价格相对便宜等优点。然而由于SDRAM内部利用电容来存储数据,为保证数据不丢失,需要持续对各存储电容进行刷新操作;同时在读写过程中需要考虑行列管理、各种操作延时等,由此导致了其控制逻辑复杂的特点。

1.3 SDRAM块

​ 1、“单元格”就是SDRAM存储芯片中的存储单元,而这个“表格”(存储阵列)我们称之为L-Bank。通常SDRAM的存储空间被划分为4个L-Bank,在寻址时需要先指定其中一个L-Bank,然后在这个选定的L-Bank中选择相应的行与列进行寻址(寻址就是指定存储单元地址的过程)。

​ 2、容量计算:

​ 对SDRAM的读写是针对存储单元进行的,对SDRAM来说一个存储单元的容量等于数据总线的位宽,单位是bit。那么SDRAM芯片的总存储容量我们就可以通过下面的公式计算出来:

SDRAM总存储容量 = L-Bank的数量×行数×列数×存储单元的容量(数据线的位宽)

2 SDRAM的操作

2.1 SDRAM的芯片初始化

​ SDRAM芯片上电之后需要一个初始化的过程,以保证芯片能够按照预期方式正常工作,初始化流程如图


SDRAM上电后要有200us的输入稳定期,在这个时间内不可以对SDRAM的接口做任何操作; 200us结束以后给所有L-Bank预充电,然后是连续8次刷新操作;最后设置模式寄存器。初始化最关键的阶段就在于模式寄存器(MR,Mode Register)的设置,简MRS(MR Set),模式寄存器设置主要用来确定芯片的工作方式,包括突发长度(Burst Length)、潜伏期(CAS Latency)以及操作模式等。

2.2 SDRAM行激活

​ 初始化完成后,无论是读操作还是写操作,都要先激活(Active)SDRAM中的一行,使之处于活动状态(又称行有效)。在此之前还要进行SDRAM芯片的片选和L-Bank的定址,不过它们与行激活可以同时进行。

​ 从上图可以看出,在片选CS#(#表示低电平有效)、L-Bank定址的同时,RAS(Row Address Strobe,行地址选通脉 冲)也处于有效状态。此时An地址线则发送具体的行地址。如图中是A0- A11,共有12个地址线,由于是二进制表示法, 所以共有4096个行(2^12=4096),A0-A11的不同数值就确定了具体的行地址。由于行激活的同时也是相应L-Bank有效,所以行激活也可称为L-Bank有效。

2.3 SDRAM列读写

​ 行地址激活之后,就要对列地址进行寻址了。由于在SDRAM中,地址线是行列共用的,因此列寻址时地址线仍然是A0-A11。在寻址时,利用RAS(Row Address Strobe,行地址选通脉冲)与CAS(Column Address Strobe,列地址选通脉冲)来区分行寻址与列寻址,如图所示。

图中“x16”表示存储单元容量为16bit。一般来说,在SDRAM中存储阵列(L-Bank) 的列数小于行数,即列地址位宽小于行地址,因此在列地址选通时地址线高位可能未用到。

2.4 数据输入输出

​ 1、数据输出(即读操作):在选定列地址后,就已经确定了具体的存储单元,剩下的事情就是数据通过数据I/O通道(DQ)输出到内存总线上了。但是在CAS发出之后,仍要经过一定的时间才能有数据输出,从CAS与读取命令发出到第一 笔数据输出的这段时间,被定义为CL(CAS Latency,CAS潜伏期)。CL时间越短,读数据时SDRAM响应就越快。由于CL只在读取时出现,所以CL又被称为读取潜伏期(RL,Read Latency)。CL的单位与tRCD一样,为时钟周期数,具体耗时由时钟频率决定。

​ 2、数据输入(即写操作):数据写入的操作也是在tRCD之后进行,但此时没有了CL(记住,CL只出现在读取操作中),数据与写指令同时发送。不过,数据并不是即时地写入存储单元,数据的真正写入需要一定的周期。为了保证数据的可靠写入,都会留出足够的写入/校正时间(tWR, Write Recovery Time),这个操作也被称作写回(Write Back)。tWR至少占用一个时钟周期或再多一点(时钟频率越高,tWR占用周期越多)

2.5 突发长度

​ 1、突发长度:

​ 突发(Burst)是指在同一行中相邻的存储单元连续进行数据传输的方式,连续传输所涉及到存储单元(列)的数量就是突发长度(Burst Lengths,简称BL)。

​ 2、读/写操作,都是一次对一个存储单元进行寻址。然而在现实中很少只对SDRAM 中的单个存储空间进行读写,一般都需要完成连续存储空间中的数据传输。在连续读/写操作时,为了对当前存储单元的下一个单元进行寻址,需要不断的发送列地址与读/写命令(行地址不变,所以不用再对行寻址)。

2.6 预充电

​ 1、预充电:在对SDRAM某一存储地址进行读写操作结束后,如果要对同一L-Bank的另一行进行寻址, 就要将原来有效(工作)的行关闭,重新发送行/列地址。L-Bank关闭现有工作行,准备打开新行的操作就是预充电(Precharge)。

​ 在读写过程中,工作行内的存储体由于“行激活”而使存储电容受到干扰,因此在关闭工作行前需要对本行所有存储体进行重写。预充电实际上就是对工作行中所有存储体进行数据重写,并对行地址进行复位,以准备新行工作的过程。 预充电可以通过命令控制,也可以通过辅助设定让芯片在每次读写操作之后自动进行预充电。现在我们再回过头看看读写操作时的命令时序图,从中可以发现地址线A10 控制着是否进行在读写之后对当前L-Bank自动进行预充电,这就是上文所说的“辅助设定”。而在单独的预充电命令中,A10则控制着是对指定的L-Bank还是所有的L-Bank(当有多个L-Bank处于有效/活动状态时)进行预充电,前者需要提供L-Bank的地址,后者只需将A10信号置于高电平。在发出预充电命令之后,要经过一段时间才能发送行激活命令打开新的工作行,这个间隔被称为tRP(Precharge command Period,预充电有效周期),和tRCD、CL一样,tRP的单位也是时钟周期数,具体值视时钟频率而定。读取时预充电时序图(CL=2、BL=4、tRP=2)写操作时,由于每笔数据的真正写入则需要一个足够的周期来保证,这段时间就是写回周期(tWR)。所以预充电不能与写操作同时进行,必须要在tWR之后才能发出预充电命令,以确保数据的可靠写入,否则重写的数据可能出错 。

2.7 刷新

​ 1、SDRAM之所以称为同步“动态”随机存储器,就是因为它要不断进行刷新(Refresh)才能保留住数据,因此刷新是SDRAM最重要的操作。刷新操作与预充电类似,都是重写存储体中的数据。但为什么有预充电操作还要进行刷新呢?因为预充电是对一个或所有L-Bank中的工作行(处于激活状态的行)操作,并且是不定期的;而刷新则是有固定的周期,并依次对所有行进行操作,以保留那些久久没经历重写的存储体中的数据。但与所有L-Bank预充电不同的是,这里的行是指所有L-Bank中地址相同的 行,而预充电中各L-Bank中的工作行地址并不是一定是相同的。

​ 2、目前公认的标准是,存储体中电容的数据有效保存期上限是64ms,也就是说每一行刷新的循环周期是64ms。我们在看SDRAM芯片参数时,经常会看到4096 Refresh Cycles/64ms或8192 Refresh Cycles/64ms的标识,这里的4096与8192就代表这个芯片中每个L-Bank的行数。刷新命令一次仅对一行有效,也就是说在64ms内这两种规格的芯片分别需要完成4096次和8192次刷新操作。因此,L-Bank为4096行时刷新命令的发送间隔为 15.625μs(64ms/4096),8192行时为7.8125μs(64ms/8192)。

3 基于SDRAM的数据读写实现

3.1 主要实现过程

​ 1、思想:

​ PC机通过串口向SDRAM芯片突发写入数据,通过按键发起读请求,将写入的数据突发读出,并再次通过串口回传到PC端。SDRAM上电之后进入等待状态等待200us,200us之后对所有L-Bank进行预充电处理,预充电后进行自动刷新(一般持续8个周期),然后进行模式寄存器的初始化设置,主要设置SDRAM的突发长度、潜伏期以及操作模式等,至此sdram的初始化就完成了。sdram进入空闲状态,当有读写需求时,sdram先进行行激活操作,然后根据命令进行列读写,当突发读写完成后再次进行预充电,然后回到空闲状态等待,此时若需要自动刷新,则发送一个自动刷新命令并进入自动刷新状态,刷新完成后再回到空闲状态。状态图如下:

​ 2、由于SDRAM的各种状态操作比较复杂,而Platform Designer提供的SDRAM IP 核接口极大的分辨率我们对于SDRAM的使用,因此实现时我们需要调用IP核来完成这些复杂的操作,我们只需要在sdram的控制模块完成突发读写操作即可。

​ 3、sdram控制模块代码

module sdram_ctrl (input            clk               ,                    //sdram时钟100MHzinput            clk_in            ,                    //fifo的数据输入时钟50MHzinput            clk_out           ,                    //fifo的数据输出时钟50MHz                       input            rst_n             ,                    //复位信号                    input   [7:0]    din               ,                    //写入数据,先存到写fifo中                    input            din_vld           ,                    //input            rd_req            ,                    //读请求    input            ready             ,                    //tx发送的握手信号output  [7:0]    dout              ,                    //读出的数据,由读fifo获取output           dout_vld          ,                    //output  [23:0]   avm_address       ,                    //sdram地址2位bank地址+13位行地址+9位列地址    output  [15:0]   avm_writedata     ,                    //写数据,写fifo的输出output           avm_read          ,                    //读使能                        output           avm_write         ,                    //写使能input   [15:0]   avs_readdata      ,                    //读数据,存到读fifo中                                          input            avs_readdatavalid ,                    input            avs_waitrequest                        //  );//状态机参数定义parameter   IDLE  = 3'b001 ,                            //空闲状态WRITE = 3'b010 ,                            //写数据状态READ  = 3'b100 ;                            //读数据状态//信号定义reg   [2:0]     cstate         ;reg   [2:0]     nstate         ;reg   [23:0]    cnt_wraddr     ;                        //写地址计数wire            add_cnt_wraddr ;wire            end_cnt_wraddr ;reg   [23:0]    cnt_rdaddr     ;                        //读地址计数wire            add_cnt_rdaddr ;wire            end_cnt_rdaddr ;reg   [7:0]     cnt_burst      ;                        //读写状态下突发长度计数wire            add_cnt_burst  ;wire            end_cnt_burst  ;reg             wr_flag        ;                        //写标志reg             rd_flag        ;                        //读标志reg             last_flag      ;                        //上一次执行的操作reg             prior_flag     ;                        //读写优先级判断,高读低写reg   [7:0]     dout_r         ;reg             dout_r_vld     ;wire  [7:0]     wrfifo_data    ;wire            wrfifo_rdreq   ;wire            wrfifo_wrreq   ;wire  [15:0]    wrfifo_q       ;wire            wrfifo_empty   ;wire  [6:0]     wrfifo_usedw   ;wire            wrfifo_full    ;wire  [15:0]    rdfifo_data    ;wire            rdfifo_rdreq   ;wire            rdfifo_wrreq   ;wire  [7:0]     rdfifo_q       ;wire            rdfifo_empty   ;wire            rdfifo_full    ;wire            idle_to_write  ;wire            idle_to_read   ;wire            write_to_idle  ;wire            read_to_idle   ;             //状态机转移always @(posedge clk or negedge rst_n) beginif (!rst_n)cstate <= IDLE;else cstate <= nstate;endalways @(*) begincase (cstate)IDLE:beginif (idle_to_write)nstate = WRITE;else if (idle_to_read)nstate = READ;elsenstate = cstate;endWRITE:beginif (write_to_idle)nstate = IDLE;elsenstate = cstate;endREAD:beginif (read_to_idle)nstate = IDLE;elsenstate = cstate;enddefault:  ;endcaseend//状态机转移条件判断assign  idle_to_write = cstate == IDLE && (~prior_flag && wrfifo_usedw > `BURST_LEN) ;assign  idle_to_read  = cstate == IDLE && (rd_flag && prior_flag)                    ;assign  write_to_idle = cstate == WRITE && end_cnt_burst                             ;assign  read_to_idle  = cstate == READ && end_cnt_burst                              ;/************************ 读写优先级仲裁 *************************///wr_flagalways @(posedge clk or negedge rst_n) beginif (!rst_n)wr_flag <= 0;else if (wrfifo_usedw > `BURST_LEN)wr_flag <= 1'b1;elsewr_flag <= 1'b0;end//rd_flagalways @(posedge clk or negedge rst_n) beginif (!rst_n)rd_flag <= 0;else if (~rd_flag && rd_req)rd_flag <= 1'b1;else if (rd_flag & idle_to_read)rd_flag <= 1'b0;end//last_flagalways @(posedge clk or negedge rst_n) beginif (!rst_n)last_flag <= 0;else if (write_to_idle)last_flag <= 1'b0;else if (read_to_idle)last_flag <= 1'b1;end//prior_flagalways @(posedge clk or negedge rst_n) beginif (!rst_n)prior_flag <= 0;else if (wr_flag && (last_flag || ~rd_flag))prior_flag <= 1'b0;else if (rd_flag && (~last_flag || ~wr_flag))prior_flag <= 1'b1;end/****************************************************************///突发计数always @(posedge clk or negedge rst_n) beginif (!rst_n)cnt_burst <= 0;else if (add_cnt_burst)beginif (end_cnt_burst) cnt_burst <= 0;elsecnt_burst <= cnt_burst + 1'b1;endendassign  add_cnt_burst = (cstate == WRITE || cstate == READ) && ~avs_waitrequest;assign  end_cnt_burst = add_cnt_burst && cnt_burst == `BURST_LEN - 1;//写地址计数always @(posedge clk or negedge rst_n) beginif (!rst_n)cnt_wraddr <= 0;else if (add_cnt_wraddr)beginif (end_cnt_wraddr) cnt_wraddr <= 0;elsecnt_wraddr <= cnt_wraddr + 1'b1;endendassign  add_cnt_wraddr = cstate == WRITE && ~avs_waitrequest;assign  end_cnt_wraddr = add_cnt_wraddr && cnt_wraddr == `BURST_MAX - 1;//读地址计数always @(posedge clk or negedge rst_n) beginif (!rst_n)cnt_rdaddr <= 0;else if (add_cnt_rdaddr)beginif (end_cnt_rdaddr) cnt_rdaddr <= 0;elsecnt_rdaddr <= cnt_rdaddr + 1'b1;endendassign  add_cnt_rdaddr = cstate == READ && ~avs_waitrequest;assign  end_cnt_rdaddr = add_cnt_rdaddr && cnt_rdaddr == `BURST_MAX - 1;//dout_ralways @(posedge clk_out or negedge rst_n) beginif (!rst_n)begindout_r <= 0;dout_r_vld = 0;endbegindout_r <= rdfifo_q;dout_r_vld <= rdfifo_rdreq;endend//例化写fifowrfifo               wrfifo_inst (.aclr               (~rst_n       ),.data               (wrfifo_data  ),.rdclk              (clk          ),.rdreq              (wrfifo_rdreq ),.wrclk              (clk_in       ),.wrreq              (wrfifo_wrreq ),.q                  (wrfifo_q     ),.rdempty            (wrfifo_empty ),.rdusedw            (wrfifo_usedw ),.wrfull             (wrfifo_full  ));assign  wrfifo_data = din;assign  wrfifo_wrreq = ~wrfifo_full & din_vld ;assign  wrfifo_rdreq = ~wrfifo_empty & add_cnt_wraddr ;//例化读fifordfifo                rdfifo_inst (.aclr               (~rst_n       ),.data               (rdfifo_data  ),.rdclk              (clk_out      ),.rdreq              (rdfifo_rdreq ),.wrclk              (clk          ),.wrreq              (rdfifo_wrreq ),.q                  (rdfifo_q     ),.rdempty            (rdfifo_empty ),.wrfull             (rdfifo_full  ));assign  rdfifo_data = avs_readdata;assign  rdfifo_wrreq = ~rdfifo_full & avs_readdatavalid ;assign  rdfifo_rdreq = ~rdfifo_empty & ~ready           ;//输出assign  dout          = dout_r                   ;assign  dout_vld      = dout_r_vld               ;assign  avm_writedata = wrfifo_q                 ;assign  avm_read      = ~(cstate == READ)        ;             //低电平有效assign  avm_write     = ~(cstate == WRITE)       ;assign  avm_address   = cstate == WRITE ? {cnt_wraddr[23],cnt_wraddr[21:9],cnt_wraddr[22],cnt_wraddr[8:0]}:{cnt_rdaddr[23],cnt_rdaddr[21:9],cnt_rdaddr[22],cnt_rdaddr[8:0]};endmodule //sdram_ctrl

3.2 运行结果

​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uPgASmaP-1659576614940)(result.png)]

assign  avm_write     = ~(cstate == WRITE)       ;
assign  avm_address   = cstate == WRITE ? {cnt_wraddr[23],cnt_wraddr[21:9],cnt_wraddr[22],cnt_wraddr[8:0]}:{cnt_rdaddr[23],cnt_rdaddr[21:9],cnt_rdaddr[22],cnt_rdaddr[8:0]};

endmodule //sdram_ctrl

#### 3.2  运行结果
![在这里插入图片描述](https://img-blog.csdnimg.cn/390bc8ff345a40a6a7373c1ac5ed957d.png)​     还有点问题,第一次接收时总是会先接收三个占空符,目前还没有找到问题在哪里。

SDRAM的数据存储实现并对其数据进行读写操作相关推荐

  1. 服务端指南 数据存储篇 | 选择合适的数据存储方案

    在服务端会经常遇到数据存储的选型问题,是选择使用关系型数据库 MySQL,还是选择内存数据库 Redis,还是选择文档数据库 MongoDB,还是选择列族数据库 HBase, 还是选择全文搜索引擎 E ...

  2. mysql更改数据库数据存储目录_MySQL更改数据库数据存储目录

    MySQL数据库默认的数据库文件位于/var/lib/mysql下,有时候由于存储规划等原因,需要更改MySQL数据库的数据存储目录.下文总结整理了实践过程的操作步骤. 1:确认MySQL数据库存储目 ...

  3. < Android数据存储> 任务二 应用程序数据文件夹里的文件读写

    :zh]Android中提供了两个方法用来打开应用程序的数据文件夹IO流. 1.FileInputStream openFileInput(String name):参数name表示某个文件名,该方法 ...

  4. mysql 轨迹数据存储_python爬虫26 | 把数据爬取下来之后就存储到你的MySQL数据库...

    小帅b说过 在这几篇中会着重说说将爬取下来的数据进行存储 上次我们说了一种 csv 的存储方式 这次主要来说说怎么将爬取下来的数据保存到 MySQL 数据库 接下来就是 学习python的正确姿势 真 ...

  5. 租户隔离怎么做MYSQL_基于JPA实现SaaS多租户模式的数据存储——共享数据库,隔离数据架构...

    SaaS是Software-as-a-Service(软件即服务)的简称,这边具体的解释不介绍.多租户的系统可以应用这种模式的思想,将思想融入到系统的设计之中. 现在SaaS Multi-Tenant ...

  6. 计算机数据存储怎么转换,数据存储单位之间怎么换算 数据储存单位之间换算方法【图文】...

    关于数据存储,你了解它是怎么一回事吗?所谓的数据存储就是指需要数据流找到的信息,这些信息多半是加工过程中需要的.或者是一些临时文件,而这些文件也是在加工过程中出现的.以不同的格式将数据记录在电脑的内存 ...

  7. 海量视频监控数据存储和管理是大数据最重要的命题

    海量视频数据的存储和管理 维基百科全书的定义:"大数据是飞速增长的,用现有数据库管理工具难以管理的数据集合".这些数据包括:社交媒体.移动设备.科学计算和城市中部署的各类传感器等等 ...

  8. IOS数据存储 —— 1 沙盒(数据存储目录)

    沙盒结构 沙盒的文件系统⽬录,如下图所示(假设应用的名称叫Layer) 1.Documents 目录 您应该将所有的应用程序数据文件写入到这个目录下. 这个目录用于存储用户数据或其它应该定期备份的信息 ...

  9. python数据存储系列教程——python(pandas)读写csv文件

    全栈工程师开发手册 (作者:栾鹏) python教程全解 CSV文件的规范 1.使用回车换行(两个字符)作为行分隔符,最后一行数据可以没有这两个字符.2.标题行是否需要,要双方显示约定3.每行记录的字 ...

最新文章

  1. HSSFWorkbook 与 XSSFWorkbook
  2. 安装 Homebrew
  3. 为什么要学python语言_我们为什么要学习Python语言?
  4. Quartz.NET总结(四)Quartz 远程调度
  5. MYSQL数据库导入出错:#1046 - No database selected
  6. git 创建webpack项目_Webpack入门:从安装到配置
  7. 服装CAD计算机按钮在哪里,富仪服装CAD快捷键大全
  8. macOS苹果电脑下载m3u8、ts视频
  9. 【Pygame实战】超有趣的泡泡游戏来袭——愿你童心不泯,永远快乐简单哦~
  10. Tomcat 端口被javaw.exe占用 有效解决方法
  11. python爬虫 爬取视频 练习
  12. 玩家密无忧 v7.0.7 绿色
  13. 设计窗口模拟教室座位表java,基于web的考研自习教室座位管理(完整源码+论文全套+教学视频)...
  14. jenkins定时构建
  15. 计算机室内设计cad实践报告,cad室内设计实习报告
  16. 《计算机存储与外设》----2.3 DRAM
  17. Android基础篇 访问Assets文件夹里面的资源【文本、图片、音频、字体包】
  18. C++设计模式之状态模式(state)(行为型)
  19. mysql8.0.19中在navicat客户端中int、bigint等类型设置长度保存后为0
  20. 智慧城市概述:算法与数据链接下的未来设想

热门文章

  1. 未能监测到驱动人生服务器,驱动人生评估结果未达到100%支持Win10如何解决
  2. 五轴数控铣削后处理技术CLSF刀位文件格式说明
  3. 蓝桥杯打卡(220118)
  4. 语音会议服务器设置,VCX语音会议服务器系统提示音录音方法
  5. Qt Model/View教程
  6. python实现煲机脚本
  7. 隔年增长的题_如何从资料分析中的隔年增长率?
  8. proxmox VE minimal 配置openbox自启动
  9. 纸张开页的尺寸对比(图)
  10. android天气预报项目总结报告,Android项目:天气预报App