您好,欢迎来到筏尚旅游网。
搜索
您的当前位置:首页中山大学计算机组成原理实验 多周期CPU设计

中山大学计算机组成原理实验 多周期CPU设计

来源:筏尚旅游网
计算机组成原理实验

《计算机组成原理实验》

实验报告

(实验四)

学院名称 : 专业(班级) : 学生姓名 : 学时

号 :

数据科学与计算机学院

间 : 2019

12 月 14 日

计算机组成原理实验

成绩 :

实验四 : 多周期CPU设计与实现

一. 实验目的

(1) 认识和掌握多周期数据通路图的构成、原理及其设计方法;

(2) 掌握多周期CPU的实现方法,代码实现方法;

(3) 认识和掌握指令与CPU的关系; (4) 掌握多周期CPU的测试方法。

二. 实验内容

设计一个多周期CPU,该CPU在单周期指令集的基础上 ==> 算术运算指令

(1)add rd , rs, rt 000000 rs(5位) rt(5位) rd(5位) 00000 100000 功能:GPR[rd] ← GPR[rs] + GPR[rt]。 (2)sub rd , rs , rt 000000 rs(5位) rt(5位) rd(5位) 00000 100010 功能:GPR[rd] ← GPR[rs] - GPR[rt]。 (3)addiu rt , rs ,immediate 001001 rs(5位) rt(5位) immediate(16位) 功能:GPR[rt] ← GPR[rs] + zero_extend(immediate); immediate做符号扩展再参加“加”运算。

(4)addi rt , rs ,immediate 001000 加“加”运算。

rs(5位) rt(5位) immediate(16位) 功能:GPR[rt] ← GPR[rs] + sign_extend(immediate); immediate做符号扩展再参

==> 逻辑运算指令

(5)andi rt , rs ,immediate 001100 rs(5位) rt(5位) immediate(16位) 功能:GPR[rt] ← GPR[rs] and zero_extend(immediate);immediate做0扩展再参加“与”运算。

(6)and rd , rs , rt 000000 rs(5位) rt(5位) rd(5位) 00000 100100 功能:GPR[rd] ← GPR[rs] and GPR[rt]。 (7)ori rt , rs ,immediate 001101 rs(5位) rt(5位) immediate(16位) 计算机组成原理实验

功能:GPR[rt] ← GPR[rs] or zero_extend(immediate)。 (8)or rd , rs , rt 000000

==>移位指令

(9)sll rd, rt,sa 000000 00000 rt(5位) rd(5位) sa(5位) 000000 功能:GPR[rd] ← GPR[rt] << sa。 ==>比较指令

(10) slti rt, rs,immediate 带符号数 001010 rs(5位) rt(5位) immediate(16位) 功能:if GPR[rs] < sign_extend(immediate) GPR[rt] =1 else GPR[rt] = 0。 (11)slt rd, rs, rt 000000 000000 rs(5位) rs(5位) rt(5位) rt(5位) rd(5位) rd(5位) 00000 101010 00000 001011 (12)movn rd, rs, rt 功能:if GPR[rt] ≠ 0 then GPR[rd] ← GPR[rs]。 ==> 存储器读/写指令

(13)sw rt , offset (rs) 写存储器 101011 rs(5位) rt(5位) offset(16位) 功能:memory[GPR[base] + sign_extend(offset)] ← GPR[rt]。 (14) lw rt , offset (rs) 读存储器 100011 rs(5位) rt(5位) offset (16位) 功能:GPR[rt] ← memory[GPR[base] + sign_extend(offset)]。 (15)lhu rt, offset(base) 100101 base(5位) rt(5位) offset(16位) 功能:GPR[rt] ← memory[GPR[base] + offset]。

==> 分支指令

(16)beq rs,rt, offset 000100 rs(5位) rt(5位) offset (16位) 功能:if(GPR[rs] = GPR[rt]) pc←pc + 4 + sign_extend(offset)<<2 else pc ←pc + 4

特别说明:offset是从PC+4地址开始和转移到的指令之间指令条数。offset符号扩展之后左移2位再相加。为什么要左移2位?由于跳转到的指令地址肯定是4的倍数(每条指令占4个字节),最低两位是“00”,因此将offset放进指令码中的时候,是右移了2位的,也就是以上说的“指令之间指令条数”。 (17)bne rs,rt, offset 000101 rs(5位) rt(5位) offset (16位) 功能:if(GPR[rs] != GPR[rt]) pc←pc + 4 + sign_extend(offset) <<2 else pc ←pc + 4

(18)bltz rs, offset

rs(5位) rt(5位) rd(5位) 00000 100101 功能:GPR[rd] ← GPR[rs] or GPR[rt]。

计算机组成原理实验

