1 DRAM背景简介
DRAM,全称为 Dynamic Random Access Memory ,中文名是“动态随机存取存储器”。所谓“动态”是和“静态”相对应的,芯片世界里还有一种 SRAM 静态随机存取存储器的存在。
笼统地说,DRAM 的结构比 SRAM 更简单,面积占用更小,适合制作大容量的存储芯片;而 SRAM 结构复杂一些,一般使用六个晶体管,面积消耗大,但是读写速度快,而且因为 SRAM 只用到晶体管,所以在工艺上和逻辑芯片相兼容,我们可以在逻辑芯片上直接集成 SRAM 。
因为 DRAM 结构简单、面积消耗小,所以一般用 DRAM 制作逻辑芯片外的大容量存储芯片,比如内存芯片。如果学过计算机组成或微机原理的相关内容,大家一定知道内存芯片和 CPU 是分离的,CPU 通过总线访问内存芯片,从内存芯片中读取数据,而这个内存芯片就是用 DRAM 制成的。
2 DRAM基本单元(Cell)
所谓 DRAM ,是指上图所示的一个电路,为了和 DRAM 芯片相区分,把图一的电路称作一个 cell 。图中的 CMOS 晶体管涉及到数字电路的知识。简单来说,CMOS 晶体管是一个电子开关,当给晶体管最上面的一端(称作栅极)加上电压或是取消电压,晶体管两端就可以流过电流。cell 中的小电容是存储信息的关键,小电容可以存储电荷,现在规定当电容存有电荷,cell存储比特信息“ 1 ”;当电容不存有电荷,存储比特信息“ 0 ”。
当要读取 cell 的存储值,首先打开电子开关(即晶体管),然后根据导通后的电容是否会进行充放电信息获得存储值。如果 cell 保存“ 1 ”,即电容存有电荷,那么当打开开关,电容就会放电;如果 cell 保存“ 0 ”,即电容不保存电荷,那么打开开关之后电容不会放电。
当要向 cell 中写入值,仍然先打开电子开关,然后在电子开关的另一侧施加电压。如果要写入“ 1 ”,则施加高电压,此时电流会通过晶体管向电容充电;如果要写“ 0 ”,则让电子开关另一端接地。施加电压一段时间后即可断开开关,此时 cell 已经保存好写入值,因为电容很小,所以施加电压的时间会很短。
2.1 Cell阵列
一个 cell 只能存储一比特信息,即“ 0 ”和“ 1 ”,为了存储大量信息,需要构建起 cell 阵列。cell 阵列的视觉图如上图。可以看到每行 cell 的晶体管的栅极都是连在一起的,即都连在字线上,这意味着给字线施加电压,字线对应的一行cell都会被打开。当一行 cell 被打开,cell 电容就会向位线充放电,一行中的每个 cell 都与一条位线直接相连,读取位线的电压变化,即可知道 cell 的存储信息。
- word line:字线(用来控制读取哪一个字,一个字是有若干个字节组成,由若干*8个位组成)。之所以叫字线,是因为给这根线通电,一行 cell 都会被打开,在计算机里八位等于一个字节,多个字节等于一个字,因此多个 cell 组合起来就是多个字,因为这根线可以打开多个字,所以叫字线
- bit line:位线。之所以叫位线,是因为在读取信息时,每一根线上的电压波动都代表一位比特信息,一根线代表一位,所以叫做位线
2.2 Cell阵列的读取
cell 的读取依靠小电容充放电,电容充放电导致位线产生电压波动,通过读取位线电压波动即可获取信息。小电容充放电所产生的电压波动是很微弱的,充放电所造成的电压波动的时间也是很短的,因此很难直接读取充放电信息,为此 cell 阵列的读取使用到了“ sense amplifier ”,即读出放大器。
读出放大器可以捕捉到微弱的电压波动,并根据电压波动的情况在本地还原出 cell 的电容电压,而且放大器内还有锁存器,可以把还原出来的电容电压值保存起来,这样一来 cell 保存的信息就从 cell 电容转移到了放大器本地。
每条位线都要接到一个放大器中,效果图如上。在读取 cell 行(之后也称作单元行)前,需要把每根位线都 precharge(预充电)到电容电压/供电电压最大值的一半,如果供电电压是 3 V,那么就预充电到 1.5 V。预充电完毕后打开字线,单元行中每个 cell 电容或是向位线放电,或是由位线充电。放电者位线电压上升一点,充电者位线电压下降一点。放大器可以捕捉位线上的电压波动,继而在本地还原、暂存对应 cell 电压。
在这里插入一些自己个人的理解:
- 如果存储值为0,没有电荷,预充到1.5V后,即使电容两段电压略高于0V,也一定进行充电。
- 如果存储值为1,存在电荷,预充到1.5V后,即使电容两段电压略低于3V,也一定进行放电。
2.3 Cell的感应放大器电路、暂存电压写回
在 DRAM 芯片中,读出放大器会把 cell 阵列分成了两半。分成两半的效果图下图。
Cell中的感应放大器有着如下的功能:
第一个功能是感测,当cell晶体管开关打开导通后,其中存储电容向位线充电,或者位线向存储电容充电时,位线上发生的电压的微小变化。感测放大器将该位线上的电压与另一根单独的位线上提供的参考电压进行比较,并将电压差放大到极限,使得存储值可以被解析为数字1或0,这是读出感应放大器在DRAM中的主要作用。
在这里插入一些个人的理解:有关于放大器是如何捕捉微弱电压波动并还原 cell 电容电压的。实际上放大器涉及到模拟电路的知识。
问题的提出:为什么要把 cell 阵列分成两半?如何更加深刻的理解放大器的实际价值?如何获取微小的电流电压,从而实现数据的稳定放大?
首先介绍一下理想放大器,在此不妨将该读出放大器理解为理想放大器,理想放大器具有如下特征:
- 放大倍率无限大
- 输入阻抗无限大,可以理解为输入电流为0
- 输出阻抗为0
- 无限大的共模抑制比(仅对两个输入端电压的差值有反应,亦即只放大有差异的部份(即差模信号)。对于两输入信号的相同的部分(即共模信号)将完全抑制
因为放大器输入端需要同时接入两根位线,仅仅捕捉两条线路上的不同电压。一般均为输入信号和地进行比较进行放大。
但是DRAM 芯片用到的放大器将两个Cell的信息进行差分比较,是“差分感测放大器”,它在放大信号波动时需要用一个基准和待测线作“比较”,此时接到放大器上的两条位线的其中一条就是所谓的基准,这条基准线经过预充电之后,其电压恒等于供电电压的一半。
第二个功能是读出放大器还充当临时数据存储元件。也就是说,在存储单元中包含的数据值被感测和放大之后,感测放大器将继续驱动保持感测的数据值,直到DRAM阵列被预充电并准备好进行另一次存取。以这种方式,可以从感测放大器访问这一行单元中的数据,而无需对单元本身进行重复的行激活。在此可以理解为:针对于这个过程中,这一堆感测放大器形成的阵列充当了缓存整行数据的行缓冲器,将信号放大并有效的保存在自己的设备中。因此,感测放大器的阵列也被称为行缓冲器,并且设计管理策略来控制感测放大器。不同的行缓冲器管理策略规定了读出放大器阵列是将数据保留一段不确定的时间(直到下一次刷新),还是在数据被恢复到存储单元后立即将其放电。
第三个功能是恢复(或刷新),主要实现当位线上的电压被感测和放大保存完存储值之后,恢复之前存储单元的值。在读取单元行时,读取行的 cell 电容,存1的电容放电之后,原本的信息就丢失了,即原来有的电荷现在放掉了;而原来存0的电容,本来没有电荷,现在却有了电荷。这种会造成信息丢失的读取行为被称为“破坏性读出”。可以理解为:导通Cell晶体管的动作允许存储电容与位线共享其存储的电荷。在电荷共享过程发生之后,存储单元内的电压大致等于位线上的电压,并且该电压电平不能用于另一读取操作。因此,在感测和放大操作之后,感测放大器还必须将放大的电压值恢复到存储单元。具体的恢复方式是在读取时,放大器还原并暂存了单元行每个 cell 的电容电压,因此可以在输出完毕之后再把这些暂存电压写回原单元行。根据放大器锁存的所读出来的值,把各条位线分别switch开关连接到供电电压或接到地,然后 cell 电容就会根据位线电压进行充电或放电,当 cell 电容充放电结束,就可以断开字线,断开字线也就宣告本次 DRAM 读取结束。
3 DRAM的刷新
Cell电容的电容值很小,存储电荷不多,无论是充电还是放电都很快,而先进 CMOS 工艺有“电流泄漏”问题,因此即使不打开字线,cell 电容也会缓慢损失电荷,久而久之信息就丢失了。(注意这里与上文的重新写回有本质的区别,虽然操作方法类似,但是造成原因不同)解决这个问题的办法是“刷新”电容,即根据电容的旧值重新向 cell 写入数据。因为要经常动态地刷新电容,所以 DRAM 叫做动态随机存储器。
“电流泄漏”是指即使晶体管没有打开,晶体管仍然可以通过极小的电流
刷新电容如何实现的?在谈论“破坏性读出”时说过放大器可以还原并暂存 cell 信息,并把暂存的信息写回到 cell 电容,因此刷新电容也可以借助放大器。具体做法是对于每个单元行,每过一段时间就自主地进行读取,等放大器暂存好信息后就立刻写回。关于单元行的刷新时机也很有讲究,一般每 64ms 内就要对 cell 阵列进行一次全面刷新。
4 DRAM芯片的读写
前文讨论过 cell 阵列的读取,在实际应用中,不会直接把一整行数据全部读出,因为一整行数据太多,真实世界中我们往往只需要其中一个比特位,因此这一节主要谈谈实际 DRAM 芯片中单个比特的读写过程。
4.1 必须的周围逻辑
为实现单个比特的读写,必须为 cell 阵列配备一系列周围逻辑电路。下图是一个简单的示范。
- 行地址Buf即为“字线译码模块”
- 蓝色模块即为“读出放大器”
- 读出放大器下面的模块是“多到一选择器”和“一到多分配器”的集合
- 最左边的蓝色模块依次是“行地址缓存”、“列地址缓存”。
为找到二维阵列中的某一个单元,必须给出该单元的行号/行地址和列号/列地址,行地址缓存保存一堆从地址总线上获取的行号,列地址缓存保存一堆从地址总线上获取的列号。
其中,行地址会送往“字线译码模块”。字线译码模块是一个译码器,可以把短行号译码成长的独热码,独热码会形成一串数,仅有一个为1其他全为0,仅会开启一条字线,如此可以控制打开该字线对应的单元行。单元行开启后,放大器捕捉位线上的电压波动,从而还原、暂存数据到放大器本地。
之后放大器把暂存的数据送到选择器,同时列地址也会被送到选择器,选择器根据列地址把数据中的某一位送到输出线。
输出数据之后,还要把单元行数据写回。
在上图的示范中,行地址和列地址是分别用两组总线送到 DRAM 芯片上的,这意味着 DRAM 芯片要为行地址和列地址准备两组输入口/ pin 口。而 cell 阵列越大,地址的位数就越多,当 cell 阵列很大时,准备两组输入口的代价十分昂贵,因此现代 DRAM 芯片让行地址和列地址共用一组总线,其效果图如下图。
4.2 完整的读过程
在读取 DRAM 芯片单个比特数据时:
- 读取前,首先给各条位线预充电(也称为 precharge ),即把位线电压拉高到供电电压的一半,拉高到一半的目的是和 cell 电容电压形成电压差,从而在打开单元行时产生电压波动,注意,预充电完成后,就可以断开位线与预充电电源的连接,此时位线处于悬空态,电压仍然保持为供电电压的一半
- 开始读取,首先在地址总线上输入行地址,稍后立刻置“行地址选通”(即RAS)有效,置 RAS 有效后, DRAM 芯片就把行地址缓存下来;
- 缓存好行地址之后,就把行地址送入译码模块,译码模块把行地址译码成独热码,独热码的每一位都接到对应的字线,理所当然的,独热码会把其中一条字线的电压值拉高;
- 拉高的字线所对应的单元行被打开,即单元行的晶体管导通,单元行的各个 cell 电容和位线连通。如果 cell 保存比特信息 1,即 cell 电容的电压等于供电电压,此时 cell 电容电压高于位线电压,电容放电,位线的电压稍稍上升;如果 cell 保存比特信息 0,即 cell 电容的电压等于地电压,即 0 电压,此时位线电压高于 cell 电容电压,位线向 cell 电容充电,位线电压稍稍下降;
- 放大器捕捉位线上的微弱电压波动,通过“差分感测”在本地生成并暂存 cell 电容电压。举个例子,如果 cell 电容等于供电电压,那么位线电压稍稍上升,放大器比较此位线和另一条基准线的电压,通过模拟电路的反馈来放大两者的电压差,最终在本地生成一个等于供电电压的输出电压,并用锁存器把输出电压锁存下来。同理,如果 cell 电容电压等于 0,放大器最终生成等于0的输出电压,并用锁存器把 0 电压锁存下来;
- 放大器锁存好行数据之后,把行数据送往多到一选择器;
- 多到一选择器根据列地址,把单元行中的某一位送到输出线;
- 输出之后,还需要把放大器的数据写回到单元行,即根据放大器的锁存值把位线拉高到供电电压或是 0 电压,位线向 cell 电容充放电,充放电结束之后,就可以关闭字线;
- 写回数据并关闭字线之后,连接位线和预充电电源,给位线预充电到供电电压的一半,为下一次读写做好准备。
请注意,上面的过程没有提到列地址哪里来的,实际上,在行地址被缓存下来之后,外界会把地址线上的地址从行地址转换成列地址,转换成列地址之后外界会置“列地址选通”有效,然后 DRAM 会把列地址缓存起来,等到第 6 步放大器送数据过来时,列地址缓存就把列地址送到多到一选择器,参与输出比特的选择。更清楚地说,列地址的缓存发生在第 2 步之后、第 7 步之前。
以下两张是 DRAM 芯片读过程的简略信号时序图,不完全一样,但都是正确的读过程,通过结合时序图和上面的文字,相信大家能更好地理解 DRAM 读的过程。其中第一张图中色块和色块的边界代表时钟的有效边沿,相信有数字电路基础的朋友很容易 get 到这一点。注意,RAS 和 CAS 头上有一条横线,这代表它们是低电平有效信号。
4.3 完整的写过程
写过程和读过程有很多相似之处:
- 位线预充电到供电电压的一半;
- 输入、缓存行地址,译码行地址,开通单元行,开通单元行后位线产生电压波动,放大器捕捉电压波动并还原、暂存行数据到本地;
- 输入、缓存列地址,与此同时置写使能有效,并在 Data Buffer 存进写入比特,注意,Data Buffer 在读取 DRAM 时用来暂存输出比特,而在写 DRAM 时则用来暂存写入比特;
- 把写入比特送到一到多分配器,分配器根据列地址把写入比特送到对应的放大器中,放大器根据写入比特改写本地暂存值;
- 放大器根据暂存的电压值刷新单元行,刷新完毕后断开单元行的字线;
- 刷新完毕后,重新给位线预充电,为下一次读写做好准备。
下图是一张写过程的信号时序图,结合文字和图,可以更好地理解这个过程。
4.4 时间消耗和行缓存
前面解读了 DRAM 读写一个比特的完整流程,现在详细说明读写过程的时间花费。
总的来说,读取一个比特的总体流程是:获得行号,译码行号,开启单元行,放大位线电压波动并暂存数据到放大器,获得列号并根据列号选择一位进行输出,写回数据,关闭字线,重新预充电。
而写一个比特的总体流程是:获得行号,译码行号,开启单元行,放大位线电压波动并暂存数据到放大器,获得列号并输入写入数据,根据列号把写入数据送到放大器并改写暂存值,写回数据,关闭字线,重新预充电。
在以上两个流程中,时间花费的大头是**“开启单元行”、“放大电压波动并暂存数据”**。
- “开启单元行”之所以花费时间,是因为行地址译码器需要拉高一整条字线,需要拉高单元行上所有晶体管的栅极电压,而拉高这么多的栅极电压很耗时间。因为可以把这些栅极抽象成一个一个电容,拉高电容的电压,其实就是对电容充电,是需要时间的,电容越大,所需时间越长,而单元行上的所有的栅极由于是并联的,可以整体抽象成一个很大的电容, DRAM 读写选通环节就是用一根字线给这个很大的电容充电,因此时间消耗很大。如果cell阵列设计的不合理,即单元行上的 cell 数量太多,那么“开启单元行”会变得很昂贵。
- 放大器放大电压波动并暂存数据也很消耗时间,因为放大器大部分是模拟电路,工作速度不快。
通过上面的分析,我们可以推导出一个结论:在读写 DRAM 时,最好不要频繁地开启新单元行和使用放大器。由这个问题出发,回想之前设计的细节。
关键在于放大器的缓存区。前面总在说数据会被缓存到放大器本地,用术语来说,这个本地缓存叫做 row buffer ,即“行缓存”。虽然前面说的读写过程都是针对一个比特的,读写一个比特需要把一行数据全部读下来,并在操作完毕之后写回单元行,这种行为看上去很浪费资源。
但是这么做是避免每次读取一个比特都需要都要经历“开启单元行、放大、读数写数、写回”的全流程,这太浪费时间,为节省时间一次读取一行数据,同时尽可能的利用这么多数据,具体方法就是不要每读写完一个比特就把 row buffer 里的数据写回,而是先保持 row buffer 数据,等待后续指令,如果后续指令还要读写这一行的数据,那么就可以直接操作 row buffer ,而不需要开启单元行并抓取数据。下面两张图依次是“读一个比特就写回”和“保持 row buffer 并连续读一行中的多个比特”的时序图,显然第二个办法效率更高。
了解到上面一段的内容之后,自然而然得出总结:在允许 row buffer 长时间保持行数据的情况下,如果读写请求发生在 row buffer 保存的单元行中(这种情况称为“行命中”),那么 DRAM 的读写速度会很快,因为 DRAM 可以直接操作 row buffer ,而不需要读取新的单元行;而如果读写请求发生在 row buffer 之外的单元行中(这种情况称为“行缺失”),那么 DRAM 就要把 row buffer 写回并读取新的单元行,这样做速度会很慢。
5 DRAM系统层次
CPU 在读写数据的时候都是面向“字”的,而一个 cell 阵列一次只能读取一个比特,本部分针对现实世界中的存储芯片实际上是如何向 CPU 提供字进行说明。
5.1 bank划分
一个 cell 阵列一次可以提供一个比特,那么多个cell阵列就可以一次提供多个比特。假如CPU一次读写8个比特,那么就可以用 8 个 cell 阵列。查找cell阵列中的一个单元需要有其行号和列号,那CPU是否需要给8个cell阵列提供 8 组地址呢?不需要,8 个 cell 阵列可以共享一组行地址和列地址。共享行、列地址的一组 cell 阵列被称作一个 bank,下图展示了一个含有 8 个 cell 阵列的 bank 。它们共用行地址、列地址和地址选通、写使能,每个阵列提供一条输出线,8 个阵列最终组成 8 根输出线,可以输出 8 个比特。
5.2 存储芯片/chip
一个 8 阵列的bank 一次读写 8 个比特,一颗存储芯片上一般含有多个 bank,下图是一颗含有 8 个 bank 的存储芯片的示意图。芯片每次读写都只针对一个 bank ,因此读写地址必须包含一个 bank 号,bank 号用于开启目标 bank,目标 bank 之外的 bank 是不工作的。
5.3 rank和DIMM
拆过电脑的朋友知道电脑用的内存芯片都嵌在一个电路板上,把这个电路板插入内存插槽后,就可增加电脑内存。电路板加板上的芯片,这就是所谓的内存条,也称为 DIMM 条(全称 Dual-Inline-Memory-Modules ,中文名叫双列直插式存储模块)。内存条通过“内存通道”连接到内存控制器,一组可以被一个内存通道同时访问的芯片称作一个 rank 。一个 rank 中的每个芯片都共用内存通道提供的地址线、控制线和数据线,同时每个芯片都提供一组输出线,这些输出线组合起来就是内存条的输出线。
下图是一个包含 8 颗芯片的 DIMM 条。这 8 颗芯片被一个内存通道同时访问,所以它们合称为一个 rank 。有的 DIMM 条有两面,即两面都有内存芯片,这种 DIMM 条拥有两个 rank 。
假设上图中的每个芯片都包含 8 个bank,每个 bank 都包含 8 个阵列,那么这条内存条就可以一次读写 8×8=64 比特,其中第一个 8 是指每个芯片输出 8 位(即为每一个bank是由8个阵列构成的可以一次输出8bit),第二个 8 是指这个 rank 总共有 8 颗芯片(即为上图的8个黑方块,每一个芯片都可以输出8bit),因为这 8 颗芯片被同一个内存通道访问,所以其被访问的 bank 和 bank 内的行地址、列地址都是完全一致的。下图是一个描述这个过程的简图:显然,我们在读写 8 颗芯片同一个 bank 同一个位置的 cell ,注意,图中没有显示不在工作状态的 bank。
电脑有时候可以插入多个内存条,多个内存条有助于提升电脑的内存容量,但是未必能提高电脑的速度。电脑的速度受“内存通道”数限制,如果电脑有四个插槽,却只有一个内存通道,那么 CPU 仍然只能一次访问一个 rank ;但如果电脑有四个插槽的同时还有四个内存通道,那么 CPU 就可以一次访问四个 rank ,很显然,四并行访问明显比串行访问快,假设每个 rank 可以输出 64 比特,那么四通道就可以一次访问 4×64=256 比特,而单通道只能访问 64 比特。
平常听到的所谓x通道内存就是指电脑有 x 个内存通道,很显然,这个 x 越大越好,不过仅有通道也不够,还得为通道提供 rank ,即为电脑插上足够多的内存条。我们可以打开电脑的“任务管理器”查看自己电脑的内存插槽数。
参考文献
位、字节、字之间的关系_位,字节,字三者的关系-CSDN博客
理想运算放大器_百度百科
【DRAM存储器二】Sense Amplifier_highman110的博客-CSDN博客
深入内存/主存:解剖DRAM存储器 - 知乎
DRAM原理_哔哩哔哩_bilibili