我记得这是2016春季学期搞得,参考和学习了很多别人的东西,这里小小的总结一下,逻辑性还不是太强,还需要好好整理
首先是指令集
CPU架构
外部接线架构
指令格式
机器状态自动机
这部分忘了,汗
这部分也忘了
忘了
忘了
回写逻辑
计组物理实验所用
μAi表示微地址的一个bit,i表示哪一位
上述机器的指令译码逻辑图
寄存器译码逻辑图
该机器下微程序设计示例
上述微程序地址对应指令为
关于74LS299B
一些概念解释
实现流水线CPU的关键是如何解决冲突
在我们之前的测试中可以看到,指令之间加入了许多额外的NOP指令,这都是为了避免指令冲突,插入NOP可以有软件的实现方式(编译器),也可以通过硬件实现,即CPU检测到冲突的时候就执行NOP指令。不过对于CPU的设计者来说,这极大地降低了CPU的性能,是不可容忍的。下面我来详细讲解指令冲突的不同场合以及避免措施。
流水线处理中,由于各个阶段的依赖关系、硬件资源的竞争等原因,会出现操作无法同时执行的情况。造成流水线故障的原因称为冒险,冒险分为构造冒险、数据冒险和控制冒险 3 种类型。
构造冒险
由于硬件资源的竞争,操作无法同时执行的情况。由于内存设计采用了指令内存和数据内存分离的方式,因此不会产生构造冒险。
数据冒险
由于指令执行所需要的数据还未准备好所引起的冒险情况。当即将执行的指令依赖于还未处理完成的数据时,会导致指令无法立刻开始执行,引发数据冒险。
图1 数据冒险
数据冒险可以简单地通过插入 NOP 的方式避免,不过这样大大降低了流水线的工作性能。另外的方法就是通过数据转发,尽管数据回写在 WB 阶段,但实际上运算结果在 EX阶段就已经确定,可以传递给下一条指令。
图2 数据转发
数据转发有一个例外就是 LOAD 指令,由于 LOAD 指令从内存调取数据是在 MEM 阶段才完成的,而此时下一条指令也已经到了 EX 阶段,与数据转发不吻合。解决 LOAD 冒险的方法是暂停机制,阻塞流水线一个周期,到下一个周期的时候再继续执行 LOAD 之后的指令。
图3 LOAD冒险暂停机制
控制冒险
无法确定下一条指令而引发的冒险情况。在执行可能会改变下一条指令的分支指令时,在这一条指令执行结果确定之前下一条指令无法开始执行,从而引起控制冒险。控制冒险也可以通过在分支 指令之后插入 3 条NOP 指令避免,不过更加高效的方法是采用静态分支预测,即假定分支指令不转移,CPU 继续执行跟在分支指令之后的指令,当分支指令执行到MEM 阶段确定了分支需要转移,则排空流水线 IF、ID、EX 阶段,重新读取转移目标地址处的指令开始顺序执行。
指令冲突的避免
数据冒险
LOAD 冒险要在 IF 阶段进行检测,如果当前已经有一条 LOAD 指令进入 ID 阶段,并且下一条指令(即处于 IF 阶段的指令)与之存在数据冲突,则阻塞流水线一个周期,具体做法是保持 PC 值不变,在 IF 阶段插入 NOP 指令。
图4 LOAD冒险避免代码实现
数据转发在 ID 阶段进行检测,数据转发到 reg_A、reg_B 或者 smdr 寄存器,如果与上一条指令(ex_ir)冲突,从 ALUo 处转发数据,否则如果与上上条指令(mem_ir)冲突,从reg_C 或者数据内存 d_datain 处转发数据,否则如果与上上上条指令(wb_ir)冲突,从 reg_C1处转发数据。
图5 数据转发部分代码实现
控制冒险
可以在 MEM 阶段设置一个跳转标志寄存器,当且仅当分支指令被确认为跳转时标志寄存器有效。标志寄存器有效则排空流水线 IF、ID、EX 阶段,这样就避免了跟在分支指令之后本不该被执行的指令在 EX、MEM、WB 阶段修改 zf、nf、cf、8 个通用寄存器以及数据内存的值,具体做法是把 id_ir、ex_ir、mem_ir 置为 NOP,dw 不使能,同时还要保证把 PC设置为跳转目标地址。
图6 控制冒险避免代码实现