000001 rs(5位) 00000 offset (16位) 功能:if(GPR[rs] < 0) pc←pc + 4 + sign_extend (offset) <<2 else pc ←pc + 4。

==>跳转指令

(19)j addr 000010 addr(26位) 功能:PC ← {PC[31:28] , addr , 2’b0},无条件跳转。 说明:由于MIPS32的指令代码长度占4个字节,所以指令地址二进制数最低2位均为0,将指令地址放进指令代码中时,可省掉!这样,除了最高6位操作码外,还有26位可用于存放地址,事实上,可存放28位地址,剩下最高4位由pc+4最高4位拼接上。

(20)jr rs 000000 rs(5位) 0000000000 未用 001000 功能:PC ← GPR[rs],跳转。

==> 停机指令 (21)halt 111111 00000000000000000000000000(26位) 功能:停机;不改变PC的值,PC保持不变。 ==>调用子程序指令 (22)jal addr 000011 addr[27:2] 功能:调用子程序,PC ← {PC[31:28] , addr , 2’b0};GPR[$31] ← pc+4,返回地址设置;子程序返回,需用指令 jr $31。跳转地址的形成同 j addr 指令。

增加实现以下指令功能操作。本次实验中需要实现运算操作的溢出判断:ALU运算操作溢出时,ALU需给出一位溢出信号(部分指令可能需要用到该信号。对于溢出发生时,需要能检测识别出,且不写回溢出错误结果,但不需要设计异常处理功能)。需设计的指令与格式如下,指令的具体描述和功能以mips官方文档为准:

三. 实验原理

多周期CPU指的是将整个CPU的执行过程分成几个阶段,每个阶段用一个时钟去完成,然后开始下一条指令的执行,而每种指令执行时所用的时钟数不尽相同,这就是所谓的多周期CPU。CPU在处理指令时,一般需要经过以下几个阶段:

(1) 取指令(IF):根据程序计数器pc中的指令地址,从存储器中取出一条指令,同时,pc根据指令字长度自动递增产生下一条指令所需要的指令地址,但遇到“地址转移”指令时,则控制器把“转移地址”送入pc,当然得到的“地址”需要做些变换才送入pc。 (2) 指令译码(ID):对取指令操作中得到的指令进行分析并译码,确定这条指令需要完成的操作,从而产生相应的操作控制信号,用于驱动执行状态中的各种操作。

(3) 指令执行(EXE):根据指令译码得到的操作控制信号,具体地执行指令动作,然后转移到结果写回状态。

(4) 存储器访问(MEM):所有需要访问存储器的操作都将在这个步骤中执行,该步骤给

计算机组成原理实验

出存储器的数据地址,把数据写入到存储器中数据地址所指定的存储单元或者从存储器中得到数据地址单元中的数据。 (5) 结果写回(WB):指令执行的结果或者访问存储器中得到的数据写回相应的目的寄存器中。

实验中就按照这五个阶段进行设计,这样一条指令的执行最长需要五个(小)时钟周期才能完成,但具体情况怎样?要根据该条指令的情况而定,有些指令不需要五个时钟周期的,这就是多周期的CPU。

取指令 IF 指令译码 ID 指令执行 EXE 存储器访问 MEM 结果写回 WB 图1 多周期CPU指令处理过程

MIPS指令的三种格式:

其中,

op:为操作码;

rs:为第1个源操作数寄存器,寄存器地址(编号)是00000~11111,00~1F; rt:为第2个源操作数寄存器,或目的操作数寄存器,寄存器地址(同上); rd:为目的操作数寄存器,寄存器地址(同上); sa:为位移量(shift amt),移位指令用于指定移多少位;

funct:为功能码,在寄存器类型指令中(R类型)用来指定指令的功能;

immediate:为16位立即数,用作无符号的逻辑操作数、有符号的算术操作数、数据加载(Load)/数据保存(Store)指令的数据地址字节偏移量和分支指令中相对程序计数器(PC)的有符号偏移量; address:为地址。

计算机组成原理实验

图2 多周期CPU状态转移图

状态的转移有的是无条件的,例如从sIF状态转移到sID就是无条件的;有些是有条件的,例如sEXE状态之后不止一个状态,到底转向哪个状态由该指令功能,即指令操作码决定。每个状态代表一个时钟周期。

图3 多周期CPU控制部件的原理结构图

图3是多周期CPU控制部件的电路结构,三个D触发器用于保存当前状态,是时序逻辑电路,RST用于初始化状态“000“,另外两个部分都是组合逻辑电路,一个用于产生下一个阶段的状态,另一个用于产生每个阶段的控制信号。从图上可看出,下个状态取决于指令操作码和当前状态;而每个阶段的控制信号取决于指令操作码、当前状态和反映运算结果的状态zero标志和符号sign标志。

