计算机组成原理实验手册:从运算器到微程序控制器的硬件调试指南
2026/9/23 14:39:06 网站建设 项目流程

简介:面向计算机组成原理课程学习者的实验教学 PDF,内容包括实验系统硬件布局、安全注意事项以及完整的运算器实验指导。资料为单个 PDF 文件,压缩包约 1.63MB,下载后可直接阅读,便于课前预习或课后对照。教程以 TD-CMA 实验系统为平台,围绕基本运算器实验组织内容:先借助硬件布局图和原理图说明运算器中算术、逻辑、移位三个独立部件及 CPLD 实现方式,再结合控制信号 S3⋯S0 和 CN 解释逻辑功能表;随后给出按图接线、设置单拍/运行时序、向暂存器 A/B 置数并观察数据总线 LED 结果等具体步骤。针对实验中的总线竞争报警、CLR 清零、FC/FZ 标志锁存等现象也做了提醒,能帮助读者快速掌握实验箱操作要领与运算器工作原理。已有 243 人学习下载,适合计算机类专业本科生实验环节使用。

1. 计算机组成原理实验:一本对不上信号就会翻车的操作手册

第一次拿到 TD-CMA 实验箱时,我最怕的不是计算机组成原理实验的原理图看不懂,而是照着教材接线后一上电就听见“嘀”的报警声——那是总线竞争,意味着有多个输出在抢同一条总线。这份《计算机组成原理实验教程》最值钱的地方,恰恰就是把这类坑写在了前面:什么时候能开机、排线颜色为什么必须对应、T4 为什么是脉冲而其他信号是电平。它覆盖运算器、静态存储器、微程序控制器和系统总线四个实验,每一条实验步骤都精确到开关编号和节拍,适合正在上计组实验课的学生、带实验的助教,以及想用真实硬件理解 CPU 工作过程的爱好者。照着做一遍,比看十遍课件都管用。这不是纯科普,是一本告诉你哪里会翻车的操作手册。

2. 实验箱硬件基础:布局、接线规则与电源时序

2.1 硬件布局与排线替代规则:先看懂引脚再动手

实验箱上凡是丝印带马蹄形标记“∩”的两根排针之间是连通的,这是读图的基础。换句话说,原理图里画在一起的引脚,在箱子上可能已经内部短接,不需要再额外接线。理解这一点能避免重复接线,也能避免在查线时把同一根信号误认为漏接。

排线是另一个容易翻车的地方。实验箱随附的排线只有 2 口、4 口、6 口、8 口四种,但原理图上经常出现 1 口或 3 口的连接需求。教材给出的做法是:用 2 口线替代 1 口、4 口线替代 3 口,但连线两端的颜色必须一一对应。这里的“颜色对应”不是玄学,排线内部的每根导线颜色是唯一的,如果 A 端接了红色、B 端接了相邻位,信号就整体错位,轻则实验现象不对,重则一上电就报警。

我习惯在动线之前先把要接的信号列成清单,逐条确认后再上电。接线本身没法用软件检查,但清单可以防止漏接和错接:

#!/bin/bash # wiring_check.sh - 接线自查工具 # 用法: bash wiring_check.sh # 说明: 数组里的键是信号名, 值是信号来源, 对照教材接线图逐项确认 declare -A signals=( ["ALU_B"]="CON单元开关 (低有效)" ["LDA"]="CON单元开关 (高有效)" ["LDB"]="CON单元开关 (高有效)" ["T4"]="时序单元 TS4 (脉冲)" ["S3-S0"]="CON单元开关" ["CN"]="CON单元开关" ) for sig in "${!signals[@]}"; do read -p "${sig}: ${signals[$sig]} 已接好? (y/n): " answer if [[ "$answer" != "y" ]]; then echo "[FAIL] ${sig} 未确认, 禁止上电" exit 1 fi done echo "[OK] 所有信号已确认, 可以上电"

这个脚本的逻辑很简单:信号名和来源存在关联数组里,逐条问用户是否接好,任何一条没确认就退出并返回非零状态。实际接线时把数组内容替换成你当前实验的信号即可,比如做运算器实验时还应该把 LDA、LDB 补充进去。它的价值不是替你接线,而是把“接线→检查→上电”这个流程变成强制动作,避免断电后随手插两根线就开机的坏习惯。

