简介这份华中科技大学计算机组成原理课程实验报告聚焦32位MIPS CPU设计面向计算机专业学生及准备组成原理实验、课程设计或复试复习的读者覆盖单周期与多周期CPU的完整设计流程。报告以logisim为实现平台围绕add、slt、addi、lw、sw、beq、bne、syscall八条核心指令展开包含数据通路绘制、硬布线控制器与微程序控制器实现、sort.hex冒泡排序测试、溢出异常处理以及故障调试与性能分析并附实验总结、心得和参考文献可帮助读者梳理从方案设计到测试验证的完整思路。压缩包仅含1个docx文档约2.8MB目录按设计要求、方案设计、实验步骤、故障与调试、测试与分析、总结心得组织便于按实验阶段检索参考。已有12128人学习适合需要对照报告结构、复盘CPU设计关键环节的读者对实验报告撰写与答辩准备亦有参考价值。1. 8 条指令的 CPU真正卡住人的不是连线2019 年 11 月底sort.hex 跑完那 10 个数按从小到大排好停在寄存器里我把 Logisim 窗口截了图才敢关。这份计算机组成原理课程的 CPU 设计实验前后耗了我将近两周而复盘下来最难的地方不是拖元件连线——单周期那条通路对着图连一天就能出来。真正让人返工的是控制信号表里几行“看着没问题”的逻辑是从单周期改到多周期时 13 个状态的跳转关系是微指令表里填错的一个 RegDst。这篇按实验报告里的真实流程讲8 条核心指令怎么拆通路、硬布线控制器的译码逻辑怎么从数据通路反推、微程序控制器的微指令表怎么填不出错、多周期状态机怎么一步步调通。刚上手的人能照着顺序做做过一遍的人能对照看看判断依据对不对。2. 八条指令逐条拆单周期数据通路怎么连才不返工实验要求很明确在 Logisim 上用给定组件搭一个 32 位 MIPS 单周期 CPU支持 add、slt、addi、lw、sw、beq、bne、syscall 这 8 条指令最后能跑通冒泡排序的 sort.hex 并输出正确结果。这句话里藏着两个约束一是指令集只有 8 条数据通路可以裁到最简二是必须跑通一个真实的排序程序意味着分支、访存、寄存器写回三条链路都得同时正确任何一条错一位排序结果就是乱的。我见过不少人上来就照着参考图连连完发现跑不通再一处处查效率很低。更省事的顺序是先把每条指令拆成“取指 → 译码取数 → 执行 → 写回”四段看清楚哪几段是公共的、哪几段是这条指令独有的把独有的部分先单独搭出来验证最后才做合并。2.1 先看清每条指令的真实工作量把 8 条指令摊开看它们的开销差别很大。add 和 slt 是纯 R 型读两个寄存器、ALU 运算、写回 rd。addi 是立即数运算读一个寄存器、立即数符号扩展、ALU 加法、写回 rt。lw 和 sw 要访存地址由寄存器加立即数偏移算出lw 还要把读出的数据写回sw 只需要把 rt 的值送进存储器。beq 和 bne 不写回也不访存它们只影响 PC。syscall 在这份设计里不新增任何通路它的作用是停机。指令格式关键动作是否写回是否访存是否改 PCaddRrs rt是rd否否sltRrs rt 置 1是rd否否addiIrs 符号扩展立即数是rt否否lwIrs 偏移 → 地址是rt来自存储器读否swIrs 偏移 → 地址否写否beqI相等则跳转否否是bneI不等则跳转否否是syscallR停机否否冻结syscall 的处理最能说明“读懂需求比照抄图重要”。它不需要任何数据通路实现方式是把 PC 寄存器的使能端拉低让 PC 停止更新——取指停住整个 CPU 就停住了。这一步在单周期里用一个 Halt 信号直接接到 PC 的使能端就能实现不需要额外元件。2.2 公共取指段与七条指令的分通路合并取指段是所有指令共享的PC 输出地址给指令存储器 IMEMIMEM 输出指令字同时 PC 加 4 得到下一条指令地址。这部分没有分支直接连。真正要设计的是剩下的部分。我的做法是每条指令画一张局部通路图画完再合并。R 型指令的通路是RegFile 的两个读口分别输出 rs 和 rt 的值送进 ALU 的两个输入端ALU 输出写回 RegFile写地址来自指令的 rd 字段。addi 的通路和 R 型几乎一样区别是 ALU 的第二个输入不是 rt 而是符号扩展后的立即数写回地址是 rt 而不是 rd。lw 的通路在 addi 基础上多一步ALU 算出的结果当地址去读数据存储器 DMEM读出的数据写回 rt。sw 不写回但要把 rt 的值送到 DMEM 的数据输入端。beq 和 bne 把 rs 和 rt 送进 ALU 做减法利用 ALU 的 equal 输出判断是否相等。合并时凡是有多个来源的输入端都要加数据选择器选择端接控制信号。具体是四个位置写回寄存器的数据来源ALU 结果还是存储器读出数据、写回寄存器的地址来源rd 还是 rt、ALU 的第二输入rt 还是符号扩展立即数、下一条 PC 的来源PC4 还是分支目标地址。这四个 MUX 就是整个单周期数据通路的骨架后面写控制器时控制信号基本都是在控制这四个选择器加几个写使能。2.3 三个容易连错的细节位宽、字节地址、停机第一处是符号扩展。立即数是 16 位要扩展到 32 位而且必须是有符号扩展——addi 的立即数可能是负数lw/sw 的偏移也可能是负数。Logisim 里的扩展器组件要选对符号位处理方式无符号扩展会让所有负偏移全部算错这种错误在跑小测试时看不出来跑 sort.hex 时数组下标一越界就全乱。第二处是地址位宽。PC 是 32 位字节地址而指令存储器和数据存储器的地址输入端只有 10 位而且是字地址。这意味着不能直接把 PC 的低 10 位接上去必须用分离器取 PC 的第 2 到第 11 位。道理很简单每条指令 4 字节字节地址 0x00000000、0x00000004、0x00000008 对应的字地址是 0、1、2正好是右移两位也就是从第 2 位开始取 10 位。字节地址二进制低 12 位字地址取 bit2~bit110x000000000000 0000 000000x000000040000 0000 010010x000000080000 0000 100020x0000000C0000 0000 11003这一步我第一版就接错了直接把 PC 的低 10 位接上去结果 PC4 时取到的还是第 0 条指令程序原地打转。数据存储器同理地址也要走同样的处理。第三处是分支信号。beq 和 bne 共用一条通路但条件相反。用 ALU 的 equal 输出构造 branch beq·equal bne·~equal这个 branch 信号接到 PC 输入端那个 MUX 的使能端。注意这里是“使能”不是“选择”分支不成立时 MUX 输出 PC4成立时输出分支目标地址。分支目标地址是 PC4 加上符号扩展后左移两位的立即数这个“PC4”在单周期里容易写成“PC”两者差一条指令跳转落点会整体偏移。提示复位信号要接到指令寄存器 IR 和数据寄存器 DR否则上电瞬间这些寄存器是随机值第一条指令就可能取错。3. 硬布线控制器控制信号表是反推出来的不是猜出来的数据通路连完只是有了“能走的路”还得有人告诉它每一步走哪条。硬布线控制器就是干这个的它的本质是一组组合逻辑输入是指令的 OP 字段和 Funct 字段输出是各个控制信号。很多人写到这一步会开始“猜”控制信号猜完发现某个指令跑不对再回头改来回折腾。正确做法是拿着已经画好的数据通路一条指令一条指令地问执行这条指令时每个 MUX 该选哪一路每个写使能该不该置 13.1 指令译码OP 比较加 Funct 比较R_TYPE 要扣掉 syscall译码逻辑的第一层是把 OP 字段翻译成“这是哪条指令”。查 MIPS32 指令手册lw 的 OP 是 0x23sw 是 0x2bbeq 是 0x04bne 是 0x05addi 是 0x08。用比较器把 OP 和这几个常数逐一比较相等就输出对应的译码信号这部分是直白的。麻烦的是 R 型指令。add、slt、syscall 的 OP 字段全是 0x00光看 OP 区分不了必须再看 Funct 字段add 是 0x20slt 是 0x2asyscall 是 0x0c。所以要先产生 ADD、SLT、SYSCALL 三个信号条件是 OP 等于 0 且 Funct 等于对应值。然后是 R_TYPE 信号。直觉上“OP 等于 0 就是 R 型”但这里有个陷阱syscall 的 OP 也是 0如果直接把“OP0”当 R_TYPEsyscall 会被误认为是一条要写回寄存器的运算指令。正确的表达式是 R_TYPE (ADD SLT) ~SYSCALL把 syscall 排除掉。指令OP 字段Funct 字段译码条件lw0x23—OP 0x23sw0x2b—OP 0x2bbeq0x04—OP 0x04bne0x05—OP 0x05addi0x08—OP 0x08add0x000x20OP 0 且 Funct 0x20slt0x000x2aOP 0 且 Funct 0x2asyscall0x000x0cOP 0 且 Funct 0x0c3.2 ALU 控制只有加法和比较二选一就够这套指令集里 ALU 只需要做两件事加法和比较大小。加法用于 add、addi、lw、sw 的地址计算、beq/bne 的减法比较比较大小只用于 slt 一条指令。所以 ALU_OP 根本不需要复杂的译码一个二路选择器就够了一路接加法操作码 0x5一路接比较操作码 0xb选择端接 SLT 译码信号。SLT 有效时做比较其余时候一律做加法。这个简化是有前提的——它成立是因为指令集里没有 sub、and、or 这些运算。如果后面要扩展指令这里就得改成多路选择或者真正的 ALU 译码器。写的时候心里要清楚这是“当前设计的最小实现”而不是通用做法。顺带说一句beq 和 bne 用减法判断相等靠的是 ALU 的 equal 输出而不是结果为零。有些实现里用“结果全零”判断遇到溢出时行为会不一样用 equal 信号更稳。3.3 控制信号逐条推导与冲突排查有了 7 个译码信号R_TYPE、ADDI、LW、SW、BEQ、BNE、SYSCALL剩下的控制信号基本都是“哪些指令需要它就把对应的译码信号或起来”。控制信号作用有效条件RegWrite寄存器写使能R_TYPE ADDI LWRegDst写地址选 rd 还是 rtR_TYPE选 rdMemToReg写回数据来自存储器LWMemWrite存储器写使能SWMemRead存储器读使能LW取指阶段另算AluSrcBALU 第二输入选立即数LW SW ADDIBeq / Bne分支译码BEQ / BNEHalt停机SYSCALL这里最容易错的是 RegWrite 的条件。写回寄存器的指令有三条R 型add、slt、addi、lw。漏掉 addi 的话所有立即数加法的结果都不会落到寄存器里程序里的循环变量永远不更新sort.hex 会死循环。漏掉 lw 的话数组元素根本读不进来。这类错误的现象往往很迷惑——程序不是完全不跑而是跑到一半卡住看着像时序问题实际是控制信号少了一项。我后来养成了一个习惯把译码逻辑和控制逻辑写成一个可执行的小脚本跑一遍 8 条指令把每条指令的控制信号打出来再和手工推导的表逐列对照。这比在 Logisim 里点开一个个探针看波形快得多。# 用 8 条指令的 OP/FUNC 编码核对译码信号与最终控制信号 OP {LW: 0x23, SW: 0x2B, BEQ: 0x04, BNE: 0x05, ADDI: 0x08, R: 0x00} FUNC {ADD: 0x20, SLT: 0x2A, SYSCALL: 0x0C} def decode(op, func): 输入一条指令的 OP 与 FUNC输出该指令应产生的控制信号 d dict(R_TYPE0, ADDI0, LW0, SW0, BEQ0, BNE0, SYSCALL0) d[LW] int(op OP[LW]) d[SW] int(op OP[SW]) d[BEQ] int(op OP[BEQ]) d[BNE] int(op OP[BNE]) d[ADDI] int(op OP[ADDI]) if op OP[R]: # R 型指令靠 FUNC 区分 add int(func FUNC[ADD]) slt int(func FUNC[SLT]) d[SYSCALL] int(func FUNC[SYSCALL]) d[R_TYPE] (add | slt) (1 - d[SYSCALL]) # syscall 不算 R 型运算 return { RegWrite: d[R_TYPE] | d[ADDI] | d[LW], RegDst: d[R_TYPE], MemToReg: d[LW], MemWrite: d[SW], MemRead: d[LW], AluSrcB: d[LW] | d[SW] | d[ADDI], Beq: d[BEQ], Bne: d[BNE], Halt: d[SYSCALL], } CASES [(add, 0x00, 0x20), (slt, 0x00, 0x2A), (addi, 0x08, 0), (lw, 0x23, 0), (sw, 0x2B, 0), (beq, 0x04, 0), (bne, 0x05, 0), (syscall, 0x00, 0x0C)] for name, op, func in CASES: print(f{name:8s}, decode(op, func))这段脚本的关键在 decode 函数里 R_TYPE 那一行它先算出 ADD 和 SLT再用 (1 - SYSCALL) 把 syscall 从 R 型里剔除。如果偷懒写成 op 0输出里 syscall 的 RegWrite 会是 1等于让停机指令去写寄存器行为就不可控了。跑完把八行输出跟你手工画的控制信号表逐列比对不上的地方就是你表里写错的那一格。参数上要注意 op 和 func 都是 16 进制整数直接跟字典里的常量比较即可不需要手动拆位。3.4 单周期跑通 sort.hex 的验收顺序单周期 CPU 搭完别急着直接加载 sort.hex。第一次就上排序程序一旦出错你面对的是一堆寄存器值根本判断不了是哪条指令的问题。我后来的做法是分四步走先写一条只有 add 的十六进制程序看寄存器能不能正确写回再加 addi 和 lw验证立即数和访存然后只放 beq 和 bne看 PC 跳得对不对最后才上 sort.hex。每一步的测试程序都短到肉眼能算出预期结果出问题就能立刻定位到具体是哪一类指令。4. 多周期改造结构与 13 个状态怎么落地单周期跑通之后第二阶段的任务是把同一个 CPU 改成多周期而且控制器要做两遍一遍微程序控制器一遍硬布线控制器。这一步看着像“重复劳动”实际上是在逼人理解一个关键区别单周期里所有事在一个时钟周期内并行做完多周期里同一个功能部件被不同指令在不同时刻反复借用所以数据通路和控制器都得重新组织。4.1 从单周期到多周期动的是结构不是连线多周期的第一个结构性变化是存储器不再区分指令和数据。单周期里有 IMEM 和 DMEM 两个存储器多周期里只留一个取指令和读写数据分时使用它。第二个变化是功能部件分时复用一个 ALU 在不同时钟周期里既算 PC4也算地址偏移还算加法和比较。为了实现分时复用所有后面周期还要用的数据都必须锁存下来。多周期版本里新增了几个寄存器DR 存从存储器读出的数据IR 存取出的指令A、B 分别存 RegFile 两个读口的输出C 存 ALU 的结果。这些寄存器的作用是“把当前周期的结果带到下一个周期”没有它们下一个时钟沿一到数据就没了。PC 的处理也变了。单周期里 PC 每个时钟都自动加 4多周期里不同指令用的周期数不同PC 不能每个时钟都更新。做法是给 PC 单独加一个写使能信号 PcWrite只在取指那个周期置 1。ALU 的输入也改了。PC4 原来靠一个独立加法器现在改由 ALU 来算所以 ALU 的 B 输入端从两个来源变成四个rt 的值、符号扩展立即数、常数 4、常数 4 之外的另一个偏移计算项用一个四路选择器切换。A 输入端也多了一路 PC 值用一个二路选择器切换。部件单周期多周期存储器IMEM DMEM 分开一个存储器分时复用ALU只算运算和地址还负责 PC4 和分支偏移计算结果暂存无IR、DR、A、B、C 五个寄存器PC 更新每周期自动 4由 PcWrite 控制停机Halt 信号拉低 PC 使能状态自循环不需要 Halt停机方式的变化值得单独说。多周期里 syscall 不再产生 Halt 信号而是让状态机停在 syscall 对应的那个状态里反复循环PC 不更新取指不再发生效果等同于停机。这是“用状态变迁实现功能”的典型例子比加一根控制线更自然。4.2 状态划分与微指令表多周期的核心是状态划分。每条指令拆成“取指 → 译码 → 执行”前两个状态所有指令共用执行阶段根据指令不同占 1 到 3 个状态。按这份设计13 个状态编号 0 到 13状态功能主要微操作0取指令IR ← Mem[PC]PC ← PC41译码取数A ← R[rs]B ← R[rt]C ← PC 扩展偏移2lw 算地址C ← A 扩展偏移3lw 访存DR ← Mem[C]4lw 写回R[rt] ← DR5sw 算地址C ← A 扩展偏移6sw 写内存Mem[C] ← B7R 型加法C ← A B8R 型写回R[rd] ← C9beq 分支相等则 PC ← C10bne 分支不等则 PC ← C11addi 运算C ← A 扩展偏移12addi 写回R[rt] ← C13syscall 停机下址指向自身PC 冻结注意状态 1 里 C 寄存器的赋值是 PC 加偏移。这里的 PC 已经是被状态 0 更新过的 PC4所以 C 里存的天然就是“PC4偏移左移两位”这正是分支目标地址。这个细节在单周期里靠一个加法器实现在多周期里靠状态顺序“免费”实现了理解这一点后面写状态机就不会把基址搞错。4.3 微指令编码与地址转移逻辑微程序控制器的思路是把每个状态需要的所有控制信号打包成一个二进制字称为一条微指令所有微指令按地址排列存进一个只读存储器状态编号就是微指令地址。控制器执行时用当前状态当地址去查表输出就是这一周期全部控制信号。微指令里还要留一段“下址字段”指明下一条微指令的地址。下址字段的填法有三条规则。取指微指令的下址是译码微指令的地址固定 1。非最后一条微指令的下址是下一条微指令的地址顺序加 1。每条指令执行阶段的最后一条微指令下址填 0表示“回到取指状态”。而译码微指令的下址也填 0但它不是回到取指而是触发地址转移逻辑——根据当前 7 个译码信号选一个入口地址。地址转移逻辑本身就是一张真值表7 个输入信号输出 4 位入口地址。Logisim 的组合逻辑分析功能可以直接从真值表生成电路不需要手工化简。表里的每一行对应一条指令比如 LW 有效时输出 2SW 有效时输出 5R_TYPE 有效时输出 7ADDI 输出 11SYSCALL 输出 13。微指令表填完后导出一个文本文件每行是一条微指令的二进制串加载到控制存储器组件里就完成了。这一步最容易出的问题是位宽不一致某一行多打一位或者少打一位加载进去之后所有后续行的地址全错位现象是“前面几条指令正常跑到某条指令忽然全乱”。我一般会写几行脚本把导出的文本核一遍。# 核对导出的微指令文本每行位宽一致下址字段落在合法状态范围内 WIDTH 21 # 一行微指令的总位数按实际设计调整 NEXT_LO 0 # 下址字段起始位 NEXT_HI 3 # 下址字段结束位 VALID set(range(14)) # 合法状态编号 0~13 with open(microcode.txt) as f: lines [ln.strip() for ln in f if ln.strip()] for i, ln in enumerate(lines): assert len(ln) WIDTH, f第 {i} 行位宽 {len(ln)}应为 {WIDTH} assert set(ln) {0, 1}, f第 {i} 行含非 0/1 字符 nxt int(ln[NEXT_LO:NEXT_HI 1][::-1], 2) # Logisim 习惯低位在右 assert nxt in VALID, f第 {i} 行下址 {nxt} 不是合法状态 print(f{len(lines)} 行微指令位宽与下址全部通过)这段脚本做三件事检查每行长度是否等于微指令总位宽、检查是否只有 0 和 1、把下址字段截出来转成整数看是否落在 0 到 13。第三项检查最有用因为下址填错一位状态机就会跳到一个空状态现象是程序跑到某条指令后彻底停住查起来毫无头绪。位宽和字段位置要按你自己的微指令格式改别照抄。5. 避坑记录五个把我按在机房到凌晨的具体问题这一节列的都是我实际撞过的墙每个都按“现象 → 原因 → 解决”写。有些坑看着低级但在调试到第三个小时的时候人就是会往复杂的方向想。5.1 跑 sort.hex 永远不停机PC 一直往前走现象程序前面排序逻辑都对输出也基本正确但永远不结束PC 一直递增波形上看不停地在取指。原因单周期版本里syscall 的 Halt 信号接到了 PC 使能端但 Logisim 里 PC 组件的使能是高电平有效还是低电平有效没确认清楚Halt1 时 PC 反而继续更新。多周期版本里则是 syscall 状态的下址字段填成了 0等于每周期都重新取指。解决单周期先单独测——手动把 Halt 置 1看 PC 是否真的停住确认有效电平再接。多周期的 syscall 微指令下址字段必须等于它自己的状态编号 13让状态机原地打转。5.2 所有访存地址都差 4 倍数组读出来全是脏数据现象lw 读出来的值和预期完全不符但寄存器运算类指令结果都对。把存储器内容打印出来看访问的地址是预期的四分之一。原因PC 是字节地址指令存储器和数据存储器的地址端口是 10 位字地址我直接把 PC 的低 10 位接了上去等于把字节地址当字地址用。解决加分离器取 PC 的第 2 到第 11 位送进存储器地址端。数据存储器同样处理凡是从 ALU 算出来的字节地址都要先右移两位再送进 DMEM。5.3 分支跳转落点总是差一条指令现象beq 该跳的时候确实跳了但跳过去之后执行的指令不对整体结果偏一位。原因分支目标地址用的是当前 PC 加偏移而不是 PC4 加偏移。单周期里 PC 在取指后已经变成下一条指令地址但我在数据通路上把旧 PC 直接引到了分支加法器。解决分支目标地址固定为 (PC4) 符号扩展(imm) 2两个细节都不能省基址是 PC4偏移要左移两位。多周期里因为 C 寄存器在译码状态存的就是已经 4 的 PC反而不容易错。5.4 只有 R 型指令结果写错寄存器现象add 和 slt 算出来的值是对的但写进去的寄存器不对后续依赖这个结果的指令全部连锁出错。原因RegDst 信号在 R 型执行状态下没有置 1写回地址选择器默认选了 rt 而不是 rd。因为 add 的 rd 和 rt 字段有时候恰好相同小测试里看不出来跑排序就暴露了。解决回到微指令表逐行核对每个状态下 RegDst 的取值——只有 R 型的写回状态是 1其余全是 0。用 3.3 那类脚本把每条指令的控制信号打出来对照比看波形快。5.5 状态机复位后跑飞第一个时钟就不在状态 0现象按下复位状态寄存器输出不是 0或者第一个时钟沿之后跳到一个没人定义的状态整个 CPU 卡死。原因状态寄存器没有接复位信号次态组合逻辑的部分输入悬空Logisim 对悬空输入默认为不定态真值表生成电路时没有覆盖无关项。解决状态寄存器必须接复位并确认复位值。次态逻辑的所有输入都要有确定来源未使用的输入在 Logisim 里显式设默认值 0。用真值表自动生成电路后逐个状态手动仿真一遍次态确认 13 个状态的跳转都符合预期再上整程序。注意这五个坑里前三个属于“通路接错”第四第五个属于“控制或状态错”。调试时先判断现象属于哪一类——如果所有运算类指令都对、只有访存错那就是通路问题如果通路都对、个别指令行为异常那就是控制信号或状态机问题。按类缩小范围比逐根线查快得多。6. 微程序 vs 硬布线同一张状态图两套控制器怎么选做完两遍控制器之后我对这两者的差别有了很具体的感受不是教科书上那句“微程序易修改、硬布线速度快”。它们用的是同一张状态变换图、同一套控制信号定义甚至同一批译码逻辑区别只在于“状态到控制信号”这个映射怎么落地。维度微程序控制器硬布线控制器实现载体控制存储器存微指令组合逻辑门电路改一条指令的代价改一行微指令文本重新加载改电路重新生成次态逻辑调试手段直接看微指令表逐行对照看波形反推是哪一级逻辑错元件规模一个存储器加地址转移逻辑大量门电路规模随状态数增长出错概率集中在填表一次填错影响一行分散在逻辑一处错影响多个状态上手难度前期理解成本高后期改起来轻前期直观状态一多就乱我自己的结论是如果状态数在十几个、指令集还要反复调整微程序控制器明显更省事——改一条指令就是改一行二进制加载进去立刻生效不用重新生成整块电路。硬布线的优势在调试路径清晰每个控制信号都能顺着门电路倒推回是哪几个译码信号或出来的出了问题能定位到具体那一级。但真正让我记住这一章的是硬布线那次翻车。状态机用真值表自动生成之后我没逐个状态验证就把程序灌进去了结果跑到 lw 的第三个状态就跳飞。查了两个小时最后发现是次态真值表里有一行的无关项被我填成了具体值导致某个不该出现的状态组合下跳到了一个不存在的编号。从那以后我每次生成状态机都强制把 13 个状态的次态逐个仿真一遍不管多赶时间这一步都不跳。硬布线的错误不像微指令那样能一眼看出一行不对它藏在逻辑里只有把每个状态都走一遍才敢说通了。回过头看这个实验真正教给我的不是怎么连一条 MIPS 数据通路而是一套拆解方法先把指令拆到微操作级别再从微操作反推控制信号最后用一张状态图把时序串起来。单周期、多周期、微程序、硬布线四种组合看着是四份作业其实是同一套逻辑的四种表达。后来做别的硬件或底层相关的东西这套“先定状态、再定信号、最后核表”的顺序我一直沿用。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?