计算机组成原理实验

图4 多周期CPU数据通路和控制线路图

图4是一个简单的基本上能够在多周期CPU上完成所要求设计的指令功能的数据通路和必要的控制线路图。其中指令和数据各存储在不同存储器中,即有指令存储器和数据存储器。访问存储器时,先给出内存地址,然后由读或写信号控制操作。对于寄存器组,给出寄存器地址(编号),读操作时不需要时钟信号,输出端就直接输出相应数据;而在写操作时,在 WE使能信号为1时,在时钟边沿触发将数据写入寄存器。图中控制信号功能如表1所示,表2是ALU运算功能表。

表1 控制信号作用

控制信号名 RST PCWre 状态“0” 状态“1” 对于PC,初始化PC为程序首地址 对于PC,PC接收下一条指令地址 PC不更改,相关指令:halt,另外,PC更改,相关指令:除指令halt外,除‘000’状态之外,其余状态慎另外,在‘000’(IF)状态时,修改PC改PC的值。 的值合适。 写指令寄存器不可用,从指令存储器到指令寄存器的通路阻断,指令寄存器IR的值不改变 ALU使能信号,其为0时即使时钟上升沿到来,ALU的值也不更改 数据存储器写使能信号,为0时不写入数据。指令:lw 根据IM的输入地址,把对应的指令写入IR,用于状态001(ID) ALU可用,根据时钟上升沿到来时的输入把运算结果和零信号写入相应的寄存器。 写内存可用,允许在时钟上升沿时把数据写入DM。指令:sw IRWre ALUenable MemWre 计算机组成原理实验