2.2 脉冲信号与电平信号:T4、CLR、ST 按钮到底在做什么

第一次做运算器实验的人最容易困惑的一个问题是:为什么有的信号拨开关就有用,有的信号按了 ST 按钮才有反应?原因在于这些信号分两类——电平信号和脉冲信号。

实验箱的时序单元输出四拍时序信号 TS1、TS2、TS3、TS4。做运算器实验时,T4 直接来自时序单元的 TS4,它是一个脉冲信号,只有按下 ST 按钮时才会产生一个上升沿,暂存器 A 或 B 在这个上升沿到来时采下数据总线上当前的值。而 ALU_B、LDA、LDB、S3~S0、CN 这些是电平信号,由 CON 单元的二进制开关直接给出,拨到高就有效、拨到低就无效。

这里面有几个容易记反的细节。第一,ALU_B 是低有效,其余控制信号都是高有效,也就是说 ALU_B 必须在低电平时 ALU 才把计算结果送到总线上。第二,CLR 按钮用来清零,它同时接在 A、B 暂存器和 FC、FZ 标志上,按一下把所有状态归零。第三,ST 按钮每按一次只产生一个 T4 脉冲,连续按两次就产生两个,实验里“连续按动 ST”通常表示需要多个节拍完成一次写入。

我把典型信号整理成一张速查表,做实验前扫一眼能少走弯路:

信号来源有效电平作用
T4时序单元 TS4上升沿脉冲将数据写入暂存器 A 或 B
LDACON 开关允许写入暂存器 A
LDBCON 开关允许写入暂存器 B
ALU_BCON 开关使 ALU 输出到数据总线
S3~S0CON 开关选择 ALU 运算类型
CNCON 开关进位输入控制
CLRCON 按钮清零 A、B、FC、FZ

注意表格里 T4 的有效形式是脉冲上升沿,而不是简单的高电平。实际拨开关时,应该先把所有电平信号设置好,再按 ST 产生脉冲,顺序反了数据就写不进去。

2.3 电源时序与总线竞争:报警声是实验箱的后悔药

实验箱的电源管理规则写在注意事项的第一页,但很多人读到第三行就跳过了,直到上电报警才回头翻书。教材明确要求:接好线路并检查无误后再打开电源;插线、拔线前必须关闭电源,不要带电操作;电源关闭后不能立即重启,至少间隔 30 秒。

这三条里最容易被忽视的是最后一条。实验箱内部有大量电解电容,掉电后余电没有及时泄放,立刻上电可能让逻辑芯片处于不确定状态。30 秒看起来长,实际上是给整个系统一个可靠的复位窗口,尤其在反复改接线的场景下,抢那几秒往往换来的是更长的排错时间。

上电瞬间如果听到“嘀”的报警声,说明有总线竞争现象,也就是多个三态门同时把数据输出到同一条总线上。遇到这种情况,标准动作是立刻关闭电源,重新检查接线,直到错误排除再继续。不要带着报警声强行操作,实验箱上的 LED 显示在这种情况下完全不可信,强行记录数据只会把错误的原因引入到实验报告里。

提示:总线竞争是实验箱最常见的安全机制报警,它保护的不仅是实验箱,也是接线错误导致的数据错误。听到报警先断电,不要按 CLR 试图“清掉”它。

懂得这些硬件层面的规则,后面的运算器、存储器和微程序实验才能顺畅做下去。下一章开始进入第一个真正动手的实验。

3. 运算器与存储器实验:从功能表到可复现的读写步骤

3.1 运算器逻辑功能表怎么看:S3~S0 与 CN 的编码关系

运算器实验的核心是一张逻辑功能表,它决定了 ALU 在给定控制信号下做什么运算。运算器内部有三个独立运算部件——算术、逻辑和移位部件,它们同时接收暂存器 A 和暂存器 B 的数据,但多路选择开关在同一时刻只选择其中一个部件的结果输出到 ALU。也就是说,三个部件永远都在算,最后的输出由控制信号决定选谁。

控制信号 S3~S0 是 4 位二进制编码,CN 是进位输入。从表 1-1 可以看到,编码空间被分成三类:0000~0100 是逻辑运算(直通、与、或、非),0101~0111 是移位运算,1000~1101 是算术运算(加减、加减 1、置进位)。其中 1110 和 1111 保留,没有定义运算。

