简介:存储器实验是计算机组成原理课程的重要实践环节。这份来自新疆大学信息科学与工程学院的实验报告,面向计算机专业本科生和正在学习静态随机存储器(SRAM)原理的读者,清晰呈现了实验目的、原理、步骤、结果及分析。包体仅含1个docx文档,压缩包约170KB,便于对照EL-JY-II实验箱直接参考,目前已有4720人学习下载。报告围绕两片6116芯片构成的2K×8位静态RAM,讲解了地址锁存器74273、三态门74LS245在地址和数据分时传送中的作用,以及/CE、/R、/W控制线的读写时序。通过单片机键盘输入地址与数据、单脉冲触发写入/读出、观察地址灯与数据灯对应关系等环节,验证了存储器与运算器的协同工作,并提供了从写数据到读校验的完整操作流程。读者可借此快速掌握SRAM读写特性、总线传送关系和实验排错思路,为深入理解计算机存储器系统提供实践支撑。 做计算机组成原理的实验,绕不开存储器。我印象很深,第一次做存储器相关实验时,对着实验箱上的芯片和杜邦线发懵,明明每个芯片都认识,连起来却不知道信号该怎么走、地址怎么算、数据为什么读不出来。后来把“存储器与CPU怎么连接”这个问题彻底想通了,才发现后面什么 cache、虚拟存储器、多模块交叉存储,全都是同一个逻辑在延伸。
这篇博文就围绕“计算机组成原理 实验 存储器”这个主题,把实验前必须搞懂的原理、模拟器和硬件两种环境下的实操流程、还有我踩过的那些坑一次说清楚。不管是正在被实验报告折磨的本科生,还是准备期末考试、考研复习的同学,照着这个思路捋一遍,至少能少走半个月弯路。
1. 实验前先弄清这几件事:存储器与CPU到底怎么“说话”
1.1 地址线、数据线、控制线:三组线各管什么
任何存储器芯片和CPU连接,核心就三组线:地址线、数据线和控制线。CPU想从某个存储单元读数据,先把地址放到地址线上;然后通过控制线发出“读”信号;存储器芯片把对应地址的数据放到数据线上;CPU从数据线上取走。写操作流程相反,CPU把地址放好、数据放好,再拉一个“写”信号,数据就被锁进对应单元了。
这个过程中有个容易忽略的点:地址线和数据线的数量并不代表芯片实际引脚数。比如常见的6264 SRAM芯片是8K×8位,有13根地址线(A0~A12)、8根数据线(D0~D7),但芯片引脚总数远不止这些,还有片选CE、写使能WE、输出使能OE、电源和地等。很多实验箱上标注的“地址总线”“数据总线”是CPU侧的概念,和芯片引脚并不一一对应,中间隔着一层译码和缓冲。我见过不止一个同学在实验报告里把CPU地址线和芯片引脚直接划等号,结果地址计算全乱套。
理解这点对后面做存储器扩展很有帮助。比如要把两片8K×8的6264组成16K×8的系统,除了把两组芯片的地址线、数据线并联接到总线上,还必须用一根高位地址线区分“访问哪一片”——这时候就要用译码器产生片选信号,选中的芯片才响应读写操作。
1.2 为什么说译码器是存储器的“门卫”
译码器解决的核心问题就一个:CPU发出一个地址,怎么知道该让哪片存储器“开门”。拿两片8K存储器举例,一共需要 16K 个地址,也就是14根地址线。低13根地址线 A0~A12 同时接到两片芯片上,访问哪一个单元由它们决定;最高位 A13 则接到译码器的输入端,译码器输出两个片选信号,分别控制两片芯片。A13=0 时选中第一片,地址范围 0000H~1FFFH;A13=1 时选中第二片,地址范围 2000H~3FFFH。
这里涉及的“存储器与CPU的连接”操作,本质就是一个地址映射的过程。做实验时用74LS138三八译码器最典型,它能把3位地址译成8路片选信号,扩展能力很强。用74LS138还有一个好处是它的输出是低电平有效,正好匹配很多SRAM芯片低电平有效的片选引脚 CE\,不用额外加反相器。
我在实际连线时吃过一次亏:译码器的使能端 G1、G2A、G2B 必须接对。G1接高电平,G2A、G2B接低电平,芯片才工作。如果忽略使能端,译码器输出始终无效,后续所有片选信号都拉不起来,现象就是整个存储器“完全没反应”。这种问题不是逻辑错误,纯属硬件细节,但排查起来特别费时间。
1.3 容量和地址线数量之间的关系要算明白
存储器容量和地址线数量的关系是实验报告必写的内容:地址线n根,可寻址 2^n 个字;数据线m根,每次读写m位。所以常说“容量 = 字数 × 字长”,比如 32K×8、64K×16 这类表达,前面代表有多少个存储单元,后面代表每个单元多少位。
具体算一下:假设CPU地址总线16位,数据总线8位,直接寻址空间就是 64K×8。但实验里往往不会用满这64K空间,而是划分给不同芯片。一片 32K×8 的SRAM需要 15 根地址线,在 16 位地址总线里占用 15 根低位线,最高位通过译码器选片。这时候它的地址范围可能是 0000H~7FFFH(如果最高位为0时选中),也可能是 8000H~FFFFH(如果最高位为1时选中),取决于译码器怎么接。
做实验前先算清楚每片芯片的地址范围,再对照实验箱的连线图检查一遍,能避免大量无效调试。另外,若实验涉及“单体多字”或“多模块存储器”,思路也一样,只是数据线宽度或存储模块数量不同,核心还是“地址分解 + 片选控制”。
2. 实验环境准备:MARS模拟器和硬件实验箱并行的双线方案
2.1 为什么我推荐用MARS做存储器实验的前半程
不少学校的计算机组成原理课设会用“北航计算机组成原理课程设计-2021秋 preproject-mips-mars”这类环节,先让学生在MARS模拟器里跑MIPS汇编,熟悉指令和内存读写,再上实验箱做硬件。MARS是一个MIPS汇编器与模拟器,用Java编写,跨平台,图形界面直观,非常适合做存储器读写实验前的逻辑验证。
推荐MARS的原因有三个:第一,它能单步执行,每条指令执行后寄存器、内存内容一目了然,方便理解 lw/sw 指令到底怎么操作存储器。第二,它可以查看数据段和堆栈段的地址布局,帮助你建立“内存地址”和“变量”之间的对应关系。第三,它支持一些系统调用,比如打印整数、结束程序等,能快速验证程序的读写结果是否正确,而不用关心硬件故障。
对软件方向的学生来说,MARS还能帮助理解为什么“学软件的要学计算机组成原理”。写高级语言时你只需要声明变量,但变量的本质是一段内存地址;指针为什么有值有址?数组访问为什么可能越界?这些问题的底层逻辑都在存储器寻址这一课里。
2.2 MARS的基本用法与关键设置
MARS的安装和使用比较傻瓜式,但有几个关键点需要注意。启动后第一件事是确认内存配置,在 Settings 菜单里可以设置地址布局,默认的“CompactDataAtZero”会让数据段起始地址从0x10010000开始,如果实验要求特定的内存布局,需要提前调整。
MARS支持完整的MIPS整数指令集,我常用到的存储器相关指令如下:
.data buffer: .space 16 # 保留16字节空间 val: .word 0x12345678 # 定义一个32位数据 .text main: la $t0, buffer # $t0 = buffer 的地址 lw $t1, val # $t1 = 从 val 地址读取的32位数据 sw $t1, 0($t0) # 把 $t1 的内容写入 buffer+0 地址 lw $t2, 0($t0) # 从 buffer+0 地址读回数据到 $t2 move $a0, $t2 # 准备打印参数 li $v0, 34 # 系统调用号34,按十六进制打印 syscall li $v0, 10 syscall代码里的la是装载地址,lw和sw是访存指令最核心的两个。MARS中lw $t1, 0($t0)的含义是:以$t0的值作为基地址,加上偏移量0,从该内存地址取出4字节装入$t1。偏移量可以是负数或正数,比如lw $t2, 4($t0)就是从buffer+4这个地址读取,这在访问数组元素时特别常用。
运行后打开 Data Segment 窗口,能看到 buffer 中0x12345678被正确写入并读出。这一套流程如果能在模拟器上熟练跑通,硬件的读写逻辑基本就理解了一半。
2.3 硬件实验的芯片选型与25系列电路图解析
模拟器验证通过后,硬件实验就是对真实芯片的连线上手。很多实验箱使用老经典的SRAM(如6264、62256)和EEPROM(如28C系列),近年也有部分课程引入25系列SPI接口存储器,即“25系列存储器电路图”里常见的25LC/25AA系列。25系列只有8个引脚,采用SPI接口通信,引脚包含CS\、SCK、SI、SO等,和并行SRAM的“一根地址线对应一比特地址”完全不同。
如果是做25系列实验,重点要搞清楚SPI时序:主机通过时钟SCK驱动数据在SI线上逐位写入,或从SO线上逐位读出;每条命令都有固定的指令码,比如读命令是0x03,写命令是0x02。写操作还需要使能指令(0x06),否则写入无效。连线相对简单:把25系列芯片的CS\、SCK、SI、SO分别接到控制器的4个IO口即可。
相比之下,并行SRAM的连线更多但逻辑更直观,适合理解计算机体系结构层面的访存过程。建议根据自己的课程要求选,如果只是自己学习,优先做并行SRAM实验,效果更明显。芯片选型时注意工作电压要匹配,实验箱常见5V供电,如果选了3.3V的芯片直接接5V可能会烧坏,有些25系列芯片标注1.8V~3.6V工作范围,必须加电平转换。
3. 实操:把存储器读写流程从指令到晶体管走一遍
3.1 实验目标和流程设计
这里我以“验证SRAM读写功能+观察地址译码关系”为实验目标,设计一个典型流程:先编写一小段程序循环向一段地址空间写入递增数据,再读回校验;如果写入和读出一致,说明存储器读写功能正常,同时通过修改高位地址观察到不同片选范围的地址跳变。这个实验可以拆成三步:
- 在MARS中写好MIPS程序,实现“向指定内存写入0~15这16个数,再读回求和”。
- 把程序烧录或下载到实验板(若课程使用FPGA+软核,则需要把汇编转换成机器码加载到IP核中)。
- 根据实验箱原理图连接SRAM芯片、译码器和CPU总线,单步观察数据是否写入。
硬件实验的连线没有统一答案,因为不同实验箱的引脚定义不同。但共性步骤是:先接电源和地,再接地址线、数据线,最后接控制线(CE\、OE\、WE\),接完必须做“通断检查”——用万用表一只表笔接芯片引脚,另一只表笔接对应总线接口,确认两端连通。
3.2 关键代码与运行结果验证
下面是在MARS中编写的完整示例,它先向内存写入16个连续递增数,再累加求和并打印,用这个结果验证存储器的读写一致性。
.data buf: .space 64 # 64字节缓冲区,可存16个整数 .text main: la $t0, buf # $t0指向缓冲区首地址 li $t1, 0 # 循环变量 i = 0 write_loop: sw $t1, 0($t0) # 把 i 写入 buf[i] addi $t0, $t0, 4 # 地址向后移动4字节 addi $t1, $t1, 1 # i = i + 1 bne $t1, 16, write_loop # i != 16 则继续写 la $t0, buf # 重新指向缓冲区首地址 li $t2, 0 # sum = 0 li $t1, 0 read_loop: lw $t3, 0($t0) add $t2, $t2, $t3 # sum += buf[i] addi $t0, $t0, 4 addi $t1, $t1, 1 bne $t1, 16, read_loop move $a0, $t2 li $v0, 1 # 按十进制打印 syscall li $v0, 10 syscall程序运行完毕后输出120,也就是0到15的和。这个结果的意义在:写入阶段使用sw指令往内存写数据,读取阶段使用lw指令把同样地址的数据取回来,经过加法运算验证了数据没有被丢失或篡改。如果把这段逻辑映射到硬件上,对应的就是CPU发出地址、数据、写信号,SRAM把数据存入相应单元;再发出同样的地址和读信号,SRAM把数据放到数据线上,CPU取回。
我建议拿到实验结果后多做一步:把写入数组改成非连续模式,比如跳过4字节写一次,观察地址如何变化。这能加深对“存储单元是按字节编址的”这一概念的理解,尤其是用MARS查看实际地址时,字节地址和字访问之间的偏移关系会非常直观。
3.3 硬件联调时的操作顺序
在实验箱上联调,顺序很重要。我一般按“先静态后动态、先读写单地址后批量地址”的步骤推进。先把程序改成直接写一个固定地址,比如向 0x0000 写入0x5A,再用逻辑笔或示波器观察该地址对应的数据总线电平是否为01011010。如果不对,检查地址译码、数据总线方向、片选是否有效,而不是急着跑大程序。
用示波器抓时序最容易发现的问题是两个:一是OE\和CE\的时序关系不对,出现了读数据时片选还没有效的毛刺;二是WE\信号太窄,SRAM来不及完成写入。前者需要在程序或状态机设计里保证OE\先于CE\有效,后者需要检查写周期时间是否符合芯片手册要求。多数SRAM的手册会给出最小写脉冲宽度,比如6264需要大约 60ns,可以对照实验板的时钟周期估算是否满足。
做完单地址读写,再扩展到连续地址写入。这时候可以观察地址总线低位的变化,比如写入16个数据时,地址线A0~A3应按二进制递增。如果发现地址线不跳变或者高电平异常,优先怀疑杜邦线接触不良,这个原因在实际中占比最高。
4. 常见问题排查:踩过的坑都整理在这儿
4.1 数据写不进存储器的几个典型原因
问得最多的现象是:“我在MARS里跑程序没问题,但实验板上数据就是写不进去。”排查方向按优先级排序:先看芯片使能引脚有没有正常拉低,再看写信号WE\有没有到达芯片写入引脚,最后查数据线是否全部连通。
我用一个速查表把常见问题和原因整理出来,方便对照排查:
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| 写数据后读回全FF | OE\或CE\无效,数据根本没进芯片 | 用万用表量CE\、OE\引脚电压 |
| 写数据后读回全00 | WE\没接好,或数据线全部对地短路 | 断开电源,量数据引脚和GND间阻值 |
| 只有某些位数据不对 | 数据线松动或虚焊 | 逐个检查D0~D7对应杜邦线 |
| 地址范围一片空白 | 译码器使能端或最高位地址线接错 | 对照原理图复核译码器输入输出 |
| 程序在MARS正常但板子异常 | 地址空间映射不一致 | 确认实验箱的地址译码规则 |
4.2 仿真和硬件结果不一致时的定位思路
MARS默认的数据段地址在0x10010000,如果实验板上的存储器被映射到0x0000~0x7FFF,程序直接放到MARS里跑通并不代表地址匹配。解决办法是学习“地址重定位”的思路:汇编里用li指令把基地址直接加载为实验板对应的存储地址,比如li $t0, 0x0000,后续所有访存都用这个寄存器作为基地址。
到了硬件联调阶段,最实用的定位手法是“最小系统验证”。不要一上来就跑完整程序,先写一段只有一条store指令和一条load指令的代码,锁存一个单值,观察这个值能否正确读回。如果单值都有问题,就从最底层排查;如果单值正常,再逐步展开循环、分支等功能。这个过程虽然慢,但能减少大面积故障时无可下手的情况。
4.3 多个存储芯片级联时隐藏的坑
做存储器扩展实验时,最容易翻车的地方不是芯片本身,而是总线竞争。比如两片8K×8的SRAM并联到同一组数据总线上,如果某一片的片选信号因译码逻辑失误而同时有效,两片芯片会同时驱动数据总线,导致数据线电平冲突、读取结果随机。排查这种现象的技巧是:把程序改成只访问一个地址,然后用示波器观察数据线上是否出现了明显的双驱动毛刺。
另一个隐藏点在于高位地址线的去耦。多片级联时高位地址线往往较长,如果实验箱上电瞬间地址线状态不确定,可能造成两片同时选中。稳妥做法是在每个芯片的电源引脚旁边加0.1uF去耦电容,并把片选信号加上拉电阻,默认状态拉高不选任何芯片,等译码器稳定后再放行。
我个人在实际操作中的体会是:存储器实验的调试时间,一半花在原理理解上,另一半花在线缆和接触上。硬件实验前先用万用表把所有连接过一遍,比出问题后盲猜高效太多。如果你也卡在某个地址读不出数据的问题上,别急着怀疑芯片损坏——先确认地址线、数据线、控制线三者都在正确的电平上,再怀疑芯片本身。把这条经验记在实验报告首页,后面做cache和流水线实验时还能继续用。
本文还有配套的精品资源,点击获取