RegWre 无写寄存器组寄存器,相关指令: 寄存器组寄存器写使能,相关指令:beq、bne、bltz、j、sw、jr、halt add、sub、addiu、and、andi、ori、xori、sll、slt、slti、lw、jal 来自寄存器堆data1输出,相关指来自移位数sa,同时,进行令:add、sub、addiu、and、andi、(zero-extend)sa,即 {{27{1'b0},sa},ori、xori、slt、slti、sw、lw、beq、相关指令:sll bne、bltz 来自寄存器堆data2输出,相关指令:add、sub、and、slt、sll、beq、bne、bltz 这是一个特殊的信号,它用于movn指令的进行,movn指令应该在rt为0时调用写回模块,所以它应该被加在ALU上,同时它还充当着写回模块的一个使能信号 来自sign或zero扩展的立即数,相关指令:addiu、andi、ori、xori、slti、lw、sw ALU的运算结果就是rt,所产生的zero信号将被和move信号本身一起接入WB模块。当zero&move==1时,就启动写回rd<-rs. ALUSrcA ALUSrcB Move WrRegDSrc[1:0] 00: 来自ALU运算结果的输出,相关指令:add、sub、addiu、and、andi、ori、xori、sll、slt、slti 01: 来自数据存储器(Data MEM)的输出,相关指令:lw 10:写入寄存器组寄存器的数据来自pc+4(pc4),相关指令:jal,写$31 (zero-extend)immediate,相关指令:andi、xori、ori; (sign-extend)immediate,相关指令:addiu、slti、lw、sw、beq、bne、bltz; Extop PCSrc[1..0] 00:pc<-pc+4,相关指令:add、addiu、sub、and、andi、ori、xori、 slt、slti、sll、sw、lw、beq(zero=0)、bne(zero=1)、bltz(sign=0); 01:pc<-pc+4+(sign-extend)immediate ×4,相关指令:beq(zero=1)、 bne(zero=0)、bltz(sign=1); 10:pc<-rs,相关指令:jr; 11:pc<-{pc[31:28],addr[27:2],2'b00},相关指令:j、jal; 写寄存器组寄存器的地址,来自: 00:0x1F($31),相关指令:jal,用于保存返回地址($31<-pc+4); 01:rt字段,相关指令:addiu、andi、ori、xori、slti、lw; 10:rd字段,相关指令:add、sub、and、slt、sll; 11:未用; ALU 8种运算功能选择(000-111),看功能表 RegDst[1..0] ALUOp[2..0] 相关部件及引脚说明: Instruction Memory:指令存储器

Iaddr,指令地址输入端口 DataIn,存储器数据输入端口 DataOut,存储器数据输出端口

RW,指令存储器读写控制信号,为0写,为1读 Data Memory:数据存储器

Daddr,数据地址输入端口 DataIn,存储器数据输入端口

计算机组成原理实验

DataOut,存储器数据输出端口

/RD,数据存储器读控制信号,为0读 /WR,数据存储器写控制信号,为0写 Register File:寄存器组

Read Reg1,rs寄存器地址输入端口 Read Reg2,rt寄存器地址输入端口

Write Reg,将数据写入的寄存器,其地址输入端口(rt、rd) Write Data,写入寄存器的数据输入端口 Read Data1,rs寄存器数据输出端口 Read Data2,rt寄存器数据输出端口

WE,写使能信号,为1时,在时钟边沿触发写入 IR: 指令寄存器,用于存放正在执行的指令代码 ALU: 算术逻辑单元

result,ALU运算结果

zero,运算结果标志,结果为0,则zero=1;否则zero=0

sign,运算结果标志,结果最高位为0,则sign=0,正数;否则,sign=1,负数

表2 ALU运算功能表 ALUOp[2..0] 000 001 010 011 100 101 Y = A + B Y = A – B Y = B<基于上面所述,我们可以写出各状态和指令对应的控制信号表

状态 pcwre irwre aluen memwre memrde regwre 000 001 110 101 010 1 0 0 0 0 0 1 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 计算机组成原理实验

011 100 111 0 0 0 0 0 0 0 0 0 sw?1:0 0 0 lw?1:0 0 0 0 1 1

指令 add sub addiu addi andi and ori or sll slti slt movn sw lw lhu beq bne bltz j jr jal halt pcsrc extop alusrcA alusrcB regdst aluctr 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 01 01 11 10 11 hold x x 1 1 1 x 1 x x 1 x x 1 1 1 1 1 1 x x x x 0 0 0 0 0 0 0 0 1 0 0 ($0) 0 0 0 0 0 0 x x x x 0 0 1 1 1 0 1 0 0 1 0 0 1 1 1 0 0 0 x x x x 10 10 01 01 01 10 01 10 10 01 10 10 x 01 01 x x x x x 00 x 000 001 000 000 100 100 011 011 010 110 110 000 000 000 000 001 001 110 xxx xxx xxx xxx z zero zero zero zero zero zero zero zero zero zero zero zero zero zero zero zero ~zero ~zero zero zero zero zero WrRegsrc move hlf 00 00 00 00 00 00 00 00 00 00 00 ($rs) xx 01 01 xx xx xx xx xx 10 xx 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0

计算机组成原理实验

四. 实验器材

电脑一台,Xilinx Vivado 软件一套,Basys3板一块。

五. 实验过程与结果

Ⅰ状态控制器设计

多周期设计的特点就是使用不同的周期数执行不同的指令。为此,我们需要有限状态机来进行正确的状态跳转。一般的有限状态机设计需要复杂的逻辑设计来进行简化,这里我们只设置八个状态,用3位状态码表示。下一状态由当前状态码和当前指令决定,这一逻辑可以用逻辑分支语句实现。

图0 状态控制器的设计(部分)

Ⅱ模块设计

多周期设计的时候,不同于单周期的一点是,我们可以根据指令的指令过程,分步设计IF ID EXE MEM WB五个模块。

一、IF模块的分析与设计

首先我们要明确,所有的状态更新都在时钟上升沿进行,状态内的时序操作都在下降沿进行。

计算机组成原理实验

IF模块做的事情是:首先,在时钟上升沿(我们以上升沿作为一个周期的开始),我们通过状态控制器实现状态更新。控制器译码状态码,PCWre信号为1。然后,在同一时钟周期的下降沿,我们能够进行npc->pc的更新,这时,组合逻辑的IM会自动把对应地址的指令读出。不过这时IRWre信号为0,指令寄存器没有写入。

至于npc的更新,它应该发生在指令译码结束后。比较合适的时间点是ID阶段时钟下降沿,指令被改变进而使得pcsrc被改变后。因此npc更新的时间点应该是ID阶段结束时的上升沿。这时控制信号irwre为1,我们可以把这个信号也当作’npcwre’使用。这样,ID阶段结束时,npc被修改。

图1.1 IF模块的设计(部分)

二、ID模块的分析与设计

指令译码模块。首先我们同样在上升沿进行状态更新.然后在下降沿时,IRWre信号为1,我们可以进行指令寄存器数据的更新。更新完毕后,控制器产生的控制信号也将发生改变,同时如上面所述,npc也会改变,等待下一个IF过程到来时进行更新。

与此同时,纯组合逻辑的寄存器文件、扩展器和都将同时更改。由此产生的imm32和寄存器数值将用于之后的模块。

还有一点,当jal指令运行到该阶段时,我们要特殊地直接进入WB阶段。而且regdst也会被控制器特殊地设置为00,以把pc+4写入$31.

计算机组成原理实验

图1.2 ID模块的设计

三、EXE模块的分析与设计

指令的具体执行阶段。主要是ALU的运算,所以我们用一个ALUenable来让ALU工作一次。由前面的指令译码,我们已经得到了对应的alusrc、extop和aluctr信号。在时钟下降沿运算完毕后,运算结果写入ALUout寄存器中,直到下一个EXE过程都不会被更改。

需要补充的一点,本实验新增了运算溢出标识的需求。这就要求在ALU模块进行add和sub运算时,不但要区分有符号数和无符号数运算,还要进行判断返回一个overflow位。这个标志位将直接接到WB模块,当运算溢出时,不写入运算结果。

图1.3 EXE模块overflow的设计

四、MEM模块的分析与设计

运算结束后的ALUout直接连接到Datamemory的地址输入端。同样,在一个上升沿过后,状态码改变。这里设置读状态码memrd和写状态码memwre。如果是读内存,则Memrd=1,那么就把内存中的对应数据读到数据总线。如果是写内存,则MemWre=1,在时钟下降沿时,Datamemory把Datain写入内存。

要补充的一点是,本实验新增了读半字指令,因此Datamemory还需要一个half信号

计算机组成原理实验

标识它在读数据时,应该读字还是半字。

图1.4 MEM模块的设计

五、WB模块的分析与设计

由于我们要设计movn指令,一个把rs写入rd的数据通路就是必须的。 问题是,该如何区分一般的数据总线到寄存器的写入和movn指令的特殊写入。 我们在写寄存器时的相关信号有: RegWre、Move、zero、WrRegDSrc和RegDst。 不能写的情况是Move=1 且 zero=0; 写rs的情况是Move=1 且 zero=1; 写数据总线的情况是Move=0;

区分出这种特殊情况后,就可以用WrRegDSrc和RegDst来确定数据总线的数值和要写入的目标寄存器地址,在下降沿进行寄存器写入。

我们可以把原有的寄存器文件和上面的逻辑封装在一起,在顶层模块使用时,就直接用WB模块来代表读寄存器和写寄存器两种操作。

计算机组成原理实验

图1.5 WB模块的设计

Ⅱ 模块调试与验证

本实验基于单周期处理器设计实验进行,所以单周期处理器中所设计的指令沿用下来,一般不会出现问题。这里重点对多周期处理器添加的新功能和新指令进行调试。

一、ALU的算术运算与溢出

本实验区分无符号数运算和有符号数运算。两种运算的计算结果相同但溢出判断方法不同。这里我们要设置一个独特的标识位标记当前进行的是有符号数加减法、无符号数加减法还是其他运算。因此我们至少要设置两位的calmode[1:0]位。

calmode 00 01 10 11 指令运算模式 非算术运算 有符号加法运算 无符号加法运算 有符号减法运算 判断是否溢出需要设置一个额外的标志位。对无符号数溢出和有符号数溢出有不同的判断方式。

测试程序

0x0 addiu $1 $0 0xffff

001001 00000 00001 1111 1111 1111 1111

计算机组成原理实验

0x4 addiu $1 $1 0x1

001001 00001 00001 0000 0000 0000 0001

0x8 addi $2 $1 0x7fff

001000 00001 00010 0111 1111 1111 1111

0xc addiu $1 $0 0x4000

001001 00000 00001 0100 0000 0000 0000

0x10 add $1 $1 $1

000000 00001 00001 00001 00000 100000

0x14 j 4

000010 00000000000000000000000100

这段程序首先给$1赋值为0xffffffff,当再做无符号数加1时,出现无符号数溢出。 再做有符号数加法$2=$1+0x00007fff,不出现有符号数溢出,正常赋值。$2=0x00007ffe

赋值$1赋值为0x00004000,进行有符号数加法$1=$1+$1,一开始不出现有符号数溢出。直到加到0x40000000,这时再做加法会有有符号数溢出。

图2.1 溢出验证1

如图2.1可以看到,无符号数加法0xffffffff+1会出现溢出,禁止写入。有符号数加法0xffffffff+0x00007fff则不会溢出,正常写入0x00007ffe。

图2.2 溢出验证2

在下面的循环部分,$1的值不断加倍,直到0x40000000。这时再进行$1=$1+$1则会产生有符号数溢出。因此可以看见,后面overflow信号一直为1,$1的写入也无法正常进

计算机组成原理实验

行,一直为0x40000000。

二、条件移数指令movn

movn指令是一条特殊的指令。它的ALU运算和寄存器写数据来源都是由一个特殊的信号move来特殊控制的。信号的执行经过EXE阶段时,ALU返回的是$rt;这样当$rt==0时,zero就为1.我们就可以用move信号和zero信号来控制WB模块正确完成写入。

测试程序

0x0 addiu $1 $0 0xf

001001 00000 00001 0000 0000 0000 1111

0x4 addiu $2 $0 0xff

001001 00000 00010 0000 0000 1111 1111

0x8 addiu $3 $0 0x1

001001 00000 00011 0000 0000 0000 0001

0xc addiu $4 $0 0x0

001001 00000 00100 0000 0000 0000 0000

0x10 movn $5 $1 $4

000000 00001 00100 00101 00000 001011

0x14 movn $5 $2 $3

000000 00010 00011 00101 00000 001011

图2.3 movn验证

可以看到,在第一条movn,由于zero==1且move==1,111WB状态时进行了特殊的写入,$5被写成0xf。而在第二条movn,由于zero==0,regwre信号被强行设置为0,禁止了movn形式的写入,而且一般形式的写入也无法进行。

三、取半字指令lhu

计算机组成原理实验

这个比较简单,在datamemory模块增加控制信号readhalf。当它为1时,把读数的高16位设置为0x0000.

测试程序

0x0 addiu $1 $0 0xffff

001001 00000 00001 1111 1111 1111 1111

0x4 sw $1 0($0)

101011 00000 00001 0000 0000 0000 0000

0x8 lw $2 0($0)

100011 00000 00010 0000 0000 0000 0000

0xc lhu $3 0($0)

100101 00000 00011 0000 0000 0000 0000

图2.4 lhu验证

很容易看到lw取了32位全字,而lhu只取了半字且用0补全。

四、子程序调用指令jal、jr

这也是一条特殊的指令,与j指令不同的地方在于:jal还需要写回。它需要特殊地设置写寄存器来源为pc+4,写目标寄存器为$31.另外,j指令的流程是IF->ID, jal指令的流程是IF->ID->WB。

jr是跳转到寄存器地址,流程是IF->ID,特殊设置pcsrc为RS即可。 测试程序

0x0 jal 2

000011 00000000000000000000000010

0x4 halt

111111 00000000000000000000000000

0x8 addiu $1 $0 0xffff

001001 00000 00001 1111 1111 1111 1111

计算机组成原理实验

0xc jr $31

000000 11111 00000 00000 00000 001000

图2.5 jal,jr验证

容易看出,在jal执行后,$31的值为pc+4=0x4.程序在0x8处继续运行,在下次调用jr $31时返回0x4,进行停机。

五、算术运算指令测试

测试程序(*add, addiu, sub, bne*) 0x0 addiu $2 $0 3

001001 00000 00010 0000 0000 0000 0011

0x4 addiu $3 $0 5

001001 00000 00011 0000 0000 0000 0101

0x8 addiu $1 $0 0

001001 00000 00001 0000 0000 0000 0000

0xc addiu $1 $1 1

001001 00001 00001 0000 0000 0000 0001

0x10 add $2 $2 $2

000000 00010 00010 00010 00000 100000

0x14 sub $2 $2 $1

000000 00010 00001 00010 00000 100010

0x18 bne $1 $3 -4

000101 00001 00011 1111 1111 1111 1100

0x1c halt

111111 00000000000000000000000000

程序执行一个循环,C语言如下

计算机组成原理实验

int a = 3, b = 5; for (int i=1;i!=b;i++) {

a = a+a; }

a = a-i;

图2.6 算术运算指令验证

容易看到程序经过几次循环后达到halt指令后,PC值不再变化,CPU进入待机状态。最终的运行结果:[$1] = 101b = 5,[$2] = 100111b = 39,其结果与我们预期结果相同;可以判断三个算术运算指令和bne、halt指令都已经成功实现

六、逻辑运算指令测试 测试程序(*andi, or, slti, beq*)

0x0 addiu $1 $0 0xffff

001001 00000 00001 1111 1111 1111 1111

0x4 andi $1 $1 0x9999

001100 00001 00001 1001 1001 1001 1001

0x8 slti $2 $1 0x0088

001010 00001 00010 0000 0000 1000 1000

0xc slti $3 $1 0xa000

001010 00001 00011 1010 0000 0000 0000

0x10 or $4 $2 $3

000000 00010 00011 00100 00000 100101

0x14 beq $2 $4 1

000100 00010 00100 0000 0000 0000 0001

0x18 sub $1 $1 $1

计算机组成原理实验

000000 00001 00001 00001 00000 100010

0x1c halt

111111 00000000000000000000000000

图2.7 逻辑运算指令验证

可以看见r1是0xffff和0x9999与运算后的结果,0x9999。且r2、r3都是正确的比较运算得到的布尔值。r4是r2和r3做或运算的结果。因为r3==r4,所以beq跳转到halt,不经过sub语句,r1的结果不变。

程序按照我们的预期进行,可以判断逻辑运算语句和slti、beq语句都成功实现。 七、访存指令测试 测试程序(*sw, lw, bltz, j*)

0x0 addiu $1 $0 1

001001 00000 00001 0000 0000 0000 0001

0x4 addiu $2 $0 1

001001 00000 00010 0000 0000 0000 0001

0x8 addiu $4 $0 0

001001 00000 00100 0000 0000 0000 0000

0xc addiu $5 $0 10

001001 00000 00101 0000 0000 0000 1010

0x10 add $3 $1 $2

000000 00001 00010 00011 00000 100000

0x14 sw $3 0($4)

101011 00100 00011 0000 0000 0000 0000

0x18 add $1 $0 $2

计算机组成原理实验

000000 00000 00010 00001 00000 100000

0x1c lw $2 0($4)

100011 00100 00010 0000 0000 0000 0000

0x20 addiu $4 $4 4

001001 00100 00100 0000 0000 0000 0100

0x24 addi $5 $5 -1

001000 00101 00101 1111 1111 1111 1111

0x28 bltz $5 1

000001 00101 00000 0000 0000 0000 0001

0x2c j 4

000010 00000000000000000000000100

0x30 halt

111111 00000000000000000000000000

这段程序循环11次,执行斐波那契数列的计算并把结果保存在一段连续的数据存储器中。我们很容易得到前11位斐波那契数列的值:

1、1、2、3、5、8、13、21、34、55、、144、233

程序在我们设计的CPU中运行得到的波形图如下

图2.8 访存指令验证

可见其r3寄存器的值是最后一次的计算结果11101001b = 233,内存的值也把这个数据大端存储。r2读取这段内存的值,也为233。计数器r5的值从10不断自减到-1,才跳出循环。

综上,我们的这段程序也顺利运行,所有的存储器存取指令、跳转指令和分支指令都已经成功完成设计。

八、移位指令测试 测试程序(*sll*)

0x0 addiu $1 $0 9

计算机组成原理实验

001001 00000 00001 0000 0000 0000 1001

0x4 sll $1 $1 1

000000 00000 00001 00001 00001 000000

0x8 sll $1 $1 2

000000 00000 00001 00001 00010 000000

0xc sll $1 $1 3

000000 00000 00001 00001 00011 000000

0x10 sll $1 $1 4

000000 00000 00001 00001 00100 000000

0x14 halt

111111 00000000000000000000000000

图2.9 移位指令验证

可以看到原始的reg1 1001每次向左移动指定的位数,最终稳定后的结果一共移动了10位。可以判断左移指令也设计完毕。

Ⅲ 硬件实现

一、数码管的显示

数码管采用扫描显示。我们首先把Basys板自带的时钟做分频,获取适当频率的新工作时钟。把时钟接到一个4进位计数器,根据计数器的数据决定当前时刻使能哪一个数码管。数码管的数据输入有四种,PC+NPC、RS+RA、RT+RB、ALUOUT+DB。用一个二位的SWITCH信号控制数据输入选择哪一个,数据输入需要经过译码才能接入七段数码管。CPU的时钟接手动时钟,同时设置一个防抖器(用多级寄存器实现,当多个寄存器的值都相同时才真正更改时钟信号,起到防抖功能)。

计算机组成原理实验

图3.1 扫描显示的实现

二、状态LD的显示

我们的状态码有8种,要显示的状态有五种,我们可以用一个寄存器数组来实现译码。把状态码对应到相应的状态并接入LD灯显示出来。

图3.2 状态译码的实现

然后要做的就是选择合适的程序烧入板内,我使用的验证程序如下 地址 0x00000000 0x00000004 0x00000008 0x0000000C 汇编程序 addiu $1,$0,14 addiu $2,$0,-5 and $2,$1,$2 ori $1,$1,9 指令代码 op(6) rs(5) 001001 001001 000000 001101 00000 00000 00001 00001 rt(5) rd(5)/immediate (16) 00001 00010 00010 00001 0000 0000 0000 1110 1111 1111 1111 1011 00010 00000 100100 0000 0000 0000 1001 16进制数代码 = 计算机组成原理实验

0x00000010 0x00000014 0x00000018 0x0000001C 0x00000020 0x00000024 0x00000028 0x0000002C 0x00000030 0x00000034 0x00000038 0x0000003C 0x00000040 0x00000044 0x00000048 0x0000004C 0x00000050 0x000000 0x00000058 0x0000005C 0x00000060 0x000000 0x00000068 0x0000006C 0x00000070 0x00000074 0x00000078 0x0000007C 0x00000080 0x00000084 0x00000088 0x0000008C 0x00000090 0x00000094 0x00000098 0x0000009C 0x000000A0 0x000000A4 0x000000A8 0x000000AC 0x000000B0 0x000000B4 0x000000B8 andi $2,$2,13 sll $1,$1,1 or $1,$0,$2 slti $3,$1,8 slti $4,$1,9 add $1,$2,$4 sub $1,$2,$1 addi $1,$1,1 addi $2,$2,-8 addi $3,$3,-0 addi $4,$4,-1 or $5,$1,$2 or $5,$5,$3 or $5,$5,$4 addiu $0,$0,-2 beq $5,$0,1 halt addiu $1,$0,0 addiu $2,$0,-3 sw $2,0($1) lw $1,0($1) bne $1,$2,3 bltz $1,1 halt j 0x1e halt jal 0x30 addi $1,$0,32767 sll $1,$1,16 addi $1,$1,32767 addi $1,$1,32767 addi $2,$1,0 addi $2,$2,2 slt $3,$2,$1 addi $1,$0,1 addi $2,$0,2 addi $3,$0,3 movn $1,$3,$0 movn $2,$3,$2 addiu $1,$0,0 sll $2,$2,16 sw $2,8($1) lhu $3,10($1) 001100 000000 000000 001010 001010 000000 000000 001000 001000 001000 001000 000000 000000 000000 001001 000100 111111 001001 001001 101011 100011 000101 000001 111111 000010 111111 000011 001000 000000 001000 001000 001000 001000 000000 001000 001000 001000 000000 000000 001000 000000 101011 100101 00010 00000 00000 00001 00001 00010 00010 00001 00010 00011 00100 00001 00101 00101 00000 00101 00010 00001 00010 00011 00100 00100 00001 00001 00010 00011 00100 00010 00011 00100 00000 00000 0000 0000 0000 1101 00001 00001 000000 00001 00000 100101 0000 0000 0000 1000 0000 0000 0000 1001 00001 00000 100000 00001 00000 100010 0000 0000 0000 0001 1111 1111 1111 1000 0000 0000 0000 0000 1111 1111 1111 1111 00101 00000 100101 00101 00000 100101 00101 00000 100101 1111 1111 1111 1110 0000 0000 0000 0001 00000000000000000000000000 00000 00000 00001 00001 00001 00001 00001 00010 00010 00001 00010 00000 0000 0000 0000 0000 1111 1111 1111 1101 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0011 0000 0000 0000 0001 00000000000000000000000000 00000000000000000000011110 00000000000000000000000000 00000000000000000000110000 00000 00000 00001 00001 00001 00010 00010 00000 00000 00000 00011 00011 00000 00000 00001 00001 00001 00001 00001 00001 00010 00010 00001 00001 00010 00011 00000 00010 00001 00010 00010 00011 0111 1111 1111 1111 00001 10000 000000 0111 1111 1111 1111 0111 1111 1111 1111 0000 0000 0000 0000 0000 0000 0000 0010 00011 00000 101010 0000 0000 0000 0001 0000 0000 0000 0010 0000 0000 0000 0011 00001 00000 001011 00010 00000 001011 0000 0000 0000 0000 00010 10000 000000 0000 0000 0000 1000 0000 0000 0000 1010 计算机组成原理实验

0x000000BC 0x000000C0 halt jr $31 111111 000000 00000000000000000000000000 11111 00000 00000 00000 001000 程序的执行和单周期时的验证程序大同小异,只不过多了新功能的验证(movn、lh、jal、jr和算术溢出)。实地检查时都已检查过,这里不再做过多展示。

图3.3 BEQ跳转

六. 实验心得

本实验基于单周期CPU的实验进行,多周期与单周期最大的不同点就是时序逻辑和状态。多周期将一条指令拆分为几个状态,每个状态执行的工作。为此,我们需要在设计CPU时,增加状态码并在主控制器里对状态码进行译码,生成各器件的控制信号。当控制信号可用时,该状态需要的器件才能工作,这保证了CPU工作的稳定性。

本实验基于单周期的指令集进行设计,原有的指令,除了新增的一些控制信号以外基本不用做大的更改。主要的设计工作是增添新的指令,我个人认为最有挑战性的是溢出检验,因为verilog的设计比较高层,它会直接补全加法器的设计,我们不能把课本上的“数值位和符号位溢出做异或”直接拿来用,而是要自己设计新的电路去验证是否溢出,途中还遇到了赋值导致的数据丢失引起的bug,verilog基础不好的确给我带来了蛮大的困难。

但是不得不承认,经历了单周期CPU实验后,我的能力有了长足的长进。在进行本实验时,我不会再感到无从下手,在遇到BUG时也能够熟练的使用各种工具寻找错误的来源,很快就能解决问题。

对比单周期实验,本实验带给我特殊的收获。我更深一步地理解了硬件设计的思想和流程,也对“为什么需要多周期”的问题有了自己的思考。不过由于能力和时间,我没能完成流水线CPU的设计,实属遗憾。

Copyright © 2019- efsc.cn 版权所有 赣ICP备2024042792号-1

违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务