为了不每次翻 PDF,我习惯把功能表转录成一个可查询的字典脚本,做实验时直接查:

# alu_table.py - 运算器功能表查询 # 键: (S3,S2,S1,S0,CN); CN为任意态时用'X' # 值: (运算描述, 影响的标志位) alu = { (0,0,0,0,'X'): ("F=A 直通", ""), (0,0,0,1,'X'): ("F=B 直通", ""), (0,0,1,0,'X'): ("F=A AND B", "FZ"), (0,0,1,1,'X'): ("F=A OR B", "FZ"), (0,1,0,0,'X'): ("F=NOT A", "FZ"), (0,1,0,1,'X'): ("F=A 不带进位循环右移 B 位", "FZ"), (0,1,1,0,0): ("F=A 逻辑右移一位", "FZ"), (0,1,1,0,1): ("F=A 带进位循环右移一位", "FC,FZ"), (0,1,1,1,0): ("F=A 逻辑左移一位", "FZ"), (0,1,1,1,1): ("F=A 带进位循环左移一位", "FC,FZ"), (1,0,0,0,'X'): ("置 FC=CN", "FC"), (1,0,0,1,'X'): ("F=A 加 B", "FC,FZ"), (1,0,1,0,'X'): ("F=A 加 B 加 FC", "FC,FZ"), (1,0,1,1,'X'): ("F=A 减 B", "FC,FZ"), (1,1,0,0,'X'): ("F=A 减 1", "FC,FZ"), (1,1,0,1,'X'): ("F=A 加 1", "FC,FZ"), } # 查加法: S=1001, CN任意 s = (1,0,0,1,'X') print(f"S=1001 CN=X -> {alu[s][0]}, 影响标志: {alu[s][1]}")

这段脚本的关键点有两个:一是用元组直接对应实验箱上的 5 位控制信号,拨码开关的状态可以直接映射到键值;二是把“X 任意态”显式表达出来,查表时不用纠结 CN 该拨 1 还是 0。实际使用时,把要做的运算类型填进去,脚本会直接告诉你该拨什么,相当于把教材表 1-1 变成命令行工具。

做实验时还有一层容易忽略:FC 和 FZ 标志是被锁存的。如果当前运算影响进位,结果输出前会置 FC;如果是零结果,会置 FZ。锁存意味着这些标志不会随着下一次电平信号的改变立刻消失,只有 CLR 清零才会复位,观察结果时要特别留意这一点。

3.2 运算器实验步骤:置数、单拍、观察一条龙

运算器实验的完整流程可以压缩成四个动作:清状态、给 A 置数、给 B 置数、设功能观察。教材的步骤写得非常细,我这里把关键动作串一遍。

首先按图接线并检查无误,把时序与操作台单元的开关 KK2 拨到“单拍”档,KK1、KK3 拨到“运行”档。打开电源后按一下 CON 单元的 CLR 按钮,把 A、B、FC、FZ 全部清零。这一步不要省,否则上一次实验残留的数据会混进这一次的结果。

给 A 置数的操作是:拨动 CON 单元的 SD27~SD20 数据开关形成目标二进制数,把 LDA 拨到 1、LDB 拨到 0,然后连续按动时序单元的 ST 按钮产生一个 T4 上升沿,数据就进入暂存器 A。给 B 置数同理,把 LDA 改为 0、LDB 改为 1,再按一次 ST。置完数以后观察 ALU 单元的 A7~A0、B7~B0 两组 LED,确认两个暂存器里的值和你预期一致,再继续下一步。

设置功能时要把 ALU_B 拨到 0、LDA 和 LDB 都拨到 0,隔离暂存器与总线的连接,然后按功能表设置 S3~S0 和 CN 的数值,观察数据总线 LED 上的结果。这条操作序列用伪代码可以表示成:

