ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

模型机设计通关指南:16条指令与微程序控制器实战解析

模型机设计通关指南:16条指令与微程序控制器实战解析 简介面向哈尔滨工程大学计算机组成原理课程设计场景压缩包提供基于Quartus完成的16条指令模型机完整工程与实验报告。包内共340个文件既有qpf/qsf/bdf工程原理图、vwf仿真波形也有v/vhd/tdf硬件描述源码、doc/txt报告等7.06MB的体积便于整包下载工程按顶层、控制器、ALU等模块拆分便于对照数据通路、控制器和运算器的设计实现。已有345人学习下载适合正在做同类课程设计或希望掌握Quartus建模流程的同学参考。读者可直接打开工程查看原理图与源码配合实验报告理解16条指令的译码、执行与仿真验证过程也可借鉴其模块划分与报告写作思路。 每次到这个时间课程群里就会被同一类问题刷屏“模型机设计课上用的16条指令到底怎么定”“微程序写进去跑不起来怎么办”说实话哈尔滨工程大学的计算机组成原理课程设计里“模型机设计16条指令”是一道非常经典的综合题。它不要求你做出能跑操作系统的CPU但要求你把“指令系统、数据通路、控制器”这一整条链路真正贯通。这篇文章就围绕这个课设把我当时从拿到题目到验收通过的全过程、踩过的坑、以及后来帮同学排查时的经验整理出来给正在做这个题目的你一个可复现的参考思路。1. 拿到“模型机设计”题先搞明白课设真正想考什么1.1 一套模型机到底由哪几块拼起来很多同学一上来就急着查“16条指令是哪16条”这种思路其实反了。模型机设计这个课设真正想考的不是背诵指令表而是下面这几件事指令系统的设计操作码怎么分配寄存器字段怎么安排寻址方式怎么表达。数据通路的连接CPU内部的寄存器、ALU、存储器、总线之间怎么连数据从哪儿到哪儿。控制器的实现每条指令分几步执行每一步产生什么控制信号。验证与调试能不能用一条条指令的实际运行结果证明你的设计正确。把这四件事翻译成物理模块就是一台模型机的标准配置程序计数器PC、指令寄存器IR、通用寄存器组、算术逻辑单元ALU、内存RAM、地址寄存器MAR、数据寄存器MDR以及产生控制信号的控制器。这个规模说大不大但麻雀虽小五脏俱全。1.2 哈工程实验环境下的常见实现路径哈工程这边的课设实现方式不同老师带的不同班会有差别。我们当时用的是实验室里的硬件实验箱整机采用“排线连线 微程序控制器”的方式微程序存放在RAM里通过拨码开关或上位机写入。也有同学用Verilog把模型机写到FPGA里跑仿真两种路线各有各的难点。如果你用的是实验箱最痛苦的不是逻辑设计而是连线和时序。一根线插错、一个三态门没控制好都是问题。如果你用Verilog逻辑好改但仿真波形里的组合竞争、寄存器写时序同样容易翻车。这篇文章我会以“实验箱 微程序控制器”为主线来讲因为这是哈工程最常见的形式如果你做的是FPGA版本核心思想完全一样只是排查手段从示波器变成了波形图。2. 16条指令的指令集设计格式、分类和取舍逻辑2.1 指令格式怎么定才不会被自己绕晕模型机毕竟是课设不需要像真实CPU那样搞复杂变长指令。我强烈建议你做定长16位指令字并且把字段位置固定下来。这是我在设计时最终确定的格式OP(4位) | DR(4位) | SR(4位) | ADDR(4位)各位字段的含义OP操作码4位最多支持16条指令题目要求16条刚好用满。DR目的寄存器编号4位可以访问16个寄存器。SR源寄存器编号4位。ADDR地址或立即数字段4位。你可能会问4位地址只能访问16个内存单元够用吗对于模型机验证来说够了。我们当时配套的存储阵列就是16个单元跑一个演示程序绰绰有余。如果指导老师要求更大的存储空间可以把指令字改成OP(4) DR(4) ADDR(8)牺牲SR字段来换地址空间这是后话。2.2 16条指令的分类与最终清单16个操作码刚好占满4位二进制。我当时把指令分成了四类这样无论是写微程序还是画状态图都清晰很多类别操作码指令功能格式控制类0000HALT停机无操作数数据传送类0001MOV Rd, Rs寄存器间传送DR, SR数据传送类0010LDR Rd, [Addr]从内存读入寄存器DR, ADDR数据传送类0011STR [Addr], Rs从寄存器写入内存SR, ADDR运算类0100ADD Rd, Rs加法DR, SR运算类0101SUB Rd, Rs减法DR, SR运算类0110AND Rd, Rs逻辑与DR, SR运算类0111OR Rd, Rs逻辑或DR, SR运算类1000XOR Rd, Rs逻辑异或DR, SR运算类1001NOT Rd取反DR运算类1010INC Rd加一DR跳转类1011JMP Addr无条件跳转ADDR跳转类1100JZ Addr零标志为1时跳转ADDR跳转类1101JC Addr进位标志为1时跳转ADDR输入输出类1110IN Rd读入开关值到寄存器DR输入输出类1111OUT Rd寄存器值输出到指示灯DR这套指令集有几个刻意为之的取舍。第一我没有单独设NOP空操作指令因为MOV R0, R0完全可以充当空操作省出一个操作码给输入输出。第二LDR和STR都显式带上地址字段而不是用寄存器间接寻址这样取指后可以直接把指令中的ADDR字段送上地址总线控制器的节拍会少一拍写起来更省心。第三JZ/JC这种条件跳转在模型机里非常经典它要求控制器能够读取ALU产生的零标志Z和进位标志C这是很多同学一开始容易忽略的。2.3 为什么一定要把“执行周期不同”这件事提前想清楚RISC风格里大家都在谈单周期或多周期模型机几乎默认采用多周期方式。这就意味着不是每条指令都走同样多的节拍。按我的设计各类指令的执行周期差异如下取指周期是公共的每条指令都必须先经过送PC到MAR、读内存、PC自增、指令装入IR。MOV、NOT、INC这类纯寄存器操作在取指之后一般只需要1到2个周期。LDR、STR这类访问内存的指令需要额外做MAR送地址、读或写内存周期会长一点。JMP/JZ/JC这类跳转指令需要把目标地址回写到PC最关键的是条件跳转还要判断标志位。这个差异直接决定了微程序的长度和微地址的分配。如果你一开始没做好规划后面微程序跳转关系会乱成一团。我的习惯是先在纸上列出一个“指令周期表”每条指令占几行、每行做什么写清楚再动手编微程序。3. 数据通路与微操作序列从“能画框图”到“能跑起来”3.1 单总线还是多总线我选单总线的理由模型机数据通路最基础的问题是总线结构。我记得当时有两种选择单总线和双总线。单总线结构下所有部件共享一条数据总线同一时刻只能有一个源向总线发送数据双总线则允许两个源同时往ALU的A端和B端送数。我最终选了单总线。原因很现实单总线控制信号少连线少调试时观察总线上的值非常直观。代价是取指和运算需要多拆几个节拍性能差一点但课设完全不在乎这一点性能稳定和好查错才是第一位的。如果你用FPGA单总线也能有效避免“到处都能写数据”的竞争问题。单总线模型机里每个能够向总线输出数据的部件前面都必须加三态门由各自的控制信号决定是否驱动总线。对应关系大概是部件输出控制信号说明PCPC_OUT送PC值到总线MDRMDR_OUT送MDR内容到总线ALU的结果寄存器ALU_OUT送运算结果到总线IR字段提取器IR_OUT送指令中的地址字段到总线寄存器组R_OUT根据SR选择寄存器并输出“同一时刻只能有一个输出控制信号有效”这句话我后来在排错时反复验证过很多次。谁违反了总线上就打架数据全是乱的这属于模型机调试里最经典的坑之一。3.2 取指周期的微操作序列不管控制器是微程序还是硬布线取指周期都长一个样。我习惯用箭头描述微操作每一行表示一个节拍内同时发生的动作T1: PC - MAR; PC 1 - PC T2: RAM - MDR T3: MDR - IR说明一下PC加1和PC送MAR放在同一拍是因为这两个动作互不冲突RAM读出的数据出现在T2经过一个存取周期后才能稳定被MDR捕获T3把指令内容锁存进IR。这里有个容易被忽略的小细节模型机的PC自增必须在取指早期完成否则跳转指令执行时PC其实是“下一条指令的地址”如果跳转判断失败PC保持这个值才能继续执行下一条。3.3 用三条典型指令拆微操作序列只说框架难免空我拿ADD、STR、JZ三条指令举例你感受一下微操作序列长什么样。ADD Rd, Rs的执行序列T4: SR - ALU_B T5: DR - ALU_A; ALU_OP ADD T6: ALU_OUT - DR; 更新Z标志和C标志ADD的关键在于它取指时已经把Rd和Rs的编号从IR里提取出来了。T4先读源寄存器T5再读目的寄存器到ALU的A端同时让ALU执行加法T6把结果写回目的寄存器并更新标志位。为什么不能T4同时送两个寄存器到单总线因为单总线一次只允许一个源出现这也是单总线的代价。STR [Addr], Rs的执行序列T4: IR的Addr字段 - MAR T5: SR - MDR T6: RAM写使能有效, MDR - RAM这里有个先后顺序必须死记**先送地址到MAR再送数据到MDR最后才发写使能。**我刚做的时候把T5和T4的顺序写反了结果每次执行STR数据总是写进上一次操作留下的旧地址里排查了将近半小时。这个坑太典型后面专门讲。JZ Addr的执行序列T4: 判断Z标志 T5: 若Z1: IR的Addr字段 - PC; 控制器转移到下一条 T6: 若Z0: PC保持上一步取指后的值条件跳转最微妙的是“要不要修改PC”这个动作。无条件跳转JMP无论在什么情况下都要在T5把Addr送到PCJZ则必须先看Z标志。控制器里需要设计一个条件选择信号我习惯叫它COND当COND为0时强制条件成立当COND为1时看Z当COND为2时看C。这样微程序可以复用同一段跳转流程只是COND字段不同。3.4 微程序控制器怎么组织字段直接控制最后说一下控制器本身。我用的微程序控制器本质上就是一块专用的“微指令存储RAM”每条微指令是一个固定宽度二进制数每一位或每一组字段控制一个操作。我的微指令字段大致这样划分源选择字段控制PC_OUT、MDR_OUT、ALU_OUT、IR_OUT、R_OUT等。目的写使能字段控制PC_LD、IR_LD、MAR_LD、MDR_LD、寄存器写使能等。ALU操作字段选择加法、减法、与、或、异或、取反、加一。内存读写字段控制RAM的读/写。微地址迁移字段指出下一条微指令地址以及是否根据Z/C标志跳转。使用微程序而不是硬布线的原因很简单微程序把“每条指令对应一串微操作”这件事变得高度可定制。你发现ADD时序不对只需要改ROM里ADD对应区域的微指令内容而不需要重新搭一堆门电路。这个设计思路和真实CPU里的微码控制也是一脉相承的所以课设里弄懂它价值和意义不止于应付验收。4. 微程序跑飞了怎么办一条完整的排错链路4.1 第一条指令就翻车现象、定位、修复我印象最深的一次排错发生在第一次往实验箱里写微程序。当时我只写了HALT和MOV两条指令想先把基本链路跑通。结果一上电指示灯乱跳IR里读出来的指令根本不是我预置的那条。排查顺序是这样的先看总线上跑的值。实验箱上一般有总线指示灯把单节拍模式打开一拍一拍看总线数据。第一拍PC送MAR地址正确。第二拍RAM读数据发现RAM输出端有数据变化。第三拍MDR送IR问题出现了IR里的值和RAM输出对不上。最终定位下来说是MDR的装载时序问题RAM数据在T2已经稳定但MDR的装载脉冲在T3才有效中间数据早就变了。换句话说我在写微程序时把“MDR锁存”和“MDR输出到IR”混在了同一拍。正确做法是在T2结束前把RAM数据锁存进MDRT3再执行“MDR - IR”。调整微程序后取指链路立刻正常。这个案例给我的教训是模型机调试不要靠猜一定要打开单节拍模式逐拍观察总线。实验箱上的LED灯就是最廉价的“示波器”。4.2 从冒烟测试到覆盖测试指令测试矩阵指令全部写完以后不要上来就跑复杂的演示程序。我当时总结了一个测试顺序非常管用先测不需要访问内存的纯寄存器指令MOV、ADD、SUB、AND、OR、XOR、NOT、INC。再测访问内存的指令LDR和STR。接着测输入输出IN和OUT。最后测跳转指令HALT、JMP、JZ、JC。每测一条指令都做两件事记录初始寄存器值、看运行后的寄存器和内存值、和预期值比对。手工比对很繁琐但模型机就16条指令用不了多少时间。我习惯把结果整理成一张测试矩阵大致长这样测试指令初始条件预期结果实际结果是否通过MOV R1, R2R25R15R15通过ADD R3, R4R31, R42R33R33通过STR [3], R1R15, 内存[3]0内存[3]5内存[3]0失败这个表格随便做但记录的每一行都有价值。验收的时候老师问“你测过哪些情况”你直接把这个矩阵递过去比你空口说“都测了”有说服力得多。4.3 三个高频坑几乎每届都会有人踩第一个坑是总线竞争。表现是运行同样的指令结果时好时坏随机出错。原因是某一个节拍里同时打开了两个输出控制信号比如ALU_OUT和R_OUT同时有效两个部件往一根总线上灌数据。修复方法很简单把每条微指令的所有输出控制信号列个清单检查同一拍里是否出现两个1。第二个坑是写使能时序。寄存器写使能和ALU结果到达的时间对不上会出现写进去的是旧数据。这个问题的排查要点是看写脉冲和数据到达的先后关系。数据必须先稳定写脉冲再有效而且写脉冲宽度要足够否则寄存器可能只捕获到一半数据。第三个坑是条件跳转后PC没变化。表现是JZ该跳没跳或者不该跳却跳了。原因通常是控制器里没有把“条件成立”和“PC装载”两个条件合在一起。我的办法是把PC_LD信号设计成PC_LD 控存跳转条件成立 AND 当前跳转指令条件满足。检查这一步需要先确认Z标志有没有生成了再看COND字段有没有送进控制器。标志位生成慢、或者标志位在ALU运算后没有更新也是常见原因。4.4 实验箱上机与Verilog仿真的差异如果你用的是Verilog实现排错手段比实验箱更舒服。ModelSim或Vivado的波形图可以看任意一根内部信号所以遇到问题先定位是哪一拍出错再往回追数据来源。但Verilog也有自己的坑仿真里信号都是理想时序不会有实验箱上那种电平竞争和接触不良的问题所以仿真通过不代表上板能过。我见过很多同学的FPGA版本在仿真里一切正常烧到板子上LED就是不亮后来发现是时钟没有做同步处理或者复位信号没有释放。这部分建议你在做Verilog版本时多注意时钟域、异步复位和按键消抖模型机虽然简单这些基本功一样不能含糊。5. 验收准备与文档课设分数不只看通电那一下5.1 演示脚本要提前演练验收的时候大概率会让你现场跑一遍程序。不要现场翻手册提前准备一个两分钟演示脚本。我当时准备的程序是用IN从开关读入两个数到R1、R2。用ADD算和。用OUT把结果显示到LED灯。用一个循环例子演示JMP和JZ比如从某个内存地址累加加到0跳出。这个脚本覆盖了输入、运算、输出、跳转四类指令演示时长刚好老师看了也觉得思路完整。演示程序不要贪多关键是中间每一步能讲清楚“现在执行到哪条指令、寄存器里的值为什么变了”。能在演示的同时指着实验箱说清楚这一步比默默跑完整个程序要高一个层次。5.2 文档里最少要画的三张图哈工程对课设报告的格式要求通常比较严但不管格式怎么变下面三张图必须有第一张是指令格式图标明4位操作码、4位DR、4位SR、4位ADDR。旁边配上16条指令的编码表。第二张是数据通路图把所有寄存器的输入端、输出端、控制信号、三态门画全。这张图是你设计的地图也是老师判断你懂不懂模型机的重要依据。画的时候记得标清楚每根线的方向尤其是MAR和MDR的输入来源。第三张是控制器状态转移图或微程序流程图。我建议画成“取指公共部分 每条指令的执行子流程”的形式一眼就能看出每条指令分几步。如果条件跳转有多个分支把分支条件标在箭头上。这三张图齐全报告的可信度就会有保障。反过来如果文档里连数据通路都没画运行结果再漂亮老师也可能认为你就是别人代做的。5.3 老师最爱问的几个“防御性提问”验收最怕的不是机器跑不出来而是老师问设计思路时答不上来。下面几个问题我几乎每次帮同学模拟验收都会问你可以提前想好答案“为什么用微程序而不用硬布线”答微程序修改方便适合教学验证硬布线速度快但设计复杂度高。“如果让你扩展到32条指令哪些地方要改”答操作码从4位扩到5位控存容量需要增加指令格式可能需要加长或变长其余数据通路不用大改。“单总线有什么缺点”答同一时间只能传输一个数据所以运算和访存要拆成多个节拍性能较低。“为什么STR要先送地址再送数据”答MAR地址先行可以避免写使能有效时由于地址未稳定导致写入错误单元同时单总线结构也不允许地址和数据同时出现在总线上。这些问题本身不难但很能检验你是不是真的理解了设计。建议你在验收前把设计文档从头到尾翻一遍尤其是自己当时觉得绕的地方多问自己几遍“为什么”。能答出“为什么”往往比答出“是什么”更让老师认可。5.4 最后一点个人体会从完成模型机设计到后来帮同学调实验我最大的体会是这个课设并不需要多高深的理论它真正在训练的是你“把一条指令从脑子里变成机器上可执行动作”的能力。很多同学卡住不是卡在不懂原理而是卡在“原理书上的话和实验箱上的线对不上”。破解方法只有一个就是逐拍跟着数据走一遍把每个控制信号都看明白。这个工作量前期显得有点枯燥但等你把16条指令的微程序全部调通再回头看组成原理书上的控制器章节会突然觉得书上写的每一句话都变成了看得见摸得着的东西。这种感觉大概就是这个课设最值得的部分了。本文还有配套的精品资源点击获取
返回列表