合肥工业大学《系统硬件综合设计》(《计算机组成原理》,CPU)课程设计报告(2020)

1 课程设计概述

1.1 设计目的

  计算机组成原理是计算机专业的核心基础课。该课程力图以“培养学生现代计算机系统设计能力”为目标,贯彻“强调软/硬件关联与协同、以CPU设计为核心/层次化系统设计的组织思路,有效地增强对学生的计算机系统设计与实现能力的培养”。课程设计是完成该课程并进行了多个单元实验后,综合利用所学的理论知识,并结合在单元实验中所积累的计算机部件设计和调试方法,设计出一台具有一定规模的指令系统的简单计算机系统。

  课程设计属于设计型实验,不仅锻炼学生简单计算机系统的设计能力,而且通过进行中央处理器底层电路的实现、故障分析与定位、系统调试等环节的综合锻炼,进一步提高学生分析和解决问题的能力。

1.2 设计任务

  本课程设计的目标总体目标是设计并实现一个满足以下条件的多周期和流水CPU:
  1)若干段流水、可以处理冲突;
  2)三种类型的指令若干条;
  3)MIPS、ARM、RISC-V等类型CPU都可以;
  4)下载到FPGA上进行验证(选)。

1.3 设计要求

  1)根据课程设计指导书的要求,制定出设计方案;
  2)分析指令系统格式,指令系统功能;
  3)根据指令系统构建基本功能部件,主要数据通路;
  4)根据功能部件及数据通路连接,分析所需要的控制信号以及这些控制信号的有效形式;
  5)调试、数据分析、验收检查;
  6)课程设计报告和总结。

1.4 技术指标

  1)支持表1.4.1所示的50条32位MIPS指令;

  2)支持5段流水机制,可处理数据冒险,结构冒险,分支冒险;

  3)能运行由自己所设计的指令系统构成的一段测试程序,测试程序应能涵盖所有指令,程序执行功能正确;

  4)运行测试程序所耗费的时钟周期数以及每种类型指令执行所需时间。

表 1.4.1 MIPS-C3指令集
序号指令指令说明指令格式OP
31-26
RS
25-21
RT
20-16
RD
15-11
SA
10-6
FUNCT
5-0
指令码
16进制
1addi加立即数addi rt, rs, immediate001000   0000010000020000000
2addiu加立即数(无符号)addiu rd, rs, immediate001001     24000000
3andi立即数与andi rt, rs, immediate001100     30000000
4ori或立即数ori rt, rs, immediate001101     34000000
5sltiu小于立即数置1(无符号)sltiu rt, rs, immediate001011     2C000000
6lui立即数加载高位lui rt, immediate00111100000    3C000000
7xori异或(立即数)xori rt, rs, immediate001110  000000000000000038000000
8slti小于置 1(立即数)slti rt, rs, immediate001010  000000000000000028000000
9addu加(无符号)addu rd, rs, rt000000   0000010000100000021
10andand rd, rs, rt000000   0000010010000000024
11beq相等时分支beq rs, rt, offset000100     10000000
12bne不等时分支bne rs, rt, offset000101     14000000
13j跳转j target000010     08000000
14jal跳转并链接jal target000011     0C000000
15jr跳转至寄存器所指地址jr rs000000    00100000000009
16lw取字lw rt, offset(base)100011     8C000000
17xor异或xor rd, rs, rt000000   0000010011000000026
18nor或非nor rd, rs, rt000000   0000010011100000027
19oror rd, rs, rt000000   0000010010100000025
20sll逻辑左移sll rd, rt, sa00000000000   00000000000000
21sllv逻辑左移(位数可变)sllv rd, rt, rs000000   0000000010000000004
22sltu小于置1(无符号) sltu rd, rs, rt000000   000001010110000002B
23sra算数右移sra rd, rt, sa00000000000   00001100000003
24srl逻辑右移srl rd, rt, sa00000000000   00001000000002
25subu减(无符号)sub rd, rs, rt000000   0000010001000000022
26sw存字sw rt, offset(base)101011     AC000000
27addadd rd, rs, rt000000   0000010000000000020
28subsub rd, rs, rt000000   0000010001000000022
29slt小于置 1slt rd, rs, rt000000   000001010100000002A
30srlv逻辑右移(位数可变)srlv rd, rt, rs000000   0000000011000000006
31srav算数右移(位数可变)srav rd, rt, rs000000   0000000011100000007
32clz前导零计数clz rd, rs011100   0000010000070000020
33divu除(无符号)divu rs, rt000000  00000000000110110000001B
34eret异常返回eret0100001000000000000000000001100042000018
35jalr跳转至寄存器所指地址,返回地址保存在R31这个寄存器jalr rs000000 00000  00100100000008
36lb取字节lb rt, offset(base)100000     80000000
37lbu取字节(无符号)lbu rt, offset(base)100100     90000000
38lhu取半字(无符号)lhu rt, offset(base)100101     94000000
39sb存字节sb rt, offset(base)101000     A0000000
40sh存半字sh rt, offset(base)101001     A4000000
41lh取半字lh rt, offset(base)100001     84000000
42mfhi读 Hi 寄存器mfhi rd0000000000000000 0000001000000000010
43mflo读 Lo 寄存器mflo rd0000000000000000 0000001001000000012
44mthi写 Hi 寄存器mthi rd000000 00000000000000001000100000011
45mtlo写 Lo 寄存器mtlo rd000000 00000000000000001001100000013
46mulmul rd, rs, rt011100   0000000001070000002
47mult字相乘mult rs, rt000000 0000000000 01100000000018
48multu乘(无符号)multu rs, rt000000  000000000001100100000019
49bgez大于等于 0 时分支bgez rs, offset000001 00001   04010000
50divdiv rs, rt000000  00000000000110100000001A

2 总体方案设计

2.1 MIPS体系结构

  MIPS是RISC处理器中的一种。MIPS即无内部互锁流水线的微处理器(Microprocessor without Interlocked Piped Stages),其设计机制是尽量避免处理器中流水线上各种相关问题,使得程序指令可以尽量不停顿的执行。可以说,MIPS是为了流水线而生的,这也是本设计选择MIPS作为目标CPU架构的主要原因。接下来介绍与MIPS有关的一些基本概念。   

2.1.1 MIPS寄存器

  MIPS寄存器主要包括:通用寄存器、特殊寄存器、浮点寄存器。本设计实现了前两者。

  1)MIPS通用寄存器

  MIPS32体系结构总共实现了32个32比特的通用寄存器,这些寄存器可供指令使用,第0号寄存器到第31号寄存器。其中,只有两个寄存器较为特殊:第0号通用寄存器不论指令向里面写入什么值读出的总是0,而第31号通用寄存器通常被子程序调用指令用来存储返回地址。表2.1.1給出了MIPS通用寄存器的命名规则。