# alu_run.py - 运算器实验操作序列 (对应 3.2 节手动流程) # 说明: 每一步都是一次电平设置 + 一次 ST 脉冲 def set_switch(sw, val): print(f"[SET] {sw} = {val}") def press_st(times=1): print(f"[PULSE] ST 按动 {times} 次, 产生 T4 上升沿") # 初始状态清零 set_switch("KK2", "单拍"); set_switch("KK1/KK3", "运行") print("[CLR] A/B/FC/FZ 已清零") # 给 A 置数: LDA=1, LDB=0, 数据开关 SD27~SD20 给出数 set_switch("SD27-SD20", "0x0F") # 示例: 给 A 置 00001111 set_switch("LDA", 1); set_switch("LDB", 0) press_st(1) # 给 B 置数: LDA=0, LDB=1 set_switch("SD27-SD20", "0xF0") # 示例: 给 B 置 11110000 set_switch("LDA", 0); set_switch("LDB", 1) press_st(1) # 设置功能: ALU_B=0, LDA=0, LDB=0, S=0010 做逻辑与 set_switch("ALU_B", 0) set_switch("LDA", 0); set_switch("LDB", 0) set_switch("S3-S0", "0010") print("[OBS] 数据总线应为 00000000, FZ=1")

这段伪代码把教材步骤拆成了逐条的开关设置指令,每一条对应实验箱上一个物理操作。参数上要注意,给 A 和 B 置数时,数据开关的最终值通过 SD27~SD20 的拨码形成,LED 亮表示 1、灭表示 0;填 0x0F 只是示例,实际值由你自己指定的数字决定。单拍档下,每按一次 ST 只走一个节拍,方便在数据通路图上逐拍观察。

这里有一个新手容易犯的错:设置功能时忘了把 ALU_B 拨到 0。ALU_B 低有效,拨到 1 时 ALU 的输出被三态门隔离,数据总线 LED 上什么也看不到,容易误判为实验箱坏了。

3.3 静态存储器实验:6116 的读写时序与地址/数据分时送入

存储器实验用的是位于 MEM 单元的一片 6116 SRAM,标称 2K×8bit,但本实验把高三位地址 A10~A8 接地,实际可用容量只有 256 字节。6116 有三根控制线:CS(片选)、OE(读使能)、WE(写使能),本实验把 CS 常接地,让它一直处于选中状态。

数据和地址如何送入是实验的核心难点。原理图显示,存储器的数据线直接接数据总线,地址线接地址锁存器 74LS273(位于 PC&AR 单元),而数据开关(位于 IN 单元)经一片 74LS245 三态门连到数据总线上。因为地址和数据共用同一组数据开关,所以必须分时操作——先送地址,再送数据。不能在同一个节拍里同时既给地址又给数据。

写存储器的操作序列是:先让存储器处于非读写状态(WR=0、RD=0),数据开关输出地址,IOR=0 打开输入三态门,置 LDAR=1 打开地址锁存器门控,按 ST 产生 T3 脉冲把地址打入地址寄存器。随后关掉地址锁存(LDAR=0),数据开关改输出要写入的数据,保持 IOR=0,把存储器置为写状态(WR=1、RD=0、IOM=0),再按一次 ST 产生 T3 脉冲,数据被打入存储器。读操作的前半段一样,只是把 IOR 拨回 1 关闭 IN 单元的输出,然后让存储器处于读状态(WR=0、RD=1、IOM=0),数据总线上的值就是当前地址读出的内容。

用伪代码表示写 00H=11H 这组操作:

# mem_write.py - 向 SRAM 00H 写入 11H 的操作序列 # 关键: 地址和数据共用一组开关, 必须分时送出 def set_switch(name, val): print(f"[SET] {name} = {val}") def pulse_st(): print("[PULSE] ST 按动 1 次, 产生 T3 脉冲") # 阶段1: 送地址 00H set_switch("WR", 0); set_switch("RD", 0) # 先关存储器读写 set_switch("IOR", 0) # 打开 IN 单元三态门 set_switch("数据开关", "00H") # 地址 00H set_switch("LDAR", 1) # 打开地址锁存 pulse_st() # T3 将地址打入 74LS273 # 阶段2: 送数据 11H (切换数据开关前先关 LDAR) set_switch("LDAR", 0) # 关闭地址锁存 set_switch("数据开关", "11H") # 数据 11H set_switch("WR", 1); set_switch("RD", 0) # 存储器写状态 set_switch("IOM", 0) # 选择 MEM 操作 pulse_st() # T3 将数据写入 6116

