1. 单总线CPU设计课程概述
这个系列实验是计算机组成原理课程的核心实践环节,通过7个递进式实验关卡,带你从零开始构建一个完整的单总线CPU。不同于教科书上的理论讲解,这套实验最硬核的地方在于:你需要亲手在Logisim仿真平台上,用数字电路实现MIPS指令译码、微程序控制、硬布线控制等CPU核心部件。
我当年做这个实验时踩过不少坑,比如微程序入口地址算错导致指令执行乱跳、状态机设计缺陷引发死循环等。通过这个系列教程,我会把调试经验毫无保留地分享给你,让你少走弯路。
2. 实验环境准备
2.1 Logisim软件配置
推荐使用Logisim-evolution版本(2.14.1以上),它对大型电路的支持更好。安装后需要做两个关键设置:
- 在"Preferences→Simulation"中将Tick Frequency调至1Hz,避免信号竞争
- 在"Project→Load Library"添加Arithmetic库,后续会用到32位比较器
2.2 工程文件结构
实验包包含7个主要电路文件:
- MipsOnBusCpu-1.circ(基础框架)
- control_store_data.xlsx(微指令编码表)
- sort-5.hex(测试程序)
- 各关卡的参考答案(建议先自己尝试)
重要提示:不要直接打开参考答案!先按实验指导自己实现,遇到问题再对比参考。
3. 核心实验详解
3.1 第1关:MIPS指令译码器设计
3.1.1 指令格式解析
MIPS指令分为三种基本格式:
code复制R-type: | opcode(6) | rs(5) | rt(5) | rd(5) | shamt(5) | funct(6) |
I-type: | opcode(6) | rs(5) | rt(5) | immediate(16) |
J-type: | opcode(6) | address(26) |
需要识别的指令及对应opcode:
code复制LW 100011
SW 101011
BEQ 000100
SLT 000000 (funct=101010)
ADDI 001000
3.1.2 电路实现步骤
- 添加6位比较器(Comparator),设置对应opcode值
- 用多路选择器整合R-type指令的特殊判断(SLT需要同时检查opcode=0且funct=101010)
- 最终输出信号用隧道(Tunnel)标注清晰,例如:
logisim复制LW -> [Opcode=100011] SLT -> [Opcode=000000] AND [Funct=101010]
3.1.3 调试技巧
- 使用"Simulate→Test Vector"功能批量验证
- 常见错误:忘记处理OtherInstr情况,导致输出高阻态
3.2 第2关:微程序入口查找逻辑
3.2.1 微程序地址映射
根据指令类型生成5位入口地址:
code复制LW 00001
SW 00101
BEQ 01001
ADDI 01101
SLT 10001
3.2.2 组合逻辑实现
推荐使用PLA组件实现真值表:
- 创建新的PLA,设置5输出(对应地址位)
- 输入位连接指令译码信号
- 填写编程表:
code复制LW 1 0 0 0 0 → 0 0 0 0 1 SW 0 1 0 0 0 → 0 0 1 0 1
3.2.3 注意事项
- 地址必须严格对齐微指令存储器的页面边界
- 保留全0地址给取指周期使用
3.3 第3关:条件判别测试逻辑
3.3.1 判别信号解析
- P1P0=00:无条件跳转
- P1P0=01:根据equal跳转(用于BEQ)
- P1P0=10:根据指令opcode跳转
3.3.2 电路设计
logisim复制MUX_2WAY(
select = P1,
inputs = [
[P0 AND equal], // P1=0
[P0 AND opcode] // P1=1
]
)
3.4 第4关:微程序控制器集成
3.4.1 数据通路连接
- 将微指令存储器(Control Store)的位宽设置为32位
- 关键字段划分:
- 下址字段:8位
- 判别位:2位
- 控制信号:22位
3.4.2 微指令编程示例
以LW指令为例:
code复制地址 下址 P1P0 控制信号
00001 00100 00 MemRead=1, RegWrite=0...
00100 01000 01 ALUSrcA=1, ALUSrcB=10...
3.5 第5关:单总线CPU联调
3.5.1 总线仲裁设计
采用三态门实现总线共享:
logisim复制ALUBus -> TriState -> MainBus
MemBus -> TriState -> MainBus
RegBus -> TriState -> MainBus
3.5.2 时序控制要点
- 每个时钟周期分为两个相位:
- Φ1:发送控制信号
- Φ2:数据稳定传输
3.6 第6关:硬布线状态机设计
3.6.1 状态编码方案
推荐使用One-hot编码:
code复制S0: 00000001
S1: 00000010
...
S7: 10000000
3.6.2 次态逻辑实现
以BEQ指令为例:
code复制if (current_state == S2 && BEQ && equal)
next_state = S5;
else if...
3.7 第7关:硬布线控制器集成
3.7.1 控制信号生成
复用微程序控制存储器:
- 将状态编码作为地址高位
- 指令译码信号作为地址低位
- 输出直接连接控制总线
3.7.2 性能对比
- 微程序方式:平均CPI=4.2
- 硬布线方式:平均CPI=3.8
4. 调试与优化技巧
4.1 信号追踪方法
- 使用Logisim的"Clock Simulator"单步执行
- 关键信号添加探针(Probe)
- 记录波形时序图辅助分析
4.2 常见故障排查
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 指令死循环 | 下址计算错误 | 检查判别测试逻辑 |
| 数据写入失败 | 总线冲突 | 检查三态门使能时序 |
| 运算结果错误 | ALU控制信号不匹配 | 核对微指令编码 |
4.3 性能优化建议
- 合并冗余微指令(如ADDI和LW的前两个周期可共用)
- 采用提前分支判断(在译码阶段预判BEQ)
- 关键路径插入流水线寄存器
5. 实验心得
做完这组实验最大的收获是理解了CPU控制器的两种实现范式:微程序方式就像"查字典",设计简单但效率较低;硬布线方式如同"背课文",需要精心设计但执行更快。实际项目中通常会混合使用——简单指令用硬布线,复杂指令走微程序。
调试时最有效的技巧是"分治法":先验证每个独立模块的功能,再逐步组装。记得保存不同版本的电路文件,方便回溯比较。当sort-5.hex最终正确执行时,那种成就感绝对值得你投入的每一分钟!
