人类怎么会设计出SDRAM这种复杂到爆炸的玩意啊TAT,上帝也是个老混蛋好好的晶体管漏什么电啊。

动态随机存取存储器Dynamic Random Access MemoryDRAM)是一种半导体存储器,主要的作用原理是利用电容内存储电荷的多寡来代表一个二进制比特(bit)是1还是0。由于在现实中晶体管会有漏电电流的现象,导致电容上所存储的电荷数量并不足以正确的判别数据,而导致数据毁损。因此对于DRAM来说,周期性地充电是一个无可避免的要件。由于这种需要定时刷新的特性,因此被称为“动态”存储器。相对来说,静态存储器(SRAM)只要存入数据后,纵使不刷新也不会丢失记忆。

—— 中文Wikipedia,DRAM页面

同步动态随机存取內存(synchronous dynamic random-access memory,简称SDRAM)是有一个同步接口的动态随机存取內存(DRAM)。通常DRAM是有一个异步接口的,这样它可以随时响应控制输入的变化。而SDRAM有一个同步接口,在响应控制输入前会等待一个时钟信号,这样就能和计算机的系统总线同步。时钟被用来驱动一个有限状态机,对进入的指令进行管线)(Pipeline)操作。这使得SDRAM与没有同步接口的异步DRAM(asynchronous DRAM)相比,可以有一个更复杂的操作模式。

—— 中文Wikipedia,SDRAM页面

你这样说谁懂啊

简单来说,DRAM就是一种用电容里有没有电来表示这一位是0还是1的存储器,而SDRAM在普通DRAM的基础上加了一些外围电路,使得对DRAM内数据的操作仅在时钟信号到来时才会进行,这样便于和总线进行同步等1

内部结构

这里以我的开发板上的H57V2562GTR-75C(根据数据手册,这款芯片有4个bank,bank内的列地址有9位,行地址有13位,每个存储单元为16位)为例,其结构如图:

diagram

我们主要需要关系的就是这个有效地址,从图中可以发现,这块内存的存储阵列是由4个Bank组成的,其中每个Bank内部大概是这样的2

Group

其中每个存储单元内有163个单独的由电容构成的存储体,故每个存储单元内可以存储16个位。

所以一个有效地址4构成如下:

内容 Bank选择 行选择 列选择
位数 2 13 9

共24位,这样一个地址确定了内存中的16位数据。

这样可以容易地算出,这一内存芯片的大小总计为 $2 ^ {24} \times 16 = 268435456$ 位,合33554432​字节,即32MiB。

引脚说明

54_TSOPII封装方式为例,忽略(往往板子上已经帮你焊好的)供电引脚:

引脚 类型 说明
CLK input 时钟信号
CKE input 时钟使能信号,控制SDRAM内部时钟是否运行,为0时,根据其他连线SDRAM会进入断电、挂起或自刷新状态
NCS input 片选信号
BA0、BA1 input Bank地址
A0-A12 input 地址线,行列分时复用,行地址使用全部13根线,列地址只使用0-8号,同时A10被用作自动预充电是否选中全部bank的flag。
NRAS,NCAS,NWE input 指定了要做的操作
LDQM,UDQM inout 是否要Mask掉一半的输入输出(16位中的8位)
DQ0-15 inout 数据线

SDRAM初始化时序

sdram_init_timming

