简介:面向计算机组成原理课程设计的学习者,这份文华学院个人课程作业文档围绕“基于Proteus简单模型计算机设计与实现”展开,适合需要参考整机设计思路、微程序设计流程与调试记录的学生对照使用,需注意其为个人作业,仅供思路借鉴。文档从需求与总体设计入手,依次梳理模型机逻辑图、运算器与存储器电路及其控制信号,进而给出时序分析、指令数据流向、微指令与微代码设计、程序编制和调试结果验证,较完整地呈现了从逻辑部件到整机运行的实现链路。压缩包仅含1个doc文件,大小约2.79MB,篇幅集中,便于按目录快速定位运算器、存储器、控制器等模块的说明。目前已有537人学习,可作为课程设计阶段梳理控制信号、区分脉冲与电平信号、检查微指令码点与时序安排的参考资料。
1. 58H 加 58H 得 D0H,凭什么说它对
两片 74LS181 串成 8 位加法,58H 加 58H 出 D0H,在 Proteus 里跑出来只要几秒。但计算机组成原理课程设计这一关,真正卡住人的不是那个 D0H,而是答辩时老师随手抽一条 IN 指令,问你这条指令占几个微地址、第三个微地址上 26 位微码里哪几位是 1、T2 和 T4 分别落在哪个节拍。这门课设要交的其实不是一张能跑通的图,而是一套能自证的设计:控制信号归纳得全不全、整机层次切得清不清、微指令码点排得对不对、开关拨动顺序记不记得住。下面这套基于 Proteus 的简单模型计算机方案,从需求拆到验收,学生可以照着交作业,已经工作的也可以拿着改。
2. 需求拆解与总体架构:五个子电路怎么拼成一台模型机
2.1 把试卷上的五条内容要求翻译成四件可交付物
题目里列了五条内容,读起来像五件事,落到实际交付时其实是四件东西:一张控制信号总表、一套层次化的整机电路、一份开关模拟调试记录、一份微指令码点与时序安排。第 3 条“用开关模拟控制信号调试二段汇编指令程序”和第 4 条“给出微指令码点以及脉冲信号的时序安排”看着是两件事,实际共用同一张表——你拨开关的顺序,就是微码里 T2、T4 两个节拍的展开顺序。
所以我一般会先把交付物定死,再动手画图:
| 交付物 | 对应题目条目 | 验收形式 |
|---|---|---|
| 控制信号总表(含有效电平) | 第 1 条 | 老师随机点一个信号,能说出它什么时候有效 |
| 层次化整机电路(父图 + 5 个子图) | 第 2 条 | 子电路端口与主电路连线一一对上 |
| 开关模拟调试记录 | 第 3 条 | 能现场复现一条 ADD 的完整拨动过程 |
| 微指令码点 + 时序安排 | 第 4、5 条 | 微码表能与调试记录逐拍对应 |
这四件东西的顺序不能颠倒。很多人上来就在 Proteus 里连运放、连寄存器,连完发现控制信号列不全,回头补表时又发现某个信号在自己的电路里根本没引出来,只能拆了重连。先列表、后画图,能少走一大截弯路。
2.2 层次化划分:主电路管总线,子电路管选通与锁存
整机切成父图和子图,不是为了好看,是为了让“改一处不牵动全局”。主电路上放按钮开关、显示灯、时序部分、指令寄存器 IR1(74LS273)和控制器;运算器单元、寄存器组、指令计数器部件、存储器单元各自做成子电路。子电路对外只暴露两类端口:挂到 D[0..7] 数据总线和 AB[0..7] 地址总线上的线,以及从控制器过来的控制端。
按这个原则,五个子电路的端口契约大致是这样:
| 子电路 | 对外输入端口 | 对外输出端口 |
|---|---|---|
| 运算器单元 | D2CK、D1CK、/EDR1、/EDR2、S3、S2、S1、S0、CN、M、/ALU-O | D[0..7] |
| 寄存器组单元 | SB、SA、/WR、RCK、/RD | D[0..7] |
| 指令计数器部件 | CLR、PCK、PC+1、ELP、PC-O、IR2CK、EIR2、IR2-O | AB[0..7]、D[0..7] |
| 存储器单元 | /WM、EMCK、/RM、/CS、AB[0..7]、D[0..7] | D[0..7] |
| 控制器单元 | CLR、SETWR、SETRD、T2、T4 | MD[0..25]、MB[0..5] |
这张表看着平淡,实际是整篇设计的地基。我在做这类层次化拆分时有个习惯:每画完一个子图,就把它的端口名抄到父图上对应的引脚标签旁边,两边的拼写必须一模一样。Proteus 对标签大小写和斜杠是敏感的,/EDR1写成EDR1不会报错,只会静静地不工作,然后你在父图上找半天。
2.3 器件选型:两块 181 为什么走串行进位
运算器用两片 74LS181 拼 8 位,低 4 位那片的结果通过 CN 送给高 4 位那片,本质是串行进位。选串行不选并行,理由很实际:模型机的时钟是人手拨开关拨出来的,一个节拍几百毫秒,进位延迟再多也无所谓;而并行进位要多一片 74LS182 和一堆连线,图一复杂,调试时连哪根线接错了都看不出来。
74LS181 的工作方式由 S3、S2、S1、S0 四位加 M 决定。M 为高电平做逻辑运算,为低电平做算术运算;CN 为高电平表示“无进位输入”,做加法时它必须是高,做减法时配合 S 端选择取反加一。这几位在微码里是电平信号,一旦写进微指令,就是整条指令执行期间保持不变的那部分。
其余器件的分工也很清楚:74LS6116 是 2K×8 的静态存储器,挂 AB[0..7] 和 D[0..7];74LS193 两片级联做程序计数器;74LS244 做三态缓冲,负责把数据送上总线;74LS273 锁存指令寄存器 IR1 和 IR2。选型上没有花样,都是课设实验箱里现成的片子,好处是网上资料多、引脚定义查得到,坏处是 273 这类锁存器对脉冲沿敏感,后面时序那一节会专门讲。
2.4 两条总线怎么不打架
整机只有两条总线:8 位数据总线 D[0..7] 和 8 位地址总线 AB[0..7]。数据总线上同时挂着运算器输出、寄存器组输出、存储器输出、PC 输出和开关输入,任何时刻只能有一个源在驱动,其余全要高阻。地址总线相对干净,主要来自 PC 和存储器地址锁存。
控制信号里的 /ALU-O、/RD、PC-O、SW-BUS 这几个,本质上都是“三态门的开关”。设计时要保证它们在同一个微地址上互斥——这条约束后面我会用一小段脚本做自动检查,靠眼睛看 26 位二进制串是看不出来的。
3. 控制信号全表:从 11 个运算器信号到整机 30 多个控制端
3.1 运算器单元的 11 个信号:3 个脉冲、8 个电平
运算器是整机里信号最密的一块。把所有控制端归纳出来,是题目第 1 条明确要求的东西,也是后面填微码表时的字段来源:
| 信号名 | 作用 | 有效电平/类型 |
|---|---|---|
| D2CK | DR2 寄存器工作脉冲 | 上升沿(脉冲) |
| D1CK | DR1 寄存器工作脉冲 | 上升沿(脉冲) |
| /EDR2 | 选通 DR2 寄存器 | 低电平(电平) |
| /EDR1 | 选通 DR1 寄存器 | 低电平(电平) |
| S3、S2、S1、S0 | 74LS181 工作方式选择 | 低电平(电平) |
| CN | 有无进位输入 | 高电平(电平) |
| M | 逻辑/算术运算选择 | 高为逻辑,低为算术(电平) |
| /ALU-O | ALU 结果输出到总线 | 低电平(电平) |
这块要分清“选通”和“打入”是两件事。数从总线进 DR1,得先让 /EDR1 变低把门打开,再给 D1CK 一个上升沿把数据锁进去;只开 /EDR1 不给脉冲,数据只在门口晃,寄存器里还是旧值。这种“门开了但没敲钟”的错误,在开关模拟阶段出现的频率最高。
3.2 寄存器组与指令计数器:选通、写入、读出三件事
寄存器组的信号是 SB、SA、/WR、RCK、/RD。SB 和 SA 两位组合选出当前要操作的是 R0 还是 R1(本例只有两个寄存器,两位刚好够),RCK 上升沿写入,/RD 低电平把选中寄存器的内容送上总线。注意这里 /WR 和 RCK 也是一对:/WR 是写允许这个“状态”,RCK 才是那一下“动作”。
指令计数器部件的端口多一些,功能却更单一。CLR 清零,PCK 是 PC 自己的打入脉冲,PC+1 让计数器加一,ELP 把 PC 的低 8 位锁存到地址总线上,PC-O 把 PC 内容送上数据总线(用于某些需要把 PC 当数据处理的场合)。IR2CK、EIR2、IR2-O 则服务于指令寄存器 IR2:IR2CK 打入,EIR2 是 IR2 的输出允许,IR2-O 把 IR2 的内容送到总线。
这里有个容易忽略的点:PC+1 是电平信号,PCK 是脉冲信号。如果只把 PC+1 拉高而不打 PCK,193 计数器不会动。反过来,只打 PCK 不给 PC+1,计数器会按当前置数方式工作。两个信号谁先谁后,取决于你用的是同步置数还是异步置数,接线时得对着 193 的真值表确认一遍。
3.3 存储器单元:四个信号锁死一次读或一次写
6116 这块只有四个控制端,但每一个都不能含糊:
| 信号名 | 作用 | 有效电平/类型 |
|---|---|---|
| /CS | 6116 片选 | 低电平(电平) |
| /RM | 6116 读允许 | 低电平(电平) |
| /WM | 6116 写允许 | 低电平(电平) |
| EMCK | 6116 写入脉冲 | 上升沿(脉冲) |
一次完整的写存储过程是:地址先稳定在 AB 上,/CS 拉低选中片子,/WM 拉低打开写通道,然后给 EMCK 一个上升沿,数据才真正落进存储单元。顺序反了,写进去的就是地址还没稳定时的垃圾数据。读则简单些:/CS 和 /RM 同时拉低,数据直接出现在 D 总线上,不需要脉冲。
3.4 脉冲与电平的分工:T2 打沿,T4 保电平
题目特意要求“区分脉冲信号和电平信号”,这是整篇设计里最见功力的地方。模型机的时序部分只产生两个节拍:T2 和 T4。它们不是两根时钟线,而是两种“操作类型”的代号。
T4 负责电平类动作:开 /EDR1、开 /RD、选 SB/SA、置 S3~S0 和 M、开 /ALU-O。这些信号在 T4 有效期间一直保持,给组合逻辑留出稳定时间。T2 负责脉冲类动作:D1CK、D2CK、RCK、PCK、IR2CK、EMCK,都是在 T4 建立好的电平环境上打一下沿,把数据锁进目标器件。
以 ADD R0,R1 的一个微步骤为例:先给 T4,让 /EDR1 和 SB/SA 选中的 R0 输出同时有效,数据总线稳定;再给 T2,D1CK 上升沿把总线上的值锁进 DR1;然后撤掉 T4 上的 /EDR1。这套“T4 铺路、T2 落锁、T4 收摊”的节奏,贯穿了后面所有指令。开关模拟时,你手里拨的那个开关,本质上就是在手工产生 T2 和 T4。
4. 微指令与微代码:把 ADD R0,R1 拆成 4 个微地址
4.1 先画信息流向,再定每条指令占几个微地址
微地址的数量不由指令长短决定,由数据要转几手决定。IN R0,#DATA1 是双字节指令:第一个微地址把开关数据送上总线并打开寄存器写,第二个微地址关掉写。ADD R0,R1 要转四手:R0 进 DR1、R1 进 DR2、ALU 做加法把结果放总线、结果写回 R0,因此占 4 个微地址。先把流向画清楚,微地址自然就数出来了。
题目要求调试的两段程序,一段只做运算,一段带存储器存取:
; 程序一:纯运算,验证 ALU 与寄存器通路 IN R0, #DATA1 ; 从开关读第一个数,例如 58H IN R1, #DATA2 ; 从开关读第二个数,例如 58H ADD R0, R1 ; R0 <- R0 + R1 OUT R0 ; 结果送显示灯 ; 程序二:加入存储器,验证 STA / MOV 通路 IN R0, #DATA1 STA (01H), R0 ; R0 写入存储单元 01H IN R0, #DATA2 MOV R1, (01H) ; 存储单元 01H 读回 R1 ADD R0, R1 OUT R0程序一的意义是把 ALU、寄存器组、总线三者之间的通路跑通;程序二多加了一次写存储和一次读存储,把 6116 也拉进回路。两段都跑通,才算把五个子电路全验证了一遍。这也是我建议的调试顺序:先跑程序一,跑通了再加程序二,别一上来就上带存储器的版本。
4.2 26 位微指令怎么排字段
微指令宽度是 26 位,记为 MD25 到 MD0;微地址 6 位,MB5 到 MB0,理论上有 64 个微地址可用。字段顺序没有国家标准,按自己电路上控制信号的排布定就行,但一旦定下来,整篇设计里不许再改。
| 位 | 字段 | 类型 | 位 | 字段 | 类型 |
|---|---|---|---|---|---|
| MD25 | SW-BUS | 电平 | MD12 | PC+1 | 电平 |
| MD24 | EDR1 | 电平 | MD11 | ELP | 电平 |
| MD23 | EDR2 | 电平 | MD10 | PC-O | 电平 |
| MD22 | M | 电平 | MD9 | EIR2 | 电平 |
| MD21 | S3 | 电平 | MD8 | IR2-O | 电平 |
| MD20 | S2 | 电平 | MD7 | RM | 电平 |
| MD19 | S1 | 电平 | MD6 | WM | 电平 |
| MD18 | S0 | 电平 | MD5 | CS | 电平 |
| MD17 | CN | 电平 | MD4 | EIR1 | 电平 |
| MD16 | ALU-O | 电平 | MD3 | R(保留) | — |
| MD15 | SB | 电平 | MD2 | R(保留) | — |
| MD14 | SA | 电平 | MD1 | R(保留) | — |
| MD13 | WR | 电平 | MD0 | R(保留) | — |
表里 MD4 的 EIR1 是把 IR1 内容送上总线的输出允许,MD3 到 MD0 留作扩展。这里要强调一点:微码里存的是“信号是否有效”,不是“引脚电平高低”。像 /EDR1 这种低电平有效的信号,微码位写 1 表示“该信号有效”,实际引脚输出是低。很多人在填表时把这两件事混了,导出的微码看着对,烧进去全反。
4.3 微代码表:ADD R0,R1 逐拍拆开
以 ADD R0,R1 为例,四个微地址对应四拍。下表里“有效信号”一栏写的是该拍需要置为有效的控制端:
| 微地址 | T4 保持的电平信号 | T2 打的脉冲 | 数据流向 |
|---|---|---|---|
| M+0 | SB/SA 选 R0、/EDR1、S3~S0 置送数方式 | D1CK | R0 → 总线 → DR1 |
| M+1 | SB/SA 选 R1、/EDR2 | D2CK | R1 → 总线 → DR2 |
| M+2 | S3~S0 置加法、M 置低、CN 置高、/ALU-O | 无 | DR1 + DR2 → 总线 |
| M+3 | SB/SA 选 R0、/WR | RCK | 总线 → R0 |
第一拍和第四拍最容易出错。第一拍里 /EDR1 和 SB/SA 必须同时有效,否则总线上要么没数据,要么数据来源不明;第四拍里 /WR 是写允许、RCK 才是那一下写动作,只给 /WR 不给 RCK,R0 里躺着的还是老值,屏幕上却看不出任何异常——它会一直显示上一次的结果,让你误以为加法算错了。
把这类表填完,原始的 CM 文件就是一堆 DB 行,每行一个二进制数,对应一个微地址的 26 位微码。手写这些二进制串几乎必错,我一般是先按上面这张表把“每拍需要哪些信号有效”写成文字,再用脚本转成二进制,最后人工抽查两三行。
4.4 用一段脚本生成微码并做互斥检查
手写 26 位 0/1 串的痛苦,试过一次就够。下面这段脚本把位序固化成字典,输入“这一拍哪些信号有效”,输出 26 位串,顺手检查几条互斥规则:
# 微码生成与互斥检查:字段顺序必须与电路接线严格一致 FIELD_ORDER = [ "SW_BUS", "EDR1", "EDR2", "M", "S3", "S2", "S1", "S0", "CN", "ALU_O", "SB", "SA", "WR", "PC_PLUS1", "ELP", "PC_O", "EIR2", "IR2_O", "RM", "WM", "CS", "EIR1", "R0", "R1", "R2", "R3", ] # 同一拍内不允许同时有效的信号组合,理由是它们会抢同一条总线或同一片存储 EXCLUSIVE_GROUPS = [ {"EDR1", "EDR2"}, # 两个 ALU 暂存器不能同时往总线上送 {"RM", "WM"}, # 同一次存储访问不能既读又写 {"PC_O", "ALU_O", "SW_BUS"}, # 三个总线源互斥 {"SB", "SA"}, # 本例只用 R0/R1,两位不做同时置位 ] def encode(active, total_bits=26): """把这一拍有效的信号集合转成 26 位二进制串,高位在左""" for group in EXCLUSIVE_GROUPS: hit = group & set(active) if len(hit) > 1: raise ValueError(f"互斥冲突:{hit} 不能同时有效") bits = ["1" if name in active else "0" for name in FIELD_ORDER] assert len(bits) == total_bits, "字段数与微指令宽度不一致" return "".join(bits), int("".join(bits), 2) if __name__ == "__main__": # ADD R0,R1 第四拍:把总线上的运算结果写回 R0 word, value = encode({"SB", "WR"}) print(word, hex(value)) # 输出的串直接抄进 CM 文件脚本干三件事:把信号名映射成位、把互斥规则变成断言、输出可直接抄进 CM 文件的字符串。FIELD_ORDER 的顺序必须和你电路上 MD25 到 MD0 的接线顺序一致,改一位,所有已生成的微码全废——所以这张表我会打印出来贴在屏幕边上,改电路前先改它。
互斥检查那几条规则看着简单,实际能挡掉大半翻车事故。比如 /RM 和 /WM 同时有效,6116 的数据线会同时被内部读出电路和写驱动占用,仿真里表现是数据线出现 X 态或一个介于高低之间的怪值,你盯着波形看半小时也未必想到是这两个信号打架。
5. 联调避坑:开关模拟调试里最容易翻车的五个点
5.1 现象:寄存器写不进去,显示灯一直是旧值
原因通常是“门开了但没敲钟”。/WR 或 /EDR 这类电平信号拉低了,但 RCK、D1CK 这类脉冲没有给上升沿,或者给了但时序在电平建立之前。在 Proteus 里,脉冲源的初始状态也可能是高,第一次“上升沿”根本没发生。
解决的办法是把每一拍拆成固定三步:先拨 T4 相关的电平开关,等总线稳定(仿真里大约几十毫秒);再拨 T2 打脉冲;最后撤 T4。三步写成检查清单贴在旁边,别凭感觉拨。
5.2 现象:数据总线上出现怪值,或者多个灯同时亮
原因是总线冲突,两个源同时驱动 D[0..7]。最常见的组合是 ALU 的 /ALU-O 还开着,寄存器的 /RD 又开了;或者开关输入的 SW-BUS 没关,存储器已经在往总线上送数。
解决上,除了靠 4.4 那段脚本做静态检查,调试时可以在总线上挂一个探针,逐拍看是哪几个三态门处于打开状态。我一般会在微码表里给每个微地址加一列“本拍总线源”,只允许填一个名字,填两个就说明设计错了。
5.3 现象:运算结果对,但换一组数据就不对
原因多半是 CN 或 M 这两位被当成了“每条指令都要重新设”的信号。实际上在连续做加法时,CN 应该保持高电平,M 保持低电平;如果某一拍把它们复位成默认值,第一个数算对了,第二个数就带进位错误。
解决办法是把 S3~S0、CN、M 看成一组“运算方式配置”,在进入 ALU 相关微地址之前统一设好,中途不改。微码表里这几位连续几拍保持相同值,是可以一眼看出来的。
5.4 现象:STA 指令执行完,存储单元里读回来是 FF 或 00
原因是写存储的顺序错了。地址还没稳定到 AB 上就给了 EMCK,或者 /WM 和 /CS 拉低的时刻晚于 EMCK 的上升沿。6116 要求地址和片选在写脉冲之前建立,写脉冲结束后还要保持一小段时间。
解决是把 STA 拆成两拍:第一拍把地址送到 AB、/CS 和 /WM 拉低;第二拍给 EMCK 上升沿;第三拍撤销 /WM。宁可多占一个微地址,也别把三件事挤在一拍里。
5.5 现象:结果碰巧对了,但换条指令就崩
这是最隐蔽的一种。有人为了让显示灯亮出 D0H,反复试微码,试到某一组恰好出结果就交卷了。这种“凑答案”在答辩现场撑不过三分钟,因为老师会换一组输入数据,或者让你现场跑程序二。
解决办法只有一个:每一条指令都要能画出完整的数据流向图,每一拍的总线源、目标器件、有效信号三样东西写清楚。写不清楚的那一拍,就是设计里的黑匣子,早晚要炸。
6. 验收与答辩:怎么用一条指令证明整机是对的
验收不是把仿真跑一遍给老师看,是一套有顺序的自证流程。我一般按“最小闭环 → 加重定位 → 现场抽考”三步走,每一步都有明确的通过标准。
第一步跑最小闭环,就是程序一的 IN、IN、ADD、OUT 四条指令。通过标准不是“灯亮了”,而是:输入 58H 和 58H 得 D0H;输入 01H 和 02H 得 03H;输入 FFH 和 01H 得 00H(验证进位溢出被丢弃)。三组数据都过,说明 ALU 通路和寄存器写回是正确的。
第二步加重定位,跑程序二。此时要观察的是 STA 写进去的值和 MOV 读出来的值是否一致,以及读出来的值参与加法后结果是否正确。这一步通过,说明地址总线、存储器、指令计数器都接对了。
第三步是现场抽考,也是答辩真正的分水岭。老师通常会做三件事:随机指定一条指令让你说出它占几个微地址;随机指一个微地址让你报出 26 位微码里哪几位是 1;随机点一个控制信号问你它是脉冲还是电平。这三问对应三张表,所以答辩前我会把控制信号总表、微码表、调试记录三张纸按顺序摆好,问哪张翻哪张。
下面这张验收表可以直接抄进报告的“结果”一节,应得结果和实际结果并排写,比只写一句“运行成功”有说服力得多:
| 验证项 | 输入 | 应得结果 | 实际结果 | 判定依据 |
|---|---|---|---|---|
| 加法通路 | 58H + 58H | D0H | D0H | 显示灯 D7~D0 |
| 加法进位丢弃 | FFH + 01H | 00H | 00H | 8 位截断 |
| 逻辑运算(M=1) | 58H AND 58H | 58H | 58H | 切换 S3~S0 |
| 存储器写读 | STA 01H 后 MOV | 原值 | 原值 | 两次 OUT 对比 |
| 时序正确性 | 单拍 T2/T4 | 每次只锁一个器件 | 无总线冲突 | 探针观察 |
答辩时被问倒最多的不是原理,是“你为什么这么接”。比如为什么 CN 接高、为什么串行进位、为什么 IR1 用 273 而不是 244——每个“为什么”背后都得有一句“因为我这样接之后,某一拍的总线源就唯一了”。把设计的每个选择都归到“保证某一时刻只有一个驱动源”这条主线上,回答起来就顺了。
从那以后我每次做这类硬件课设,都会在画第一根线之前先把控制信号总表和微码位序表写死,哪怕当时还一张图都没有。这两张表一旦定下来,后面画图、填码、拨开关全是有据可查的机械动作,不再靠试。希望帮到你。
本文还有配套的精品资源,点击获取