计算机组成原理学到存储器这一块,很多人第一次开始发懵:补码、ALU 就算再抽象,好歹是“纸面功夫”,而 3.2.3 节讲的“存储器与 CPU 的连接”,是要你把地址线、数据线、控制线真刀真枪地搭在一起。很多同学卡就卡在不知道该把哪根线接哪根、片选信号怎么产生、位扩展和字扩展有什么区别。我当年第一次做 RAM 扩展实验,因为看不懂 74LS138 译码器怎么接,愣是把自己绕晕了两个晚上。这篇内容按我上课和做实验的思路来写,先把存储器和 CPU 之间那几组线的角色拆清楚,再把片选、译码、地址分配从头到尾走一遍,最后补充几个我亲手踩过的坑。适合正在学计算机组成原理、准备实验考试,或者想搞明白存储扩展原理的读者。
1. 先把连接的本质想清楚:三组线,各干各的
1.1 为什么说“连接”的本质就是三线对接
存储器与 CPU 的连接,本质上就是三组总线之间的对接:CPU 要访问一个存储单元,需要告诉存储器“我要访问哪个地址”,于是有了地址总线;需要把数据读上来或者写下去,于是有了数据总线;还需要让存储器知道“读还是写、什么时候有效”,于是有了控制总线。
地址总线的方向是单向的,从 CPU 发出去,用于选择存储器内部某个存储单元。数据总线是双向的,CPU 能从上面读数据,也能往上面写数据。控制总线里常见的几根包括读信号 RD(或者 MEMR)、写信号 WR(或者 MEMW)、存储器请求信号 MREQ,以及送到存储芯片上的片选信号 CS、输出使能 OE、写使能 WE 等。注意,MREQ 一般需要参与地址译码,它的作用相当于一个“总闸”:只有当 CPU 确实在访问存储器时,译码逻辑才允许某个存储芯片被选中。
到存储芯片这一侧,芯片管脚也对应着三组:A0~An 是片内地址线,D0~Dm 是数据线,CS/CE 是片选端,OE 是输出使能端,WE 是写使能端(SRAM 类芯片常见这些。DRAM 还会多出 RAS、CAS 和行列地址复用,这一节先放一放,后面再说)。
所以“连接”这件事,不要把它想成神秘的电路设计,它就是在做三件事:地址总线怎么分配、数据总线怎么拼接、控制信号怎么转换成存储芯片需要的电平。只要抓住这三条主线,后面所有题都好办。
1.2 位扩展和字扩展,到底在扩展什么
存储系统扩展的两种基本方式,这一节历来是考试重点,也是实验必做。先说结论:位扩展解决“字长不够”的问题,字扩展解决“地址空间不够”的问题。
位扩展,形象点说就是加宽车道。假设一个存储芯片是 16K×1 位,也就是它有 16K 个单元,每个单元只有 1 位数据。但 CPU 是 8 位数据总线,一次要访问 8 位数据,怎么办?把 8 片这样的芯片并排放在一起,每片负责提供 1 位数据,CPU 的数据线 D0 接第一片的数据端,D1 接第二片的数据端,依此类推。这样 8 片合在一起,就相当于组成了一个 16K×8 的存储体。两边的地址线和控制线是并联在一起的,因为每次访问的时候,8 片芯片都要同时工作,各贡献一位。
字扩展,则是增加存储单元行数。比如 8K×8 的芯片,容量只有 8K 个字,而 CPU 的地址总线足够大,想扩展出 64K×8 的空间,那就需要 8 片 8K×8 的芯片,按“字方向”排列。地址线的低 13 位(A0~A12)同时接到 8 片芯片上,用于片内寻址;高 3 位(A13~A15)则送入译码器,译码出 8 个片选信号,同一时刻只让其中 1 片被选中。这样每一片芯片占据 8K 地址范围,8 片合起来就覆盖了完整的 64K 地址空间。
用生活化一点的例子来记:去图书馆找一本书,先要知道书架号(片选信号),再要知道书在这个书架的第几层第几格(片内地址)。字扩展解决书架数量的问题,位扩展解决一个书架上“一次能取出多宽信息”的问题。
2. 核心硬骨头:CPU 与存储芯片连接的三个关键参数
2.1 地址线的分配逻辑:“低位片内、高位片选”
很多人记不住地址线该怎么分配,归根到底是没想明白一个点:CPU 发出的地址总线其实是“一长串二进制数”,比如 16 根地址线 A15~A0,从 0000 0000 0000 0000 到 1111 1111 1111 1111,可以表示 64K 个地址。当系统里有多个存储芯片时,这串地址必须拆成两部分用:
- 低位地址线:直接接到存储芯片的片内地址端,用于在所选芯片内部定位某一个存储单元。
- 高位地址线:不直接进芯片,而是接译码器,用于产生片选信号 CS,决定哪一片芯片工作。
为什么必须这么拆?因为如果所有高位地址线都接到芯片的地址端,每个芯片内部可访问的空间就会超出实际容量,导致同一个物理存储单元被多个逻辑地址看到,也就是地址重叠。反过来说,如果低位地址线不够,又无法覆盖芯片内部的全部单元,访问就会丢数据。
有一个很实用的速算口诀:芯片容量是 2^k 个字,片内地址线就需要 k 根;系统中共有 N 片进行字扩展,片选地址就需要 log2(N) 根。举例来说,8K×8 的芯片,8K=2^13,所以片内地址线是 13 根。要组成 64K×8 的系统,需要 8 片进行字扩展,那么片选信号需要 log2(8)=3 根高位地址线来译码。所以总地址线数就是 13+3=16 根,刚好对应 64K 空间的寻址需求。
2.2 数据线为何按“位”拼接
如果你拿到的是数据和 CPU 不等宽的芯片,比如 16K×4 位,要组成 64K×8 的内存,这时候就既要位扩展,又要字扩展。
先算芯片总数:总容量是 64K×8,单片容量是 16K×4。横向看,64K/16K=4,说明需要在字方向上放 4 组芯片;纵向看,8/4=2,说明每组需要 2 片做位扩展才能凑出 8 位数据。所以总片数 = 4×2 = 8 片。
连接上,每组 2 片的数据端,一片接 CPU 数据线的 D0~D3,另一片接 D4~D7,两片的地址线并联,片选端并联,控制线并联;然后 4 组之间通过高位地址线译码出来的 4 个片选信号加以区分。这个结构在考试题里非常常见,理解了“先位扩展凑宽度、再字扩展凑容量”的思路,直接按乘法分两步走,就很少出错。
2.3 控制线的接法与信号电平的坑
控制线是最容易接错的地方,因为芯片手册上给的是 OE、WE 这类管脚,而 CPU 出来的是 RD、WR,名字对不上,很多人就不敢接。
实际上在 SRAM 类芯片中,CPU 的读信号 RD 通常接存储芯片的 OE(输出使能),CPU 的写信号 WR 接存储芯片的 WE(写使能)。读操作时 RD 有效,把芯片输出缓冲打开;写操作时 WR 有效,把总线上的数据写入指定单元。如果芯片是单独的读使能和写使能分开管脚,接法就一一对应;如果芯片只有一根 WE,读时把它拉高即可。
片选端 CS 一般是低电平有效,也就是 CS=0 时芯片工作,CS=1 时芯片被选中之外,数据输出端呈高阻状态。这个细节很关键:只有当 CS 为高或者 OE 无效时,芯片的数据引脚才不会和 CPU 数据总线“抢线”。很多实验里数据总线冲突,烧东西,往往就是片选没控制好,导致多片芯片同时在数据线上输出数据。
还要留个心眼:CPU 的 MREQ 信号常常要参与译码逻辑,把它和译码器使能端接在一起,从而保证 CPU 访问 I/O 接口或者执行其他不访问存储器的操作时,存储芯片不会被误选。有些简化实验电路图不接 MREQ 也能跑,但实际系统里这是数电考卷上的高频细节。
2.4 芯片数与容量的快速速算
为了考试和实际选片方便,我习惯这样算三步:
- 第一步,字数扩展倍数 = 系统字数 / 单片字数。
- 第二步,位扩展倍数 = 系统位数 / 单片位数。
- 第三步,芯片总数 = 字数扩展倍数 × 位扩展倍数。
比如题目要组成 32K×8 的系统,手头芯片是 8K×4,先算 32K/8K=4,再算 8/4=2,总数=8 片。再比如 64K×8 的题目,芯片是 64K×1,那就是 64K/64K=1,8/1=8,总数是 8 片,全是位扩展。公式本身不复杂,难的是很多人把“乘法”和“加法”搞混,总觉得一组是乘、另一组也是乘,其实只要抓住“位扩展加宽、字扩展加长”这个几何直觉,就不会错。
3. 片选信号与译码器:74LS138 的实战使用
3.1 为什么片选不能省,三种译码方式怎么选
想一想,如果系统中挂了 8 片 8K×8 的存储芯片,所有芯片的地址线和数据线都并在一起,那么 CPU 访问某一个地址时,假如 8 片芯片全都“听见”了地址信号,全都把自己某个单元的数据放到数据总线上,总线就乱套了。片选信号就是用来保证“同一时刻只有一个芯片在输出”的关键。
常见的片选产生方式有三种:
- 线选法:用一根高位地址线直接接一片芯片的 CS 端。优点是简单,但地址空间严重不连续,浪费大,通常只适合芯片数量少的场合。
- 全译码法:所有高位地址线全部参与译码产生片选信号。每个芯片的地址范围固定且唯一,无地址重叠,是实际系统最推荐的做法。
- 部分译码法:只有一部分高位地址线参与译码,另一部分不参与。这样会省译码器,但会产生地址重叠,也就是同一个芯片被多个地址区间映射到。考试中经常让你判断哪些地址重叠,本质就是看没参与译码的那几位地址可以是 0 也可以是 1。
从工程角度,如果对地址连续性有要求,就用全译码;如果只是做简单实验,线选法也能凑合。从学习角度,一定要把全译码搞透,因为它是理解地址映射的基础。
3.2 74LS138 接线要领与一个可以照抄的实例
74LS138 是常用的 3-8 译码器:有 3 个选择输入端 C、B、A,8 个输出端 Y0~Y7。输出是低电平有效,也就是对应输出端为 0 时代表选中。要使能它,一般需要 G1 接高电平,G2A 和 G2B 接低电平。
用它做片选译码,经典接法是这样的:把高位地址线 A15、A14、A13 分别接到译码器的 C、B、A 端,把 CPU 的 MREQ 信号经过逻辑控制后接到使能端。这样当 A15A14A13=000 时,Y0 输出低电平,选中第一片;A15A14A13=001 时,Y1 输出低电平,选中第二片;依此类推。
以 8 片 8K×8 芯片组成 64K×8 存储系统为例,地址线低 13 位 A12~A0 接每一片芯片的片内地址,高 3 位 A15~A13 接 74LS138。地址分配如下表:
| 译码器输入(A15 A14 A13) | 选中的输出端 | 片选信号 | 地址范围(十六进制) |
|---|---|---|---|
| 000 | Y0 | CS0 | 0000H~1FFFH |
| 001 | Y1 | CS1 | 2000H~3FFFH |
| 010 | Y2 | CS2 | 4000H~5FFFH |
| 011 | Y3 | CS3 | 6000H~7FFFH |
| 100 | Y4 | CS4 | 8000H~9FFFH |
| 101 | Y5 | CS5 | A000H~BFFFH |
| 110 | Y6 | CS6 | C000H~DFFFH |
| 111 | Y7 | CS7 | E000H~FFFFH |
这个表不是随便排的。每一片 8K 芯片内部地址从全 0 到全 1,正好占据 8192 个地址,用十六进制表示就是 0000H 到 1FFFH;下一片紧接着从 2000H 开始,一直到 3FFFH。把这些范围背下来或者会推导,考试里的填空和判断就基本拿下了。
3.3 使能端为什么必须认真接
74LS138 的使能端往往是被忽视的重灾区。G1、G2A、G2B 如果不按要求接,译码器可能完全不工作,或者一直输出错误信号。实际接线上,比较常见的是把 G1 接 +5V,G2A 和 G2B 接地;另一种设计是把 MREQ 信号接到 G2A 上,这样只有当 CPU 发出存储器访问请求时,译码器才允许工作。后者更贴近真实 CPU 的工作方式。
我见过太多学生在实验箱上整了半天,发现哪片芯片都选不中,最后用万用表一量,G1 被接到了低电平,译码器被永久禁用。所以接线时第一件事就是用万用表或者示波器确认使能端的电平状态,再谈片选问题。
4. 完整实例:8 片 8K×8 芯片组成 64K×8 存储系统
4.1 背景设定和整体连线思路
现在我们把前面所有内容串起来做一个可以照做的完整实例。假设 CPU 的地址总线是 16 根(A15~A0),数据总线是 8 根(D7~D0),我们要用 8 片 8K×8 的 SRAM(常见的型号如 6264)组成一个 64K×8 的存储系统。
第一步,每片 SRAM 的片内地址端 A12~A0 与 CPU 的 A12~A0 一一相连,这样每片芯片内部正好有 8192 个存储单元。第二步,每片 SRAM 的数据端 D7~D0 与 CPU 的 D7~D0 一一相连。第三步,CPU 的 A15、A14、A13 送入 74LS138 的 C、B、A,译码器的 8 个输出端 Y0~Y7 分别接到第 1 片到第 8 片 SRAM 的 CS 端。第四步,CPU 的 RD 接 SRAM 的 OE,CPU 的 WR 接 SRAM 的 WE。
这个方案里,所有存储芯片的数据线和低位地址线全部并联,但片选信号彼此独立,所以同时只有一片会响应总线的访问。这种“数据线并联,片选线分开”的结构是字扩展的标准做法,也是后面理解 Cache 和主存映射关系的基础。
4.2 地址范围推导与片选信号对照
画好连接图之后,一定要亲手推导一遍地址范围。以第一片芯片 CS0 为例:A15A14A13=000,所以地址的最高 3 位固定为 0;低 13 位 A12~A0 可以是从 000 0000 0000 0 到 111 1111 1111 1 的任意值。写完整的 16 位地址,就是 0000 0000 0000 0000 到 0001 1111 1111 1111,转换成十六进制恰好是 0000H 和 1FFFH。
第二片 CS1 对应 A15A14A13=001,地址范围变成 0010 0000 0000 0000 到 0011 1111 1111 1111,即 2000H~3FFFH。后面每片间隔都是 8K 个地址,依次递增。记住一个规律:相邻两片的地址范围首尾相接、连续不重叠,这说明全译码方式下存储空间利用率高,没有任何浪费。
4.3 接线完成后的验证方法
实验做完了怎么验证系统真的能用?我个人的习惯是三步走。第一步,写测试数据:向 0000H、1000H、2000H……FFFFH 等边界地址写入 0x55 和 0xAA。第二步,再把这些地址依次读出来,和写入的数据比对,如果某一片地址范围里的数据全部出错,问题大概率出在对应 CS 那一路;如果所有数据都错,先查时钟和使能端,再查 RD/WR 控制信号。第三步,用逻辑分析仪抓 CS 引脚的波形,正常情况下只有当前访问的那一片 CS 会拉低,其余全部保持高电平。
提示:写入 0x55 和 0xAA 这种交替二进制数很有讲究,因为 0x55 是 01010101,0xAA 是 10101010,可以一次性暴露出数据线有没有接反、接错的问题。如果读回来是 0x05 或者 0x50,基本就能判断是哪根数据线有问题。
5. 常见问题与排查技巧实录
5.1 读出来的数据全是 FF,先查这三处
实验中最经典的现象是:你往某个地址写了一个数,读回来却是 FF,好像根本没写进去。遇到这种情况,我的排查顺序是固定的。
第一,查 CS 是否有效。用示波器或者万用表在写操作瞬间观察片选引脚,如果 CS 根本没拉低,说明译码器或使能端有问题,芯片压根没被选中。第二,查 WE 写使能有没有真正到达芯片。很多系统里面 WR 经过反相器或者缓冲器,中间任何一处断线都会导致写操作无效。第三,查数据线。如果 CS 和 WE 都正常但数据还是 FF,可能是数据线接触不良,也可能是芯片本身损坏。换一片同型号芯片试试,能排除很大一部分硬件问题。
5.2 地址重叠:为什么两个地址能访问到同一个单元
部分译码方式特别容易出现地址重叠。比如片选信号只用 A15 和 A14 译码,A13 不参与,那么 A13 为 0 和为 1 时,实际上会选中同一个芯片,只是地址不同而已。这意味着原本应该由 16K 空间才能容纳的存储系统,只需要 8K 容量就“覆盖”了 16K 地址,地址空间浪费了一半。
考试里经常让判断“哪些地址范围是重叠的”,做法就是把未参与译码的地址位分别取 0 和 1,列出所有可能的地址组合,凡是映射到同一个片选区域的就是重叠地址。工程上尽量用全译码避免这种问题,因为重叠地址一旦发生,软件上很容易出现“写 A 地址把 B 地址的数据改了”这种极其隐蔽的 bug。
5.3 存储芯片为什么越用越“飘”,电源和总线冲突是元凶
芯片烧毁或者数据时好时坏,很大概率不是芯片本身质量差,而是电路设计或者操作习惯有问题。
最常见的坑是上电瞬间 CS 端为低电平。系统刚上电时,译码器输入可能还是不定态,如果此时某个芯片的 CS 意外有效,同时 CPU 数据总线正好有输出,两个输出端就在总线上短兵相接,轻则数据错误,重则烧毁芯片。所以在实验系统中,译码器的使能端往往还承担着上电复位期间禁用片选的功能。另一个常见坑是电源管脚旁边缺了去耦电容,芯片高速翻转时电源纹波太大,导致逻辑电平判断错误。我们做实验时习惯每一片芯片的电源和地之间就近接一个 0.1μF 瓷片电容,效果立竿见影。
还有接线习惯的问题:一定要先断电再改线,改完线仔细核对照 一遍再上电。很多“莫名奇妙”的故障,最后查出来就是杜邦线松了,或者信号线插错了一格。
5.4 从连接到缓存,一个小延伸
存储器与 CPU 的连接,后面接着讲的往往是 Cache 和主存之间的关系,也有很多热词话题是“CPU 缓存到底怎么影响性能”。理解连接这一节,其实就理解了 Cache 出现的原因之一:主存速度跟不上 CPU,CPU 在访问主存时经常要等待,这个等待时间严重拖慢整体性能。Cache 就是插在 CPU 和主存之间的高速小容量存储器,把当前正在频繁访问的数据副本放在手边,减少 CPU 访问主存的次数。
与之相关的还有 DRAM 的连接问题。SRAM 用 6 个晶体管存 1 位数据,速度快但贵;DRAM 用 1 个晶体管加 1 个电容存 1 位数据,密度高但需要刷新,而且为了减少引脚数,地址线常常采用行、列分时复用,也就是 RAS 和 CAS 两拍分别送行地址和列地址。这比 SRAM 的“一次性送地址”复杂不少,也是 DDR 内存条看起来那么复杂的原因之一。理解了这套连接逻辑,再看内存条上的地址线、数据线、控制引脚,就不会觉得是一堆看不懂的针脚了。
我个人带过的学生里,凡是能把三组线的关系画清楚的人,后面学 Cache、学 DMA、学外设接口都顺很多。因为这一节看似只是“连线题”,实际上培养的是把 CPU 内外逻辑串起来看的思维方式。
最后再分享一个很笨但很好用的方法:做存储器连接实验之前,先在纸上把 CPU 画在左边,存储芯片画在右边,然后分别用三种颜色画出地址线、数据线、控制线。不用管画得丑不丑,只要能把每一条线的来龙去脉标清楚,再去实验箱上接线,出错的概率至少降低一半。连接的核心永远是那三件事:低位地址给片内、高位地址给片选、数据线按位拼接,控制线别接反。把这三句话记住了,3.2.3 这一节的题和实验,基本都能稳稳拿下。