这段流程最重要的参数是信号切换的顺序:地址阶段必须保证 LDAR=1 且存储器不读不写,数据阶段必须保证 LDAR=0 且 IOM=0。顺序颠倒的后果是数据写到了错误的地址,或者地址被当作数据写入了存储器,这两种情况在数据通路图上都能看到明显异常。

读操作更有一个容易踩的坑:读之前要把 IOR 从 0 改回 1。IOR=1 时 IN 单元的三态门关闭,数据总线才能被存储器的输出正常驱动;如果忘了这一步,IN 单元的数据开关还在往总线上送数,总线竞争就出现了,读出来的内容自然是乱七八糟的。

3.4 联机模式:数据通路图与单节拍调试

如果实验箱和 PC 联机,整个过程会直观得多。打开联机软件,选择【实验】→【存储器实验】,会弹出数据通路图。联动操作时,每按动一次 ST 按钮,数据通路图上会有数据流动的动画,反映当前时刻是谁在往总线上写、谁在读。特别注意,即使是读操作,也需要按一次 ST,数据通路图上才会有流动,因为读使能信号必须在时序节拍里生效。

软件里的【调试】→【单节拍】菜单,作用等效于把 KK2 拨到“单步”档后按一次 ST。这个功能在做微程序实验时尤其有用,它能把每个节拍的动作冻结下来,方便仔细分析 SRAM 的读写过程。手动模式下,我一直按教材的流程先送地址再送数据,配合 LED 灯的亮灭以及数据通路图上的高亮线条,基本能定位百分之九十的问题。

4. 微程序控制器实验与常见问题排查:微指令格式、装载与踩坑记录

4.1 微指令格式:24 位怎么拆成控制信号

微程序控制器实验是整个教程里信息量最大的一个实验。微程序控制器的基本任务是把机器指令翻译成微命令序列,而微命令的集合以二进制代码形式存放在控制存储器中,这种代码就叫微指令。控制器按照时序节拍逐条取出微指令并执行,就完成了一条机器指令的功能。

本实验的控制存储器由三片 2816 E2PROM 组成,掉电后数据不丢失,这一点和普通 SRAM 完全不同。微指令字长 24 位,字段布局如下:

232221201918-1514-1211-98-65-0
字段M23M22WRRDIOMS3-S0A 字段B 字段C 字段MA5-MA0

高五位中的 M23、M22 是命令字,实际实验中通常用不到全部字段;WR、RD、IOM 控制存储器和外设的读写,S3~S0 直接送到 ALU 选择运算类型。A、B、C 三个译码字段各 3 位,分别经过 74LS138 译码扩展出多个控制信号。A 字段的 001 译出 LDA、010 译出 LDB、011 译出 LDR0、111 译出 LDIR;B 字段的 001 译出 ALU_B、010 译出 R0_B;C 字段的 001 是测试字位 P<1>,用于根据机器指令产生微程序分支。

MA5~MA0 是 6 位的后续微地址。在不判别测试的情况下,T2 时刻打入微地址寄存器的内容就是下一条微指令的地址;当 T4 时刻进行测试判别时,地址转移逻辑根据机器指令修改微地址寄存器,实现分支。理解这个机制是看懂微程序流程图的前提:每个矩形框右上角是当前微地址,右下角是后续微地址,P<1> 测试位决定流程走向哪个分支。

4.2 手动编程与校验:KK3、KK4、KK5 开关组合

微程序写入控制存储器有两种方式,手动和联机。手动方式依赖时序与操作台单元上的四组开关:KK1 控制运行与停止,KK3 选择编程、校验还是运行,KK4 选择操作对象是控存还是普通存储器,KK5 选择地址是置数、加 1 还是减 1。

以向 00H 单元写入 000001 这条微指令为例。先把 KK1 拨到“停止”档、KK3 拨到“编程”档、KK4 拨到“控存”档、KK5 拨到“置数”档。用 CON 单元的 SD05~SD00 开关给出目标地址 000000,用 IN 单元的开关给出低 8 位数据。连续按动两次 ST:第一次按动后,MC 单元低 8 位显示该单元原来的数据;第二次按动后,显示当前改写的数据。然后把 KK5 拨到“加 1”档,用 IN 单元给出中 8 位数据,同样连续按两次 ST 完成中 8 位写入;再给高 8 位,连续按两次 ST 完成写入。写完这一条,控存地址会自动加 1 到 01H,继续编辑下一条。

