基于FPGA 的SDRAM控制器

发布时间 2023-09-23 16:13:51作者: 13tree

SDRAM基本信息

储存能力计算

  • 4X16X4=256(Mbit),注意不是MByte

SDRAM控制

sdram包含两个部分:sdram_ctrl、fifo_ctrl。

  • sdram_ctrl:其顶层为SDRAM的控制模块内部实例化了5个模块,有初始化、自刷新、写和读模块,还有一个仲裁模块对这四个不同操作进行分配;
  • fifo_ctrl:其顶层为SDRAM的数据输入输出,内部实例化了两个用于连接SDRAM输入和输出端的FIFO,以及控制程序

sdram_ctrl模块内部例化了四个模块

初始化模块sdram_init

  • SDRAM在上电后首先需要执行初始化操作,才可执行其他操作。

  • 配置寄存器模式

  • 潜伏期为三个周期

  • 突发长度为页突发

  • 突发类型为顺序类型

  • 写模式为页突发写模式

  • 信号操作:

    1. 正常加载时钟信号:设置CKE时钟使能为高电平CKE = 1
    2. 等待Vdd和CLK信号稳定:等待T= 200us
    3. 进行预充电操作,对所有L-Bank进行预充电:cmd = PRECHARGE,A10 = 1
    4. 等待预充电操作:cmd = NOP,TRP =20ns
    5. 自动刷新命令:cmd = AUTO REFRESH
    6. 等待自动刷新:cmd = NOP,TRFC = 70ns
    7. 重复多次自动刷新操作:重复8次
    8. 配置模式寄存器:cmd = LOAD MODE REGISTER,address= ,A10 = ,bank = 0
    9. 等待配置模式寄存器完成,TMRD = 30ns
  • 总体采用状态机来设计,分为以下几个状态:

    1. 初始化
    2. 预充电
    3. 预充电等待
    4. 自动刷新
    5. 自动刷新等待
    6. 配置模式寄存器
    7. 配置模式寄存器等待
    8. 初始化完成
  • 为了防止亚稳态的产生,状态机编码均使用格雷码

  • 跳转条件使用计数器进行计数完成判断

    • cnt_clk,等待状态的计数器
    • cnt_clk_rst,负责等待计数器的清零操作
  • 另外为了跳转操作代码简洁,声明组合逻辑的信号完成各个状态的跳转

    • trp_end
    • trc_end
    • tmrd_end
  • 由于自动刷新信号需要重复进行,因此使用计数器完成自动刷新操作计数

    • cnt_init_aref,计数自动刷新操作的重复个数
  • 最终按照各个状态完成输出信号的幅值,注意使用的为时序逻辑,因此输出相比状态跳转延迟一拍

自动刷新模块sdram_a_ref

  • SDRAM刷新有两种操作方式:
    • 自刷新:用于休眠模式可以低功耗的保存数据
      • 操作:CKR信号为低电平时,写入刷新指令,进入自刷新模式,当CKE信号拉高时,退出自刷新模式;
    • 自动刷新模式:由于SDRAM动态特性,使用电容来保存数据,因此需要不断对电容进行充放电,以此保证数据不丢失。本文主要讨论自动刷新。
  • 自动刷新操作需要不断的循环操作,是一个周期操作,至少需要每64ms完成一次。
  • 自动刷新时间计算
    • 存储体中电容的数据有效保存期上限是 64ms那么使用的SDRAM的地址位宽为13位,共有2^13=8192行。
    • 那么每行刷新的时间为:64ms/8192=64*1000000ns/8192=7812.5ns。
    • SDRAM的时钟为100MHz,周期为10ns,那么计数器最大值为:7812.5/10=781.25个。
    • 但是由于自动刷新操作和写读请求会产生冲突,会出现不会立即刷新的情况,因此需要保留一段时间,那么设置计数器最大值为750个,要求在7500ns中完成其中一行的刷新。
  • 信号操作
    1. 预充电操作:cmd = PRECHARGE、A10 = 1、bank = 2'b11
    2. 预充电等待:cmd = NOP,TRP = 20ns
    3. 自动刷新:cmd = PRECHARGE
    4. 自动刷新等待:cmd = NOP,TRFC = 70ns
    5. 自动刷新重复两次
  • 初始化模块为所有信号的首位,因此需要初始化信号拉高后在开始执行
    • init_end = 1时,模块开始正常工作
  • 由于是一个周期信号,那么需要一个计数器进行周期计数,并以此信号完成状态机的启动。
    • cnt_ref作为计数器,最大值经计算为750-1,并且达到最大值后清零继续计数
  • 其余信号需要在状态机跳转下进行操作:
    1. 初始
    2. 预充电
    3. 预充电等待
    4. 自动刷新
    5. 自动刷新等待
    6. 自动刷新结束
  • 另外自动刷新需要和仲裁模块进行握手,当仲裁模块同意进行刷新操作时,才可进行该操作
    • 当内部计数器cnt_ref达到最大值时,此时需要自动刷新,拉高自动刷洗请求信号aref_req = 1,仲裁模块接受该信号,进行判断若同意则拉高自动刷新使能信号,aref_en = 1。
    • 当自动刷新完成,拉高自动刷新结束信号,aref_end= 1,仲裁模块接受该信号,拉低自动刷新使能信号,aref_en = 0;