表 2.1.1 MIPS通用寄存器命名规则
寄存器编号助记符用途
0zero固定为0
1at编译暂存寄存器,为编译器保留
2~3v0,v1用来存放子程序的返回值(非浮点)
4~7a0~a3用来传递子程序的前四个参数(非浮点)
8~15t0~t7,t8,t9暂存寄存器,离开时子函数时不需要对其进行存储和恢复
16~23s0~s7子函数寄存变量;改变这些寄存器值的子程序必须存储旧的值并在退出前恢复,对调用程序来说值不变
26,27k0,k1为中断/陷阱指令保留
28gp全局指针;某些对时间敏感的系统可以使用该寄存器为static或extern变量提供快速的寻址方式
29sp堆栈指针
30s8/fp子程序用它来作帧指针
31ra子程序返回地址

  2)MIPS特殊寄存器

  MIPS体系结构定义了以下3个特殊寄存器:
  i. PC:程序计数器,保存当前执行程序的地址;
  ii. LO:保存乘法指令低位结果或除法指令的商;
  iii. HI:保存乘法指令高位结果或除法指令的余数。

  3)MIPS浮点寄存器

  MIPS体系结构还定义了以下3种浮点寄存器:
  i. 32个32比特的浮点寄存器(FPRs);
  ii. 5个FPU控制寄存器用于识别和控制FPU;
  iii. 8个浮点条件代码,它们是FCSR寄存器中的一部分。
  浮点寄存器的实现比较复杂且不在本次课程设计的任务要求中,故本设计没有对其进行实现。

2.1.2 MIPS指令集

  每一条MIPS指令的长度是32位。MIPS指令集包括三种类型的指令:寄存器类型指令,立即数类型指令和跳转类型指令1B7。寄存器类型指令只有通用寄存器之间的操作,不包含立即数;立即数类型指令是通用寄存器和16位立即数之间的操作;而跳转类型指令包含一个26位的指令地址索引,可以进行大范围绝对地址跳转。MIPS架构这种精简指令集处理器在很大程度上减少指令数目并且极大的简化了指令的译码和执行,程序编译器还可以利用若干个简单的指令合理的组合,形成更加复杂的操作。如表2.1.2.1所示为MIPS处理器的指令格式,表2.1.2.1是对指令中各个域的说明。

表 2.1.2.1 MIPS指令格式
31 2625 2120 1615 1110 65 0
R类型opcodersrtrdsafunct
31 2625 2120 1615 1110 65 0
I类型opcodersrtimmediate
31 2625 2120 1615 1110 65 0
J类型opcodeinstr_index
表 2.1.2.2 MIPS指令域说明
说明
opcode6比特指令主操作码
rs5比特源寄存器索引
rt5比特源/目的寄存器索引
rd5比特目的寄存器索引
sa5比特移位偏移量
funct6比特函数功能码,用来指示寄存器指令的具体功能
immediate16比特的立即数,用做逻辑指令操作数、算术指令操作数、存储器访问
instr_index指令的地址偏移量和分支指令的跳转目的偏移量
26比特的跳转索引,指示跳转指令的索引

  MIPS32指令分为如下几类:
  1)存储器访问指令:用于在存储器和通用寄存器之间传递数据。访问存储器的地址是通用寄存器的值和16位有符号立即数相加的值,存储器访问指令都属于立即数类型指令。
  2)运算指令:完成定点的算术、逻辑、移位等运算操作。操作数据既可以是通用寄存器的值,也可以是16位立即数。运算指令可以是寄存器类型或立即数类型。
  3)分支跳转指令:可以改变程序指令的顺序执行。分支指令用PC寄存器的值加上16位立即数左移2位指向目的地址。跳转指令把指令中程序索引或者某个通用寄存器的内容写入PC。分支指令属于立即数类型,跳转指令可以是寄存器类型或跳转类型。
  4)系统协处理器指令:进行系统协处理器寄存器的读写操作。

2.1.3 MIPS五级流水线

  流水线是指将计算机指令处理过程拆分为多个步骤,并通过多个硬件处理单元并行执行来加快指令执行速度。本设计的目标是MIPS五级流水线,其各个阶段的主要工作如下:
  取指阶段(Instruction Fetch,IF):从指令存储器读出指令,同时确定下一条指令地址。
  译码阶段(Instruction Decode,ID):对指令进行译码,从通用寄存器中读出要使用的寄存器的值,如果指令中含有立即数,那么还要将立即数进行符号扩展或无符号扩展。如果是转移指令,并且满足转移条件,那么给出转移目标,作为新的指令地址。
  执行阶段(Execute,EX):按照译码阶段给出的操作数、运算类型,进行运算,给出运算结果。如果是Load/Store指令,那么还会计算Load/Store的目标地址。
  访存阶段(Memory,MEM):如果是Load/Store指令,那么在此阶段会访问数据存储器,反之,只是将执行阶段的结果向下传递到回写阶段。同时,在此阶段还要判断是否有异常需要处理,如果有,那么会清除流水线,然后转移到异常处理例程入口地址处继续执行
  回写阶段(Write Back,WB):将运算结果保存到目标寄存器。
  除上述内容外,MIPS架构还包括:MIPS协处理器0以及MIPS中断与异常等,因本设计并未涉及相关内容,故不作介绍。

2.2 流水CPU设计

2.2.1 总体设计

  本设计拟实现的CPU是全冒险处理的五阶段CPU,下面对需要解决的问题进行简单地描述。
  单周期时即使指令之后的执行不再需要使用前面用到的部件,还仍然霸占着那些部件。合理而高效的做法自然是让指令执行时不再需要使用前面的部件时,应该调入下一条指令执行。所以关键问题是:如何将单周期数据通路进行划分,当指令未执行完而新的指令已经调入时必须使用寄存器保存当前指令的执行信息,如何设置流水寄存器内容。此外,程序在流水线上的执行还会带来一些“流水线专属”的问题,如下所示。
  i1. R2 <- R1 + R3
  i2. R4 <- R2 + R3
  第一条指令是计算一个值,并将其储存在R2,而第二个指令是使用这个值计算结果并储存在R4,但是在我们拿出第二步的操作数时,第一步的结果还未被储存。在如此情况下,得到的运算结果是不可预知的。这个问题即是所谓的“数据相关”,又称“数据冒险”,一般发生在指令乱序执行时,可能会发生读取数据与写入数据之间的时序与空间的相关性。数据相关又分三种情况:RAW、WAR、WAW。数据相关可以采用重定向的方法解决,真数据相关也就是RAW,只需要检测相邻指令读寄存器编号与写寄存器编号一致时就采用定向技术(旁路),将正确的数据(还未来得及写回)通过新增的数据通路送到需要读该寄存器的地方。有一种特殊的数据相关:load-use冲突,并不能简单地重定向,有关冲突必需在ID阶段监测,否则会造成额外的硬件开销。除了数据相关,流水线中还存在结构相关与分支相关,前者指的是指的是在指令执行的过程中,由于硬件资源满足不了指令执行的要求,发生硬件资源冲突而产生的相关。比如:指令和数据都共享一个存储器,在某个时钟周期,流水线既要完成某条指令对存储器中数据的访问操作,又要完成后续的取指令操作,这样就会发生存储器访问冲突,产生结构相关。解决结构相关可以考虑采用哈佛结构:指令存储器和数据存储器分开(实际商用的处理器是分开的指令cache与数据cache);后者指的是流水线中的分支指令或者其他需要改写PC的指令造成的相关。最后介绍分支相关的解决办法,本设计采用静态分支预测中的预测失败的方法,当监测到分支指令时并不做特别的处理,等到分支成功或者失败的结果计算出来时,若是分支失败则分支指令和普通指令没有任何区别,一旦分支成功,就要清楚误取深度条指令,从新从分支地址出取出正确的指令。当解决上述所有问题后,本设计就实现了全冒险处理的五段流水CPU。