手动写入时最容易被绕晕的是“连续按动两次 ST”的语义。第一次 ST 是在读出原值用于显示,第二次 ST 才是真正写入。校验时同样要连续按两次 ST,才能把对应字节的内容显示出来。如果只按了一次就去看 LED,看到的是旧数据,就会误判写操作失败。

用操作序列表示:

# ucode_write.sh - 手动向控存 00H 写入 $M 00 000001 # 步骤: KK1=停止, KK3=编程, KK4=控存, KK5=置数 # 说明: 每个字节连续按 ST 两次, 第一次显示旧值, 第二次写入 echo "KK1 -> 停止; KK3 -> 编程; KK4 -> 控存; KK5 -> 置数" echo "SD05-SD00 -> 000000 (地址)" echo "IN 开关 -> 00000001 (低8位)" echo "ST x2 -> 写入低8位, 地址显示 000000" echo "KK5 -> 加1" echo "IN 开关 -> 00000000 (中8位)" echo "ST x2 -> 写入中8位" echo "IN 开关 -> 00000000 (高8位)" echo "ST x2 -> 写入高8位" echo "完成 00H 单元编辑, 地址自动变为 01H"

这段脚本本质是一个操作清单,把每个动作翻译成 shell 注释输出。真正动手时,按这个顺序逐条执行即可,任何一步的顺序错误都可能导致数据写进错误的字节位置。

4.3 联机装载与校验:TXT 格式与刷新指令区

手动往 8 条微指令要按 24 次 ST,效率很低,联机装载会快得多。联机软件支持从 TXT 文件装载微程序到实验系统,文件格式是:

$M XX XXXXXX

$M 是微指令标志,XX 是十六进制微地址,XXXXXX 是 24 位微指令值,按高 8 位、中 8 位、低 8 位的顺序排列。分号后面是注释文字,装载时会被忽略。本次实验用到的最简微代码文件如下:

; 微控器实验指令文件 $M 00 000001 ; NOP $M 01 007070 ; CON(INS)->IR, P<1> $M 04 002405 ; R0->B $M 05 04B201 ; A 加 B->R0 $M 30 001404 ; R0->A $M 32 183001 ; IN->R0 $M 33 280401 ; R0->OUT $M 35 000035 ; NOP

装载时选择联机软件的【转储】→【装载】功能,指定这个 TXT 文件,软件会把微指令逐条写入下位机。装入过程中输出区的“结果”栏会显示装载信息,包括当前装载的是机器指令还是微指令、还剩多少条。装载完成以后,用【转储】→【刷新指令区】读出下位机所有的微指令,检查每个地址单元的数据是否与表格里的十六进制值一致。

如果某一条不一致,可以用软件单独修改。先点击指令区的“微存”TAB 按钮,再单击需要修改的单元数据,该单元会变成编辑框,输入 6 位十六进制数据并回车,修改后的数据以红色显示。这个功能对应着实验报告里“如果校验出微代码写入错误,重新写入、校验”的要求,比手动改快得多。

4.4 常见问题排查:现象、原因与解决

微程序实验的故障现象高度集中在几个固定模式上,排查思路也比较统一。

现象一:一上电就听到报警声,MC 单元指示灯混乱。原因是出现了总线竞争,通常是 CS、OE、WE 或者数据方向控制信号接错,导致多个器件同时驱动总线。解决方法是立即断电,按接线图逐根核对排线,特别检查 74LS245 的方向控制引脚和使能引脚是否接到了正确位置。

现象二:手动写入后校验读出的数据与预期不符。原因是 ST 按钮的按动次数不够,或者 KK5 没有在正确时机拨到“加 1”档。常见做法是每写完一个字节都要回想一次操作序列:置数挡给地址,按两次 ST 写低 8 位;拨加 1 挡,写中 8 位;再写高 8 位。如果中途漏按一次,后续所有单元的地址都会错位。