光看时序图可(肯)能(定)不好理解,我们还是像软件算法那样一步步写下来比较好,记得所有指令都在CLK上升沿时被执行:

  1. 在芯片上电并为其输入稳定的时钟信号后,等待(即不断发送NOP指令,{NRAS, NCAS, NWE} = 'b111)至少100µs

  2. 发送precharge指令(即{NRAS, NCAS, NWE} = 'b010,同时A10 = 1来选中全部bank)。

  3. 等待precharge完成,等待时长为$t_{RP}$,在我这块片子上至少是20ns。

  4. 发送auto refresh指令(即{NRAS, NCAS, NWE} = 'b001),并等待其执行完成,等待时长为$t_{RC}$,在我的片子上至少是63ns。

  5. 重复步骤4至少一次。

  6. 发送load mode register指令(即{NRAS, NCAS, NWE} = 'b000),mode register的值由地址线(含Bank地址线)给出:

    mode_register

    可见

    • A9线上的值代表了写入的模式(突发写还是单写)
    • {A6, A5, A4}线上的值代表了CAS延迟(CL),即读操作的列地址写入RAM控制器后,读到的值在几个时钟周期后才会出现在输出线上
    • A3设置突发类型,连续型还是非连续型
    • {A2, A1, A0}线上的值代表突发读写时的长度

    需要注意的是,“突发”并非指从输入的地址开始向后连续读写多个存储单元的值,而是:

    • 开始时将每个行分成长为突发长度的多块

    • 进行读写时从输入的地址开始读写,地址在当前的块内进行“环形递增(连续突发的情况)”

      burst

  7. 等待load mode register完成,等待时长为$t_{MRD}$,在我这块片子上至少是2个时钟周期(即发送一个NOP)。

  8. 初始化完成,可以开始用了。

SDRAM写入时序

write-without_autoprecharge

  1. 发送active指令({NRAS, NCAS, NWE} = 'b011),同时ABA线上送入BANK和相应行的地址,来激活对应的行。
  2. 等待$t_{RCD}$(发送NOP),在我的片子上是20ns。
  3. 发送write指令({NRAS, NCAS, NWE} = 'b100),同时ABA线上送入BANK和相应行地址,A10 = 0DQ线上送入第一个16位要写入的数据。
  4. 【根据突发长度重复几次】指令线送入NOP,DQ线上送入第n个要写入的数据。
  5. 如果从active指令发出到现在,时间未满$t_{RAS}$,本片为$42ns$,则用NOP补足。
  6. 发送precharge指令(即{NRAS, NCAS, NWE} = 'b010
  7. 等待precharge完成,等待时间为$t_{RP}$,在我的片子上是20ns。
  8. 写入完成

SDRAM读取时序

read_without_autoprecharge

  1. 发送active指令({NRAS, NCAS, NWE} = 'b011),同时ABA线上送入BANK和相应行的地址,来激活对应的行。
  2. 等待$t_{RCD}$(发送NOP),在我的片子上是20ns。
  3. 发送read指令({NRAS, NCAS, NWE} = 'b101),同时ABA线上送入BANK和相应行地址,A10 = 0
  4. read后$CL$个时钟周期后,读取的几个数据依次出现在DQ上(每个时钟上升沿出现一个)
  5. 如果从active指令发出到现在,时间未满$t_{RAS}$,本片为$42ns$,则用NOP补足。
  6. 发送precharge指令(即{NRAS, NCAS, NWE} = 'b010
  7. 等待precharge完成,等待时间为$t_{RP}$,在我的片子上是20ns。
  8. 读取完成

SDRAM刷新时序

auto_refresh

直接在其他命令完成后(即最后的precharge完全完成后)发送auto refresh{NRAS, NCAS, NWE} = 'b001,然后等待$t_{RC}$(我的片子上至少是15ns)即可。

SDRAM内部电容保存数据的最长时间是64ms。一次刷新可以刷新每个bank的一行。因此两次刷新之间的时间间隔不应超过$64ms / 每bank行数$,在我的片子上这个数值是$64ms / 8192 = 7.8125\mu s$,考虑到时钟精度问题,$7.68\mu s$(1024个时钟周期)刷新一次为宜。

所以,整个SDRAM的操作就可以建模为一个状态机,初始化完成后,进入等待状态,当需要刷新时就进行刷新,在读写请求到来时,如果没有刷新的任务,就进入读写状态,否则进行一次突发读写,然后再判断是否需要刷新或进一步读写。

1. 然而我觉得并不方便TAT
2. 很多资料上画的储存单元都是正方形的,这给人一种“一个储存单元对应一个电容,也对应一个位”的错误暗示,此处把单元画成长方形,希望可以让读者更容易地看出存储单元是有宽度的,对应了多个位。
3. 随芯片型号而定
4. 如果要求按字节编址,则要再增加一位,来判断是读写当前内存16位中的高8位还是低8位,即向RAM控制器发送的地址为25位。