2.2.2 流水接口部件设计

  如2.1.3节所述,在MIPS五段流水CPU上,指令的解释执行可以分为IF、ID、EX、MEM与WB五个阶段,且这五个阶段在不同的功能部件上执行,故可使五个子任务并发执行。流水寄存器中应该包含指令在后续解释执行阶段所需的所有信号,并且为了便于调试最好将PCPlus4、IR信号也全部传递。流水寄存器中传递的信号及在各段的使用情况如图2.2.2.1所示。从图2.2.2.1上可以看到:IF/ID寄存器中保存着PCPlus4和IR信号也全部传递信号,而ID/EX寄存器中包含着指令在EX执行所需要的全部信号以及指令在MEM和WB阶段需要的所有信号,EX/MEM寄存器包含着指令在MEM阶段执行所需要的全部信号以及指令在WB段执行的全部信号,MEM/WB寄存器则包含着指令在WB段执行所需要的全部信号,因此流水寄存器不仅需要包含指令在本段执行所需要的信号,还要将指令在后续阶段所需要的信号逐步传递给后续流水寄存器(相当于实现了控制器的复用。考虑到后面要实现流水线的停顿和指令的清空,流水寄存器中还需有使能信号和清零信号。


图 2.2.2.1 流水寄接口设计及信号传递示意

2.3 数据转发流水线设计

  数据转发技术和重定向、旁路等所指的概念是一样的,是为了解决流水线中存在的RAW冲突。后面指令读寄存器编号和前面指令写寄存器编号相同时就会出现真数据相关,该问题存在的原因是执行结果未写回寄存器文件,后续指令就需要从寄存器文件中读取还未来得及写入的寄存器的值,但实际上这些需要写入的值都已经在数据通路中计算出来了,只是没有写回而已,故本设计的解决方法是:通过构建额外的数据旁路来将这些数据送到需要使用它们的地方,其原理如图2.3.1。


图 2.3.1 数据旁路示意

  发生数据相关单元应该检测EX段读的寄存器编号(最多可能是两个),与MEM和WB段指令写寄存器编号是否出现相同的情况,若出现,则数据冲突发生,所以需要将数据送到EX段对应位置,如果编号不相同,则正常执行,无需重定向。此处有两个问题需要注意,当读、写0号寄存器时,是不需要重定向的,因0号寄存器内容始终为0,一定不会出现数据冲突。如果读寄存器编号与MEM和WB段写寄存器编号均相同,应该重定向那个段的数据,显示应该重定向MEM段的的数据,因此MEM段的数据重定向具有更高的优先级,在实现的时候只需将输入端为MEM段数据的二路选择器放在后面即可。当上述问题解决后,数据转发流水线也就完成了,重定向具体实现如图2.3.2所示,流水寄存器信号在每段流转情况如图2.3.3所示。


图 2.3.2 流水寄接口设计与重定向实现


图 2.3.3 流水线寄存器信号在每段流转情况

2.4 气泡式流水线设计

  这一节讨论load-use相关的问题。当连续两条指令依次执行,其中前一条是lw指令,而后面的一条指令的源寄存器编号与lw指令的目标寄存器编号相同时,会发生load-use冲突(RAW相关),如图2.4.1所示。此时,EX段的时延将是访问与ALU运算串行进行的时间,若在这种情况下仍然直接对指令进行重定向,在不发生错误的前提下,将会造成CPU频率降低,而这对于某些工作环境下的CPU是无法接受的,故本设计需要使用其它方法来解决这个问题。


图 2.4.1 load-use冲突示意图

  为了解决load-use冲突,可以采用以下方法:当指令流水处于ID阶段时,需要插入气泡并暂停流水线,在重定向的检测上外加判定需要重定向的指令的前一条指令是否为lw指令即可。否则,将必须以增加硬件开销为基础来保证程序不出错。原因如下所述。

  假设在EX段检测,考虑以下情况:MEM段和WB段执行的指令都为lw指令,且两条lw指令的目的寄存器编号正好与EX段指令源寄存器编号相同,若插入一个气泡,WB段lw指令将被“挤走”,重定向的数据被丢失,而EX段指令又未从寄存器文件中取得正确的源寄存器值,因此就造成了程序执行可能出现错误。考虑在ID段检测load-use冲突,若在EX段与MEM段的指令出现与在EX段检测load-use冲突相同的情况,插入一个气泡并监测ID段及IF段流水线,那么最先的lw指令将会流动到WB段,将寄存器文件改造了为下降沿写入,故下一个时钟上升沿来临时,正确的源寄存器值将被锁入ID/EX流水寄存器中。

  综上,load-use冲突需要插入气泡并暂停流水线,且关于load-use冲突的检测必须在ID段。如图2.4.2所示为气泡流水线原理图。


图 2.4.2 气泡流水线原理图

3 详细设计与实现

3.1 流水CPU实现

3.1.1 流水接口部件实现

  1)流水线部件内部接口

  所谓的流水接口部件即是流水寄存器,由于本设计拟构建的是经典五段流水线,故共需要设置4个流水寄存器:IF/ID、ID/EX、EX/MEM、MEM/WB。这4个流水寄存器中应包含哪些信号用于向下一级寄存器传递,已在2.2.2节介绍过,在此不赘述,仅給出各个寄存器的内部接口图片。
  如图3.1.1.1所示为IF/ID段流水寄存器内部接口。


图 3.1.1.1 IF/ID 段流水寄存器内部

  如图3.1.1.2所示为ID/EX段流水寄存器内部接口。


图 3.1.1.2 ID/EX 段流水寄存器内部

  如图3.1.1.3所示为EX/MEM段流水寄存器内部接口。


图 3.1.1.3 EX/MEM 段流水寄存器内部

  如图3.1.1.4所示为MEM/WB段流水寄存器内部接口。


图 3.1.1.4 MEM/WB段流水寄存器内部接口

  2)流水线部件Verilog实现

  可以说,4个流水线寄存器的工作原理与实现方式大同小异,只是向其下一级传递的信号略有不同,此处仅給出IF/ID段流水寄存器的Verilog实现。

module RegD(								//IF/ID阶段的寄存器,根据IF的指令与PC确定ID时其对应值
  input               clk, rst, En,
  input       [31:0]  InstrF, PCPlus4F,		//IF(取指令)周期 时的指令 / 下一条指令的PC处
  output  reg [31:0]  InstrD, PCPlus4D		//ID(指令译码)周期 时的指令 / 下一条指令的PC处
  );
  
  initial
  begin
    InstrD <= 0;
    PCPlus4D <= 0;
  end
  
  always @(posedge clk)
    if (rst)
    begin
      InstrD <= 0;
      PCPlus4D <= 0;
    end
    else if (En)
    begin
      InstrD <= InstrF;
      PCPlus4D <= PCPlus4F;
    end
    else
    begin
      InstrD <= InstrD;
      PCPlus4D <= PCPlus4D;
    end
  
endmodule

3.1.2 数据通路实现

  本设计所构建五段流水线CPU的数据通路如图3.1.2.1所示。