现象三:微程序装载成功后,执行机器指令结果不对。原因多半是机器指令给出的时机不对。教材要求当后续微地址显示为 000001 时,在 CON 单元的 SD27~SD20 模拟给出指令码,这条指令会在下一个 T3 被打入指令寄存器 IR。如果给早了,指令还没有被正确的微程序入口接收;给晚了,当前微指令已经执行完,指令丢失。解决方法是先把 KK2 拨到单拍,单步执行到 MAR 为 000001 时再设置指令码。

现象四:存储器实验里数据读不出来,数据总线 LED 全灭或全亮。原因是 IOR 的状态没有随读写阶段切换。写阶段需要 IOR=0 让数据开关进入总线,读阶段必须 IOR=1 关闭 IN 单元输出,否则三态门始终占用数据总线。把 IOR 按流程切换一次,问题基本立刻消失。

提示:出现异常时优先检查“有没有第二个人在驱动总线”,这是微程序实验报错的第一大来源,其次是开关挡位和 ST 次数。不要一上来就怀疑芯片坏了。

5. 总线实验与联机调试:用数据通路图验证指令执行的技巧

5.1 总线实验:I/O 地址译码与读写控制逻辑

最后一个系统总线实验把前面的内容串到了一起。实验平台的外部总线分为数据总线、地址总线和控制总线,外部设备和 CPU 内总线之间通过三态门连接,实现内外总线的分离和数据流向控制。I/O 地址空间的划分由地址总线的高两位 A6、A7 译码完成,实验箱把地址空间切成四个区域,每个区域对应一个片选信号:

A7A6选定地址空间片选信号
0000-3FIOY0
0140-7FIOY1
1080-BFIOY2
11C0-FFIOY3

读写控制逻辑用 IOM 区分操作对象:IOM=1 时读写 I/O 设备,IOM=0 时读写存储器。RD 和 WR 都是高有效,T3 时序参与写操作来保证写脉宽。做总线实验时,IOM 这类的开关位置会直接影响所有外设动作,接完线以后先把 IOM 扳到目标位置再上电,能避免一半以上的误操作。

5.2 用数据通路图反向验证微程序:一个能省半小时的检查技巧

联机调试时我最常用的技巧是“反向验证”:不在通路图动画里追着每一条线看,而是盯住后续微地址 MAR 的值。微程序里的每条指令都有固定的流转规律,当 MAR 显示为 000001 时,说明当前机器指令已经执行完,控制器回到取指入口,准备取下一条指令。这是整个微程序执行循环的固定状态,也是切入观察的最佳时机。

再往前一步,我会写一个小的微代码文件检查脚本,在装载之前先把格式错误过滤掉:

# ucode_check.py - 装载前检查微代码文件 # 用法: python ucode_check.py ucode.txt # 说明: 检查 $M 行格式、地址范围和十六进制位数 import sys, re pat = re.compile(r"^\$M\s+([0-9A-Fa-f]{2})\s+([0-9A-Fa-f]{6})") addr_seen = set() for line in open(sys.argv[1], encoding="utf-8"): line = line.split(";")[0].strip() if not line: continue m = pat.match(line) if not m: print(f"[FAIL] 无法解析: {line}") sys.exit(1) addr, val = m.group(1).upper(), m.group(2).upper() if addr in addr_seen: print(f"[FAIL] 重复地址: {addr}") sys.exit(1) addr_seen.add(addr) if int(addr, 16) > 0x3F: print(f"[FAIL] 地址越界: {addr}") sys.exit(1) print(f"[OK] 共 {len(addr_seen)} 条微指令, 格式全部正确")

这个脚本做的事情很朴素:用正则匹配每一条 $M 行,确认地址是两位、数据是六位十六进制,再检查是否有重复地址和地址越界。我一般在装载前跑一遍,能提前拦掉手误造成的“地址多打一位”或“少打个空格”之类的问题。要知道控存写错一条,联机调试时看到的执行结果会非常诡异,排查成本远高于写脚本的几分钟。

从那以后,我每次做微程序实验都强制自己走一遍“格式检查 → 装载 → 刷新校验 → 单步执行”的流程,校验环节从不敢跳。实验箱的每个信号都有了明确含义之后,这套流程其实很快,但能明显把出错概率压下去。希望这份教程里的操作细节和踩坑记录也能帮你少走几段弯路。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询