写操作模块sdram_write

  • 本模块使用的写操作模式为:不自动进行预充电的页突发写模式
  • 信号操作
    1. 激活L-Bank特定行:
      1. cmd = ACTIVE
      2. address = 特定写入行地址
      3. bank = 特定写入bank地址
    2. 等待激活
      1. cmd = NOP
    3. 写数据命令
      1. cmd = WRITE
      2. address = 特定写入列地址
      3. DQ = 写入第一个数据
    4. 写入数据中
      1. cmd = NOP
      2. DQ = 开始写入第二个数据,每隔一个周期写入一个数据
    5. 突发终止命令
      1. cmd = BURST TERM
      2. DQ = 不写入数据
    6. 预充电命令
      1. cmd = PRECHARGE
      2. A10 = 1
      3. bank = 2'b11
    7. 预充电等待
      1. cmd = NOP
      2. TRP = 20ns
  • 当初始化模块完成初始化后才可以SDRAM才可以正常操作
    • init_end = 1,此时写模块才可以正常开始工作
  • sdram模块的写操作是由SDRAM上端的fifo模块发起请求,经过sdram仲裁器同意后开始执行写操作的
    • SDRAM仲裁器判断可以写入时拉高,wr_en = 1
  • 当写数据操作执行完毕后状态机跳转至写结束状态时,通过组合逻辑赋值将写结束信号拉高
    • wr_end = 1
  • 另外当FIFO请求向SDRAM写入数据时,SDRAM并不能直接开始写入,因此当真正开始写入数据时需要一个提前一拍的响应信号告知fifo可以输出数据
    • wr_ack = 1
    • 相比于写数据的同步信号需要提前一拍,作为FIFO的读出数据的读请求信号
  • 其余信号在状态机下完成
    1. 初始状态
    2. 激活命令
    3. 激活等待
    4. 写命令
    5. 写等待
    6. 预充电
    7. 预充电等待
    8. 写结束
  • 写入数据的信息由外部信号传入,具体为输入端的FIFO传入
    • [23:0] wr_addr:写入数据的地址,可以分割为:2bit bank地址、13bit 行地址、9bit 列地址
    • [15:0] wr_data,写入数据
    • [9:0] wr_burst_len,写入数据个数
  • 除了常规控制信号输出外还有一个,控制SDRAM数据总线输出使能信号,该信号和真正写入sdram的数据同步。

读操作模块 sdram_read

  • SDRAM数据读方式为:不带自动预充电的页突发读模式
  • 信号操作
    1. 激活L-Bank的特定行
      1. cmd = ACTIVe
      2. address = Row
      3. bank = 2‘b11
    2. 等待激活
      1. CMD = NOP
      2. TRCD =
    3. 读命令
      1. CMD = READ
      2. address = column
    4. 读出数据
      1. cmd = NOP
      2. 潜伏期设置为3,因此在写入读命令后的间隔3个周期,会有读数据的输出
    5. 突发终止
      1. cmd = BURST TERM
  • 读数据操作需要在初始化操作完成后进行
    • 当init_end = 1 时,sdram_read进入正常工作模式
  • sdram读数据操作是由外部信号发起的,具体而言是SDRAM下端FIFO发出的读请求信号,并且该信号需要经过仲裁器判断后生效,仲裁器拉高读使能信号
    • rd_en = 1,读模块开始进行读操作
  • 当sdram_read读操作完成后,状态机进入结束状态,使用组合逻辑同步拉高读结束信号
    • rd_end = 1
  • 由于数据需要存入下端FIFO模块因此需要输出一个读模块工作响应信号,并且需要与数据同步,作为FIFO输入的写请求信号
    • rd_ack = 1,需要和输出数据同步
  • 其余信号在状态机下完成
    1. 空闲状态
    2. 激活
    3. 激活等待
    4. 读命令
    5. 潜伏期
    6. 读数据
    7. 预充电
    8. 预充电等待
    9. 读结束
  • 并且为了尽可能的满足建立时间,SDRAM真正的驱动信号相比SDRAM的控制信号有-75°的相移,因此为了将SDRAM的输入数据纳入本时钟域,需要对SDRAM的输出数据进行打拍操作,因此看起来并不会有一拍的延迟,因为SDRAM的输出本身就是提前了一些。
    • 75°相移更多的是时序上的考量,本质上是牺牲了保持时间换取建立时间的冗余
  • 最终SDRAM读出的数据和其同步信号输出至下端的FIFO
    • rd_sdram_data
    • rd_ack

仲裁模块 sdram_arbit

  • SDRAM共有四种操作模式,且有着明确的执行 优先级要求,其中初始化最高,次之自动刷新,最后是写读操作。在实际使用中不免会遇到多个模块同时需要执行或者当前执行过程中有新的请求需要执行这两种情况,此时就需要仲裁器来保证SDRAM收到的请求正常被执行。
  • 同时优先级的判断在状态跳转条件处体现,以及在各个操作模块的使能信号判断条件处体现