图 3.1.2.1 五段流水线CPU数据通路

3.1.3 五段流水线寄存器模块

  根据数据通路,可以自然地得到Verilog实现的各个模块,下面給出本设计所构建五段流水线CPU的有关模块的定义接口。各个模块的接口的作用大同小异,不一一注释。
  如表3.1.3.1所示为RegD模块的定义接口。

表 3.1.1 RegD模块定义接口
序 号接 口 名宽度(bit)输入/输出作 用
1clk1输入 
2rst1输入 
3En1输入 
4InstrF32输入IF(取指令)周期 时的指令
5PCPlus4F32输入IF(取指令)周期 时下一条指令的PC处
6InstrD32输出ID(指令译码)周期 时的指令

  如表3.1.3.2所示为RegE模块的定义接口。

表 3.1.2 RegE模块定义接口
序 号接 口 名宽度(bit)输入/输出作 用
1clk1输入 
2rst1输入 
3FlushE1输入 
4PCPlus4D32输入 
5SrcA2D32输入 
6SrcB2D32输入 
7RsD5输入 
8RtD5输入 
9RdD5输入 
10ShamtD5输入 
11ImmD32输入 
12RegWriteD1输入 
13RegDstD1输入 
14MemWriteD1输入 
15MemToRegD1输入 
16ALUControlD4输入 
17ALUSrcD2输入 
18StartD1输入 
19IsJJalD1输入 
20IsJrJalrD1输入 
21IsLbSbD1输入 
22IsLhShD1输入 
23IsUnsignedD1输入 
24HiLoWriteD1输入 
25HiLoD1输入 
26IsShamtD1输入 
27MdOpD2输入 
28PCPlus8E32输出 
29SrcA2E32输出 
30SrcB2E32输出 
31RsE4输出 
32RtE4输出 
33RdE4输出 
34ShamtE4输出 
35ImmE32输出 
36RegWriteE1输出 
37RegDstE1输出 
38MemWriteE1输出 
39MemToRegE1输出 
40ALUControlE4输出 
41ALUSrcE2输出 
42StartE1输出 
43IsJJalE1输出 
44IsJrJalrE1输出 
45IsLbSbE1输出 
46IsLhShE1输出 
47IsUnsignedE1输出 
48HiLoWriteE1输出 
49HiLoE1输出 
50IsShamtE1输出 
51MdOpE2输出 

  如表3.1.3.3所示为RegM模块的定义接口。

表 3.1.3 RegM模块的定义接口
序 号接 口 名宽度(bit)输入/输出作 用
1clk 1输入 
2rst1输入 
3PCPlus8E32输入 
4ALUOutE32输入 
5WriteDataE32输入 
6RtE 5输入 
7WriteRegE5输入 
8RegWriteE1输入 
9MemWriteE1输入 
10MemToRegE1输入 
11IsJJalE1输入 
12IsJrJalrE1输入 
13IsLbSbE1输入 
14IsLhShE1输入 
15IsUnsignedE1输入 
16PCPlus8M32输出 
17ALUOutM32输出 
18WriteDataM32输出 
19RtM5输出 
20WriteRegM5输出 
21RegWriteM1输出 
22MemWriteM1输出 
23MemToRegM1输出 
24IsJJalM1输出 
25IsJrJalrM1输出 
26IsLbSbM1输出 
27IsLhShM1输出 
28IsUnsignedM1输出 

  如表3.1.3.4所示为RegW模块的定义接口。

表 3.1.4 RegW模块定义接口
序 号接 口 名宽度(bit)输入/输出作 用
1clk 1输入 
2rst1输入 
3PCPlus8M32输入 
4ALUOutM32输入 
5ReadDataM32输入 
6WriteRegM5输入 
7RegWriteM1输入 
8MemToRegM1输入 
9IsJJalM1输入 
10IsJrJalrM1输入 
11IsUnsignedM1输入 
12BEOutM4输入 
13PCPlus8W32输出 
14ALUOutW32输出 
15ReadDataW32输出 
16WriteRegW5输出 
17RegWriteW1输出 
18MemToRegW1输出 
19IsJJalW1输出 
20IsJrJalrW1输出 
21IsUnsignedW1输出 
22BEOutW4输出 

  如表3.1.3.5所示为RegFile模块的定义接口。

表 3.1.5 RegFile模块定义接口
序 号接 口 名宽度(bit)输入/输出作 用
1clk1输入 
2rst1输入 
3WE31输入 
4A15输入第 一个读寄存器地址
5A25输入第 二个读寄存器地址
6A35输入第 三 个读寄存器地址
7WD332输入要写入的数据
8SrcAD32输入从寄存器读出的数据
9SrcBD32输出

3.2 数据转发流水线实现

3.2.1 数据冲突检测

  如2.3节所述,对于非load-use的RAW真数据相关,可采用重定向技术避免流水线暂停直待数据写回。

  本设计使用6路并发比较进行RAW相关检测,其中4路是为了检测ID段读寄存器(最多两个)的编号和EX段、MEM段写寄存器编号是否相同,另外2路并发比较是如果ID段读寄存器编号为0的话,即使检测到与前面两条指令的写寄存器编号相同也不必重定向,因为0号寄存器内容永远为0。

  本设计使用Verilog实现数据冲突检测如下。

  assign ForwardAD = ((RsD == WriteRegM) && RegWriteM && (IsJJalM || IsJrJalrM) && (WriteRegM != 5'b00000)) ? 2'b11 :
                     ((RsD == WriteRegM) && RegWriteM && (WriteRegM != 5'b00000)) ? 2'b01 :
                     ((RsD == WriteRegW) && RegWriteW && (WriteRegW != 5'b00000)) ? 2'b10 : 2'b00 ;
  assign ForwardBD = ((RtD == WriteRegM) && RegWriteM && (WriteRegM != 5'b00000)) ? 2'b01 :
                     ((RtD == WriteRegW) && RegWriteW && (WriteRegW != 5'b00000)) ? 2'b10 : 2'b00 ;
  assign ForwardAE = ((RsE == WriteRegM) && RegWriteM && (WriteRegM != 5'b00000)) ? 2'b01 :
                     ((RsE == WriteRegW) && RegWriteW && (WriteRegW != 5'b00000)) ? 2'b10 : 2'b00 ;
  assign ForwardBE = ((RtE == WriteRegM) && RegWriteM && (WriteRegM != 5'b00000)) ? 2'b01 :
                     ((RtE == WriteRegW) && RegWriteW && (WriteRegW != 5'b00000)) ? 2'b10 : 2'b00 ;
  assign ForwardM  = ((RtM == WriteRegW) && RegWriteW && (WriteRegW != 5'b00000)) ? 1 : 0;

3.2.2 数据旁路

  在上一节中,本设计构建数据冲突检测机制得到了重定向信号,仅有信号是不足以完成重定向的工作,还必须增加新的数据旁路才能实现重定向:通过加入4个二路选择器将MEM段和WB段数据在发生数据冒险时通过旁路送到对应位置。

  本设计使用Verilog实现数据重定向如下。

module MUX2 #(parameter WIDTH = 32)
             (input  [WIDTH-1:0] d0, d1, 					//双路选择器
              input              s, 
              output [WIDTH-1:0] y);

  assign y = s ? d1 : d0; 
  
endmodule

module MUX3 #(parameter WIDTH = 32)							//三路选择器
             (input  [WIDTH-1:0] d0, d1, d2,
              input  [1:0]       s, 
              output [WIDTH-1:0] y);

  assign y = s[1] ? d2 : (s[0] ? d1 : d0); 
  
endmodule

module MUX4 #(parameter WIDTH = 32)							//四路选择器
             (input  [WIDTH-1:0] d0, d1, d2, d3,
              input  [1:0]       s, 
              output [WIDTH-1:0] y);

  assign y = s[1] ? (s[0] ? d3 : d2) : (s[0] ? d1 : d0); 
  
endmodule

3.3 气泡式流水线实现

  最后,介绍如何实现解决load-use冲突与分支冲突。

  load-use冲突必须在ID段检测,具体操作是:在EX段插入一个气泡,后暂停IF和ID段流水线,如此便能使lw指令访存结束后在WB段进行重定向。

  对于分支指令,本设计采用的是静态预测失败的方式进行处理:总是假定分支失败,在EX段得到分支结果,若分支失败,则非顺序代码被加载执行;若分支成功,则需要清除误取的指令并从分支目标处重新取值执行。

  本设计使用Verilog实现起泡式流水如下。

  assign NPCD = (IsJJalD)           ? {PCPlus4D[31:28], ImmD_26, 2'b00} :
                (IsJrJalrD)         ? Reg_32 :
                (BranchD && CompD)  ? PCPlus4D + ImmD_32 : PCPlus4D + 4;
  assign IsJBrD = ((BranchD && CompD) || IsJJalD || IsJrJalrD);

  至此,本设计已经实现了全冒险处理机制的五段流水CPU。为了便于介绍下一步的工作,此处給出本设计使用所构建全冒险处理机制的五段流水CPU的Verilog实现的模块接口,部分模块的定义接口已在3.1.3节展示,此处不赘述。

  如表3.3.1所示为ALU模块的定义接口。

表 3.3.1 ALU模块定义接口
序 号接 口 名宽度(bit)输入/输出作 用
1A32输入操作数A
2B32输入操作数B
3ALUControlE4输入算术或逻辑指令操作符
4ALUOutE32输出算术或逻辑输出

  如表3.3.2所示为BECtrl模块的定义接口。

表 3.3.2 BECtrl模块的接口描述
序 号接 口 名宽度(bit)输入/输出作 用
1ByteOffset2输入DM低二位地址
2IsLhShM1输入 
3IsLbSbM1输入 
4BEOutM4输出DM字节写入控制信号

  如表3.3.3所示为Comp模块的定义接口。

表 3.3.3 Comp模块的接口描述
序 号接 口 名宽度(bit)输入/输出作 用
1A32输入操作数A
2B32输入操作数B
3CompOpD3输入比较运算符
4CompD1输出比较结果

  如表3.3.4所示为Ctrl模块的定义接口。

表 3.3.4 Ctrl模块的接口描述
序 号接 口 名宽度(bit)输入/输出作 用
1OpD6输入输入指令的OPCode,指定是哪种寄存器运算
2FunctD6输入输入指令的FunctCode,32位指令的FunctD段
3RtD5输入写回寄存器的数据流的选择信号
4RegWriteD1输出指令译码阶段(ID)寄存器写输出
5MemWriteD1输出指令译码(ID)存储器写
6MemToRegD1输出指令译码阶段(ID)存储器向寄存器
7RegDstD1输出指令译码阶段(ID)写回寄存器的地址的选择信号
8BranchD1输出分支指令
9IsJJalD1输出跳转指令
10IsJrJalrD1输出寄存器跳转指令
11IsLbSbD1输出 
12IsLhShD1输出 
13IsUnsignedD1输出 
14HiLoWriteD1输出 
15HiLoD1输出 
16IsMdD1输出 
17IsShamtD1输出 
18MdOpD2输出 
19ALUControlD4输出 
20ALUSrcD2输出 
21ExtOpD2输出 
22CompOpD3输出 

  如表3.3.5所示为DM_4k模块的定义接口。

表 3.3.5 DM_4k模块的接口描述
序 号接 口 名宽度(bit)输入/输出作 用
1A10输入地址
2WD32输入写入的数据
3clk1输入时钟信号
4WE1输入数据存储器写使能
5BE4输入写回的位置
6RD32输出写回输出

  如表3.3.6所示为DMExt模块的定义接口。

表 3.3.6 DMExt模块的接口描述
序 号接 口 名宽度(bit)输入/输出作 用
1ReadDataW32输入读取即将写的32位数据
2BEOutW4输入指定读取数据
3IsUnsignedW1输入是否无符号数
4DMExtOutW32输出扩展后的32位数

  如表3.3.7所示为Ext模块的定义接口。

表 3.3.7 Ext模块的接口描述
序 号接 口 名宽度(bit)输入/输出作 用
1ImmD_1616输入无符号扩展
2ExtOpD2输入扩展类型
3ImmD_3232输出立即数

  如表3.3.8所示为Hazard模块的定义接口。

表 3.3.8 Hazard模块的接口描述
序 号接 口 名宽度(bit)输入/输出作 用
1OpD6输入读寄存器周期时的操作数
2RsD5输入ID / EX 阶段的地址
3RtD5输入
4RsE5输入
5RtE5输入
6RtM5输入
7WriteRegE5输入EX / MEM / WB 阶段的地址
8WriteRegM5输入
9WriteRegW5输入
10ALUSrcD2输入 
11IsJrJalrD1输入 
12BranchD1输入 
13IsMdD1输入 
14BusyE1输入 
15IsJJalM1输入 
16IsJrJalrM1输入 
17MemToRegE1输入 
18MemToRegM1输入 
19MemWriteM1输入 
20RegWriteE1输入 
21RegWriteM1输入 
22RegWriteW1输入 
23StallF1输出IF阶段访存阶段是否暂停
24StallD1输出ID阶段访存阶段是否暂停
25FlushE1输出EX阶段是否清除流水线
26ForwardAD2输出 
27ForwardBD2输出 
28ForwardAE2输出 
29ForwardBE2输出 
30ForwardM1输出 

  如表3.3.9所示为IM_2k模块的定义接口。

表 3.3.9 IM_2k模块的接口描述
序 号接 口 名宽度(bit)输入/输出作 用
1A9输入地址
2RD32输出指令

  如表3.3.10所示为MD模块的定义接口。

表 3.3.10 MD模块的接口描述
序 号接 口 名宽度(bit)输入/输出作 用
1clk1输入时钟信号
2rst2输入复位信号
3D132输入操作数D1
4D232输入操作数D2
5WE1输入写使能
6HiLo1输入HI/LO写使能
7Start1输入运行使能
8Op2输入操作符
9Busy1输出标记是否忙
10Hi32输出HI寄存器输出
11Lo32输出LO寄存器输出

  如表3.3.11所示为MIPS模块的定义接口。

表 3.3.11
序 号接 口 名宽度(bit)输入/输出作 用
1clk1输入时钟信号
2rst2输入复位信号

  如表3.3.12所示为MUX2模块的定义接口。

表 3.3.12 MUX2模块的接口描述
序 号接 口 名宽度(bit)输入/输出作 用
1d032输入数据或地址
2d132输入
3s1输入非冲突信号
4y32输出非冲突的数据或地址

  如表3.3.13所示为MUX3模块的定义接口。

表 3.3.13 MUX3模块的接口描述
序 号接 口 名宽度(bit)输入/输出作 用
1d032输入数据或地址
2d132输入
3d232输入
4s1输入非冲突信号
5y32输出非冲突的数据或地址

  如表3.3.14所示为MUX4模块的定义接口。

表 3.3.14 MUX4模块的接口描述
序 号接 口 名宽度(bit)输入/输出作 用
1d032输入数据或地址
2d132输入
3d232输入
4d332输入
5s1输入非冲突信号
6y32输出非冲突的数据或地址

  如表3.3.15所示为NPC模块的定义接口。

表 3.3.15 NPC模块的接口描述
序 号接 口 名宽度(bit)输入/输出作 用
1PCPlus4D32输入PC+4 的值
2ImmD_2626输入指令的低 26 位
3ImmD_3232输入分支指令的偏移
4Reg_3232输入
5IsJJalD1输入跳转类型
分支类型
6IsJrJalrD1输入
7BranchD1输入
8CompD1输入
9NPCD32输出下一条指令的地址
10IsJBrD1输出是否跳转/分支指令

  如表3.3.16所示为PC模块的定义接口。

表 3.3.16 PC模块的接口描述
序 号接 口 名宽度(bit)输入/输出作 用
1clk1输入 
2rst1输入 
3En1输入 
4IsJBrD1输入是否跳转、分支指令
5NPCD32输入PC按条件自增后的值
6PCPlus4F32输出实际的PC(下一条指令处)值
7PCF32输出PC当前值

  如表3.3.17所示为StartCtrl模块的定义接口。

表 3.3.17 StartCtrl模块的接口描述
序 号接 口 名宽度(bit)输入/输出作 用
1InstrD32输入ID阶段读入的指令
2BusyE1输入指示是否忙
3StartD1输出是否启动控制模块

4 实验过程与调试

4.1 实验环境

4.1.1 硬件

  Dell G3 3579笔记本电脑。

4.1.2 软件

  ModelSim SE-64 2019.2、MARS 4.5、Vivado 2020.1。

4.2 测试用例与功能测试

  在完成全冒险处理机制的五段流水CPU的设计与实现后,下面通过实验对本设计所构建CPU进行样例测试与功能测试,样例测试是指检测CPU在运行指定程序时的输出与标准输出以及CPU各时序状态与预期是否一致。本设计所用MIPS测试样例程序在附录中給出。功能测试是指CPU能否达到设计的性能指标。

4.2.1 样例测试

  本设计所构建CPU支持MIPS-C3指令集的全部50条指令,接下来选择有代表性的指令进行波形测试。为了便于实验结果对比,将同CPU的波形输出同MARS 4.5运行相同程序的结果进行对比。

  1)理想流水线测试


图 4.2.1.1 MARS界面的寄存器-1


图 4.2.1.2 MARS界面的寄存器-2


图 4.2.1.3 MARS界面的寄存器-3


图 4.2.1.4 ModelSim的仿真波形图-1


图 4.2.1.5 MARS界面的寄存器-4


图 4.2.1.6 MARS界面的寄存器-5


图 4.2.1.7 MARS界面的寄存器-6


图 4.2.1.8 ModelSim的仿真波形图-2


图 4.2.1.9 MARS界面的寄存器-7

  如图4.1.1.1至图4.1.1.9所示,使用无冲突的MIPS程序对CPU进行理想流水线测试,由实验结果可知,本设计所构建CPU运行样例程序所得波形与MARS显示的寄存器数据完全一致。

  2)跳转指令测试(以j指令为例)


图 4.2.1.10 MARS界面的寄存器-8


图 4.2.1.11 ModelSim的仿真波形图-3


图 4.2.1.12 ModelSim的仿真波形图-4


图 4.2.1.13 MARS界面的MIPS指令执行-1

  如图4.1.1.10至图4.1.1.13所示,使用含j跳转指令的MIPS程序对CPU进行测试,程序运行到第11个时钟周期时发生了指令跳转,由实验结果可知,本设计所构建CPU运行样例程序所得波形与MARS显示的寄存器数据完全一致。

  3)数据前推


图 4.2.1.14 ModelSim的仿真波形图-5


图 4.2.1.15 MARS界面的寄存器-9


图 4.2.1.16 ModelSim的仿真波形图-6

  如图4.1.1.14至图4.1.1.16所示,使用含需要进行数据前推方能计算得出正确结果的MIPS程序进行CPU测试,程序运行到第6个周期时发生了数据前推。由实验结果可知,最终,本设计所构建CPU运行样例程序所得波形与MARS显示的寄存器数据完全一致。

  4)数据冲突

  i. load-use冲突


图 4.2.1.17 MARS界面的寄存器-10


图 4.2.1.18 MARS界面的MIPS指令执行-2


图 4.2.1.19 MARS界面的寄存器-11


图 4.2.1.20 MARS界面的MIPS指令执行-3


图 4.2.1.21 MARS界面的寄存器-12


图 4.2.1.22 MARS界面的存储器-1


图 4.2.1.23 MARS界面的MIPS指令执行-4


图 4.2.1.24 MARS界面的寄存器-13


图 4.2.1.25 MARS界面的存储器-2


图 4.2.1.26 MARS界面的MIPS指令执行-5


图 4.2.1.27 MARS界面的寄存器-14


图 4.2.1.28 MARS界面的MIPS指令执行-6


图 4.2.1.29 MARS界面的寄存器-15


图 4.2.1.30 MARS界面的MIPS指令执行-7


图 4.2.1.31 MARS界面的存储器-3


图 4.2.1.32 ModelSim的仿真波形图-7

  如图4.1.1.17至图4.1.1.32所示,使用含load-use冲突的MIPS程序进行CPU测试,程序运行到第3个周期时发生了load-use冲突。由实验结果可知,最终,本设计所构建CPU运行样例程序所得波形与MARS显示的寄存器数据完全一致。

  ii. sw-lw冲突


图 4.2.1.33 MARS界面的MIPS指令执行-8


图 4.2.1.34 MARS界面的寄存器-16


图 4.2.1.35 MARS界面的存储器-4


图 4.2.1.36 ModelSim的仿真波形图-8


图 4.2.1.37 MARS界面的寄存器-17


图 4.2.1.38 MARS界面的MIPS指令执行-9


图 4.2.1.39 MARS界面的寄存器-18


图 4.2.1.40 MARS界面的MIPS指令执行-10


图 4.2.1.41 MARS界面的存储器-5


图 4.2.1.42 ModelSim的仿真波形图-9

  如图4.1.1.33至图4.1.1.42所示,使用含sw-lw冲突的MIPS程序进行CPU测试,程序运行到第7个周期时发生了load-use冲突。由实验结果可知,最终,本设计所构建CPU运行样例程序所得波形与MARS显示的寄存器数据完全一致。

  iii. RAW冲突


4.2.1.43 MARS界面的寄存器-19


图 4.2.1.44 MARS界面的MIPS指令执行-11


图 4.2.1.45 MARS界面的寄存器-20


图 4.2.1.46 MARS界面的MIPS指令执行-12


图 4.2.1.47 ModelSim的仿真波形图-10


图 4.2.1.48 MARS界面的寄存器-21


图 4.2.1.49 MARS界面的MIPS指令执行-13


图 4.2.1.50 ModelSim的仿真波形图-11

  如图4.1.1.43至图4.1.1.50,使用含sw-lw冲突的MIPS程序进行CPU测试,程序运行到第14个周期时发生了load-use冲突。由实验结果可知,最终,本设计所构建CPU运行样例程序所得波形与MARS显示的寄存器数据完全一致。

  iv. 结构冲突


图 4.2.1.51 MARS界面的寄存器-22


图 4.2.1.52 MARS界面的MIPS指令执行-14


图 4.2.1.53 MARS界面的寄存器-23


图 4.2.1.54 MARS界面的MIPS指令执行-15


图 4.2.1.55 MARS界面的寄存器-24


图 4.2.1.56 MARS界面的MIPS指令执行-16


图 4.2.1.57 ModelSim的仿真波形图-12

4.3 性能分析

  1)采用单周期方式(开源项目代码)下执行benchmark时钟周期数为:2698,而采用静态分支预测分支失败的五段流水方式benchmark时钟周期数为:3870。假定单周期方式时钟周期为五段流水方式的5倍,则加速比为:

S = 2698 × 5 Δ t 3870 × Δ t ≈ 3.49 S = \frac{2698 \times 5\Delta t}{3870 \times \Delta t} \approx 3.49 S=3870×Δt2698×5Δt3.49

  2)正确进行数据转发与阻塞;

  3)执行乘法需5个时钟周期,执行除法需10个时钟周期,该功能使用一个计数器模拟实现。

4.4 主要故障与调试

4.4.1 保持复位信号为1导致时序不正确

  复位信号reset只应在系统开始运行后的极短时间内为1,其后处于0状态。

  故障现象:寄存器中未观察到预期的数据,时序“不正确”。

  原因分析:复位信号reset用于将系统恢复至初始状态,只能在系统开始运行后的一小段时间内为1,使系统各个指标回复至初始态,便于数据的载入以及系统的正式运行。

  解决方案:reset最初赋值为1,至#5时改赋为0,此后若系统未遇特殊异常引起系统中断复位,则不再改变。

4.4.2 load-use冲突的处理未达预期效果

  因未在ID段检测信号,导致一条lw指令被挤掉。

  故障现象:指令执行后,寄存器中的数据并非预期的值。

  原因分析:benchmark中存在两条lw指令的目的寄存器编号分别与后一条指令两个源寄存器编号相同的情况,若在EX段才检测load-use冲突,则因为插入一个气泡,将第一条lw挤出流水线,故EX段时钟有效时,其需要的数据已经正确写回,从而无法通过定向技术获得。

  解决方案:按照标准的处理方法,将load-use的检测提前至ID段。

4.4.3 算术右移计算结果不正确

  如题,CPU在进行算术右移时所得计算结果不正确。

  故障现象:ALU在进行算术右移运算时,输入数据与控制信号都正确,但结果不对。

  原因分析:查阅有关资料得知被移位的对象必须定义为reg类型,而本设计实现起初是直接对输出信号进行assign赋值。

  解决方案:使用reg类型的临时变量进行转存。

4.4.4 其他BUG

  实验过程中遇到了各种各样稀奇古怪的bug,不一而足,“不足为外人道也”。

  故障现象:数组越界、语法错误、负载没有读进去,等等。

  原因分析:对Verilog不熟悉、前期准备不足。

  解决方案:查阅有关资料,求助同学。

5 总结与心得体会

5.1 课设总结

  本设计实现了全冒险处理机制的MIPS五段流水CPU,可以运行MIPS-C3指令集的所有指令,比较全面地解决了数据冲突、结构冲突、控制冲突等问题,支持数据阻塞和转发。本设计所实现的CPU执行乘法需要5个时钟周期,执行除法需要10个时钟周期,该功能使用一个计数器模拟实现。CPU在进行乘除法运算时,可以并行执行其他非乘除法指令,冒险检测单元会根据ID级的指令类型以及乘除法部件的busy值来判断是否对流水线进行暂停和冲刷。为和测试代码保持一致,方便进行测试,本设计所实现CPU支持一个延迟槽。值得一提的是,为简化控制信号,没有对ALU单独设置控制器,而是直接在主控制器中发出ALU控制信号。

5.2 课设心得

  在完成本次课程设计得过程中,我们通过查阅有关资料,完成了既定任务。早在大三《计算机组成原理》开课时,陈田老师就曾说过“这门课有一个课设,是让你们自己造一台CPU”,那时觉得是天方夜谭:本科生怎么可能做得出来CPU?后面经过学习与主动了解,明白了“造CPU”的具体要求,畏难情绪少了许多。计组算得上是对于课程考试并不十分擅长的我学得比较透彻的一门课,个中原因可能是自身对硬件比较喜爱吧。本次课程设计的一个遗憾是:我们没有足够的时间来将CPU烧到FPGA上面,完成硬件演示,功底还是差了许多,李荣浩的那句经典歌词“要是能从来,我要学李白……”。总之,CPU课设挺好玩的,哈哈哈!😄

  老师真帅,給个优吧!🤣😂良也行啊……QwQ

参考文献

[1]唐朔飞. 计算机组成原理(第2版)[M]. 北京:高等教育出版社,2008.
[2]张晨曦,王志英等. 计算机体系结构(第2版)[M]. 北京:高等教育出版社,2014.
[3] https://zh.wikipedia.org/zh-hans/%E6%B5%81%E6%B0%B4%E7%BA%BF_(%E8%AE%A1%E7%AE%97%E6%9C%BA)
[4]雷思磊. 自己动手写CPU[M]. 北京:电子工业出版社,2014.
[5]https://zh.wikipedia.org/wiki/%E5%86%92%E9%99%A9_(%E8%AE%A1%E7%AE%97%E6%9C%BA%E4%BD%93%E7%B3%BB%E7%BB%93%E6%9E%84)
[6]王泽坤. MIPS架构CPU设计及SoC系统实现[D]. 沈阳:东北大学,2014.
[7]蔡晓燕,袁春风,张泽生. MIPS架构多周期CPU的设计[D]. 计算机教育:第17期:93-96,2014.
[8] 李东泽,曹凯宁,曲明,王富昕. 五级流水线RISC-V处理器软硬件协同仿真验证[J]. 吉林大学学报(信息科学版):Vol. 35 NO.6:612-616,2017.
[9]Charles Price. MIPS IV Instruction Set[S]. MIPS Technologies, Inc. 1995.

附录

  项目源码已经托管至GitHub网站,网址:

  https://github.com/25thengineer/HFUT_2020_MIPS_CPU

  测试文件TESTBENCH:

#case 1
	.org 0x0
	.set noat
	.set noreorder			#不进行指令调度
	.set nomacro
	.global __start
__start:
	# 注意,MIPS编译时,会将rs和rt的二进制位置互换,写法上是rt,rs,指令码是opcode rs rt(写入rt)
	# 
	# 用nop和ori指令作为开始标志
	# start code
	# 3400 0000 3400 0000
	# ori $0, $0, 0x0000
	# ori $0, $0, 0x0000
	# 注意由于,编译装入问题,此处不用原先指令开头
	# 3403 8000
	# ori  $3, $0, 0x8000

	# 此处开始书写代码
	##################
	# Test Subset 2  #
	ori $v0, $0, 0x1234
	lui $v1, 0x9876
	addi $a0, $v0, 0x3456
	addi $a1, $v1, -1024
	xori $a2, $v0, 0xabcd
	slti $a1, $a0, 0x34
	slti $a1, $v0, -1
	andi $a3, $a2, 0x7654
	slti $t0, $v1, 0x1234 
	
	
	##################
	# Test Subset 1  #
	sub $t0, $v1, $v0
	xor $t1, $t0, $v1
	add $t2, $t1, $t0
	add $t2, $t2, $v0
	sub $t3, $t2, $v1
	nor $t4, $t3, $t2
	or  $t5, $t3, $t2
	and $t6, $t3, $t2
	slt $s3, $t5, $t4
	slt $s4, $t5, $t4
	
	### Test for shift
	sll $t0, $t0, 3
	srl $t1, $t0, 16
	sra $t2, $t0, 29
	addi $t3, $0, 0x3410


	# end code
	# 3400 0000 3400 0000
	ori $0, $0, 0x0000
	ori $0, $0, 0x0000
#case 2
	.org 0x0
	.set noat
	.set noreorder			#不进行指令调度
	.set nomacro
	.global __start
__start:
	# 注意,MIPS编译时,会将rs和rt的二进制位置互换,写法上是rt,rs,指令码是opcode rs rt(写入rt)
	# 
	# 用nop和ori指令作为开始标志
	# start code
	# 3400 0000 3400 0000
	# ori $0, $0, 0x0000
	# ori $0, $0, 0x0000
	# 注意由于,编译装入问题,此处不用原先指令开头
	# 3403 8000
	# ori  $3, $0, 0x8000

	# 此处开始书写代码
	addi $18 $0 1
	addi $19 $0 2
	add $17 $18 $19
	sub $20 $17 $18
	add $21 $19 $17


	# end code
	# 3400 0000 3400 0000
	ori $0, $0, 0x0000
	ori $0, $0, 0x0000
#case 3
	.org 0x0
	.set noat
	.set noreorder			#不进行指令调度
	.set nomacro
	.global __start
__start:
	# 注意,MIPS编译时,会将rs和rt的二进制位置互换,写法上是rt,rs,指令码是opcode rs rt(写入rt)
	# 
	# 用nop和ori指令作为开始标志
	# start code
	# 3400 0000 3400 0000
	# ori $0, $0, 0x0000
	# ori $0, $0, 0x0000
	# 注意由于,编译装入问题,此处不用原先指令开头
	# 3403 8000
	# ori  $3, $0, 0x8000

	# 此处开始书写代码
	addi $s5,$zero,7
	addi $sp,$sp,-12
	sw $s5,4($sp)#load-use hazard
	lw $s5,4($sp)
	sw $s5,8($sp)

	addi $s1,$zero,5
	sw $s1,0($sp)#sw-lw hazard
	lw $s1,0($sp)
	or $s4,$zero,$s1


	beq $zero,$zero,label #beq
	addi $s2,$zero,5
label:
	addi $s2,$zero,6
	addi $s3,$zero,7
	add $s4,$s3,$s2 #Data hazard
	add $s7,$s2,$zero #Structure hazard


	# end code
	# 3400 0000 3400 0000
	ori $0, $0, 0x0000
	ori $0, $0, 0x0000
#case 4
	.org 0x0
	.set noat
	.set noreorder			#不进行指令调度
	.set nomacro
	.global __start
__start:
	# 注意,MIPS编译时,会将rs和rt的二进制位置互换,写法上是rt,rs,指令码是opcode rs rt(写入rt)
	# 用ori指令作为开始标志
	# start code
	# ori $0, $0, 0x0000
	# 3400 0000
	# 此处开始书写代码
	.org 0x0000
	ori $0, $0, 0x0000
	
	# 真正的代码
	ori $3, $0, 0x8000 		# [1] $3 = 0x00008000
	sll $3, 16				# [2] $3左移16位,$3 = 0x80000000
	ori	$1, $0, 0x0001		# [3] $1 = 0x1
	b 	s1					# [4] 转移到s1处
	ori $1, $0, 0x0002		# [5] $1 = 0x2,延迟槽指令
b1:
	ori	$1, $0, 0x1111
	ori $1, $0, 0x1100
 
	.org 0x20
s1 :
	ori	$1, $0, 0x0004		# [6] $1 = 0x4
	movz $2, $1, $0 		# [7] $2 = $1 = 0x4
	bal s2					# [8] 转移到s2处,同时设置$31为0x30
	srlv $3, $3, $1			# [9] $3右移 $1 = 0x4 位,$3 = 0x08000000,延迟槽指令
	ori	$1, $0, 0x1100		# 地址为0x30,保存在$31中
	ori	$1, $0, 0x1111
	bne $1, $0, s3
	nop
	ori	$1, $0, 0x1100
	ori $1, $0, 0x1111

	.org 0x50
s2:
	ori	$1, $0,  0x0003		# [10] $1 = 0x3
	beq	$3, $3,  s3			# [11] $3等于$3,发生转移,目的地址是s3
	or  $1, $31, $0			# [12] $1 = 0x30,延迟槽指令
	ori	$1, $0,  0x1111
	ori $1, $0,  0x1100
b2:
	addi $2, $1, 0x0015 	# [16] $2 = $1(0x6) + 0x15 = 0x1b
	ori	$1, $0, 0x2			# [17] $1 = 0x2,
	mul $4, $1, $2 			# [18] $4 = $1(0x6) * $2(0x1b)
	ori $1, $1, 0xffff
	sll $1, $1, 16
	addi $1, $1, 0xffe3
	bgtz $2, s4				# [19] 此时$1为0x8,大于0,所以转移至标号s4处
	addi $1, $1, 0x1000
	
	.org 0x90
s3:
	ori $1, $0, 0x0005		# [13] $1 = 0x5
	bgez $1, b2				# [14] 此时$1为0x5 大于0,转移至前面的b2处
	ori $1, $0, 0x0006		# [15] $1 = 0x6,延迟槽指令
	ori	$1, $0, 0x1111 
	ori $1, $0, 0x1100
	nop 

s4:
	ori $2, $0, 0xffff      # [20]  $2 = 0x0000ffff
	sll $2, $2, 16			# $2 = 0xffff0000
	ori $2, $2, 0xfff1 		# $2 = -15 (0xfffffff1)
	ori $3, $0, 0x11 		# $3 = 17  (0x00000011)
	div  $0, $2, $3			# hi = 0xfffffff1
							# lo = 0x0					
	divu $0, $2, $3			# hi = 0x00000003
							# lo = 0x0f0f0f0e						
	div  $0, $3, $2			# hi = 0x02
							# lo = 0xffffffff
							# 
next0:
	j next0 # [21] 指令运行结束,等待退出
	nop


	# end code
	# 3400 0000 3400 0000
	ori $0, $0, 0x0000
	ori $0, $0, 0x0000
  • 23
    点赞
  • 197
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值