✅作者简介:CSDN内容合伙人、信息安全专业在校大学生
系列专栏 :信息安全本科生课设-系统硬件综合设计报告
新人博主 :欢迎点赞收藏关注,会回访!
舞台再大,你不上台,永远是个观众。平台再好,你不参与,永远是局外人。能力再大,你不行动,只能看别人成功!没有人会关心你付出过多少努力,撑得累不累,摔得痛不痛,他们只会看你最后站在什么位置,然后羡慕或鄙夷。
2022年 6月
word转markdown格式有点乱,见谅
基于先修课程,根据系统设计思想,使用硬件描述语言设计实现一款基于MIPS32,ARM,RISC-V或者自定义指令集的微处理器(CPU)。要求:完成单周期CPU设计,或多周期CPU设计,或5级流水线CPU设计(递进式、难度依次提升。所有学生必须至少完成单周期CPU的设计工作),并将设计的CPU下载至FPGA开发板(ego-1)上运行。以此贯穿数字逻辑、计算机组成原理、计算机体系结构课程,实现从逻辑门至完整CPU处理器的设计。
Windows10电脑一台
Xilinx Vivado 软件一套
FPGA开发板(ego-1)一块。
多周期CPU指的是将整个CPU的执行过程分成几个阶段,每个阶段用一个时钟去完成,然后开始下一条指令的执行,而每种指令执行时所用的时钟数不尽相同,这就是所谓的多周期CPU。CPU在处理指令时,一般需要经过以下几个阶段:
(1) 取指令(IF):根据程序计数器pc中的指令地址,从存储器中取出一条指令,同时,pc根据指令字长度自动递增产生下一条指令所需要的指令地址,但遇到“地址转移”指令时,则控制器把“转移地址”送入pc,当然得到的“地址”需要做些变换才送入pc。
(2) 指令译码(ID):对取指令操作中得到的指令进行分析并译码,确定这条指令需要完成的操作,从而产生相应的操作控制信号,用于驱动执行状态中的各种操作。
(3) 指令执行(EXE):根据指令译码得到的操作控制信号,具体地执行指令动作,然后转移到结果写回状态。
(4) 存储器访问(MEM):所有需要访问存储器的操作都将在这个步骤中执行,该步骤给出存储器的数据地址,把数据写入到存储器中数据地址所指定的存储单元或者从存储器中得到数据地址单元中的数据。
(5) 结果写回(WB):指令执行的结果或者访问存储器中得到的数据写回相应的目的寄存器中。
图 1 多周期CPU阶段转化图
MIPS的指令是32位的,相当于一条指令的含义与操作细节完全由32个二进制数完全决定。对32位二进制数的不同划分使用,构成了不同的指令格式。MIPS指令集有三种指令格式:R型指令,I型指令,J型指令
图 2 MIPS的三种指令格式
其中:
**Op:**指令操作码;
**Rs:**第一个源操作数寄存器号,参与运算使用;
**Rt:**第二个源操作数寄存器号,参与运算使用;
**Rd:**目的操作数寄存器号,保存结果使用;
**Shamt:**位位移量,移位指令用于指定移多少位;
**Func:**指令函数码,用于选择Op操作中的具体函数;
**Immediate:**为16位立即数,用作无符号的逻辑操作数、有符号的算术操作数、数据加载、数据保存指令的数据地址字节偏移量和分支指令中相对程序计数器的有符号偏移量;
**Address:**为26位地址或16位地址。
算数运算指令实现三条,如下表1所示。实现的功能依次为Rd = Rs + Rt; Rd = Rs – Rt; Rt = Rs + immediate(需扩展到32位)
表 1 算数运算指令
Add | Rs | Rt | Rd | |
---|---|---|---|---|
000000(6位) | Rs(5位) | Rt(5位) | Rd(5位) | Reserved(11位) |
Sub | Rs | Rt | Rd | |
000001(6位) | Rs(5位) | Rt(5位) | Rd(5位) | Reserved(11位) |
Addi | Rs | Rt | Immediate | |
000010(6位) | Rs(5位) | Rt(5位) | immediate (16位) |
移位和比较指令实现三条,如下表2所示。实现的功能依次为Rd = Rt<< Shamt; 带符号数if(Rs
Sll | Rt | Rd | Shamt | ||
---|---|---|---|---|---|
011000 (6位) | Rs(5位) | Rt(5位) | Rd(5位) | Shamt (5位) | Reserved(6位) |
Slt | Rs | Rt | Rd | ||
100110 (6位) | Rs(5位) | Rt(5位) | Rd(5位) | reserved(11位) | |
Sltiu | Rt | Rs | Immediate | ||
100111(6位) | Rs(5位) | Rt(5位) | immediate (16位) |
存储器读写指令实现两条,如下表3所示。实现的功能依次为:MEM[Rs+ Immediate] = Rt ; Rt = MEM[Rs+ Immediate];(需扩展到32位)
表 3 存储器读写指令
SW | Rs | Rt | Immediate | |
---|---|---|---|---|
110000 (6位) | Rs(5位) | Rt(5位) | Rd(5位) | immediate (16位) |
LW | Rs | Rt | Immediate | |
110001 (6位) | Rs(5位) | Rt(5位) | Rd(5位) | immediate (16位) |
跳转指令实现四条,如下表4所示。实现的功能依次为:if(Rs = Rt) pc = pc + 4 + Immediate <<2 else pc = pc + 4; if(Rs < 0) pc = pc + 4 + Immediate <<2 else pc = pc + 4; pc = [pc+4[31:28] + Address + 00];pc = Rs;
表 4 跳转指令
Beq | Rs | Rt | Immediate |
---|---|---|---|
110100 (6位) | Rs(5位) | Rt(5位) | immediate (16位) |
Bltz | Rs | Immediate | |
110110(6位) | Rs(5位) | Rt(5位) | immediate (16位) |
J | Address | ||
111000 (6位) | Address(26位) | ||
Jr | Rs | ||
111001 (6位) | Rs(5位) | Reserved(21位) |
逻辑运算指令实现三条,如下表5所示。实现的功能依次为:Rd = Rs | Rt; Rd = Rs & Rt; Rt = Rs | Immediate(需扩展到32位)
表 5 逻辑运算指令
Or | Rs | Rt | Rd | |
---|---|---|---|---|
010000(6位) | Rs(5位) | Rt(5位) | Rd(5位) | Reserved(11位) |
And | Rs | Rt | Rd | |
010001(6位) | Rs(5位) | Rt(5位) | Rd(5位) | Reserved(11位) |
Ori | Rs | Rt | Immediate | |
010010(6位) | Rs(5位) | Rt(5位) | immediate (16位) |
其它指令实现两条,如下表6所示。实现的功能分别是pc = [pc+4[31:28] + Address + 00],&31=nextPC;(保护现场);停机指令。
表 6 其它指令
Jal | Address |
---|---|
111010 (6位) | Address(26位) |
Halt | |
111111(6位) |
根据多周期CPU的数据通路和控制线路,将其分为5个大模块,21个小模块互相调用,最终利用顶层CPU封装将其链接到一起,如图3所示,其中从上到下,从左到右依次的功能为,获取当前PC;获取下一个PC;获取当前指令;获取下一条指令;依照MIPS三种指令格式分割指令;ALU的两个端口输入A和B;一个输出端口result;一个寄存器回写端口DB;立即数扩展;数据存储器;数据寄存器组;取指;译码;执行;访问存储器;寄存器回写;
图 3 多周期CPU模块划分图
程序输入的参数有时钟信号,重置信号,是否PC可更改信号,下一条指令的地址。输出当前指令的地址。判断逻辑为,当重置信号到来时,PC置0;当时钟上升沿和PC可更改信号来临时,PC的值更新为下一条指令的值。如何确定下一条指令的地址呢?需要有一个CU译码之后的控制信号,来决定下一条PC的地址,根据控制信号的不同的值,输出下一条PC的地址为PC+4,或者PC+4+Immediate*4或者寄存器Rs的值,或者一个跳转地址。
本程序将要运行的指令存放到同路径下的txt文件中,调用$readmemh()函数来实现对文件的读取和指令寄存器的赋值,读取时为16进制。在定义指令寄存器时,定义存储器单元长度为8,共128个存储单元,可存放1024bit, 可存放32条指令(空间大小为自定义,可扩展)。每次读取指令时,根据PC传入的地址,采用大端方式顺序读取一条指令。读取完成之后,并没有第一时间去赋值给其它模块,而是通过一个IR缓冲器,当时钟上升沿到来时,才更新当前指令。每当指令被更新后,程序需要根据图2所示的MIPS的三种指令格式来分割指令,为后续CU译码做准备。至此,PC模块结束。
表 7 PC模块实现(1)
输入 | 时钟信号,重置信号,PC更改信号,next_PC |
---|---|
输出 | 当前指令地址 |
功能 | 当时钟上升沿和PC可更改信号来临时,PC的值更新为下一条指令的值 |
表 8 PC模块实现(2)
输入 | 重置信号,PC, next_PC, Rs, Address,选择器 |
---|---|
输出 | 下一条指令地址 |
功能 | 当时钟信号来临时,根据选择器的值,让next_PC的值得到更新 |
表 9 PC模块实现(3)
输入 | 时钟信号,IR可更改信号 |
---|---|
输出 | 更新后的IR |
功能 | 当时钟上升沿和IR可更改信号来临时,IR的值得到更新 |
表 10 PC模块实现(4)
输入 | IR |
---|---|
输出 | OP, Rs, Rt, Rd, Shamt, Immediate, Address |
功能 | 当IR值被更新后,根据MIPS的指令格式,对其进行分割 |
该模块输入两个32位的被运算的数A和B, 一个5位的偏移量,和一个32位的立即数,通过对不同的ALU操作码的分析,进行不同的运算或逻辑操作,最终返回运算结果和是否为0的标志位(该标志位用于有条件跳转)。ALU操作码的定义如下表4。
表 11 ALU的操作运算及功能
OP(3位) | 功能描述 |
---|---|
000 | 两个操作数加 |
001 | 两个操作数减 |
010 | 两个操作数的无符号比较 |
011 | 两个操作数的有符号比较 |
100 | 一个操作数左移另一个操作数位 |
101 | 两个操作数或 |
110 | 两个操作数与 |
111 | 两个操作数异或 |
其中,在立即数扩展时,需要输入一个标志位。该标志位决定它是无符号扩展还是有符号扩展。无符号扩展时,无论正数负数,高位全部补0即可;符号扩展时,正数的高位全补0,负数的高位全补1。最终返回被扩展的立即数。
表 12 ALU模块实现(1)
输入 | 操作数A和B, ALU_OP, Shamt, Extend |
---|---|
输出 | 0标志位Zero_sign,result |
功能 | 根据ALU_OP,对输入进行不同的运算或逻辑操作。 |
表 13 ALU模块实现(2)
输入 | 立即数,扩展信号 |
---|---|
输出 | 扩展后的立即数 |
功能 | 根据扩展信号的不同,对立即数进行符号或无符号扩展 |
该模块分为寄存器组和主存两个模块。其中寄存器组为32个32位的寄存器,有两个读端口和一个写端口,一个输入时钟信号和一个写使能信号,以及三个寄存器的地址。此处存储器的规模为8个32bit的空间(可自定义扩展),通过时钟输入信号来启动程序,通过读写使能信号来判断是读还是写。存储器有一个读端口,一个 写端口,一个存储器地址和两个读写信号,通过读写信号来判断是读还是写,此处读写均为大端操作。因为多周期可能存在寄存器回写这一操作,所以此处用了DB复用,通过DB信号来决定DB的值是来自于存储器还是寄存器(ALU运算结果)。在下降沿来临时,进行回写操作。
表 14 WR模块实现(1)
输入 | 时钟信号,两个读端口,一个写端口,读地址和写地址 |
---|---|
输出 | 从寄存器读出的数或写回寄存器的数 |
功能 | 对寄存器组进行读写操作 |
表 15 WR模块实现(2)
输入 | 一个读信号,一个写信号,读写地址,一个读端口,一个写端口 |
---|---|
输出 | 读出的值或回写寄存器的值 |
功能 | 对存储器进行读写操作 |
为了使指令和数据保持稳定,此处设置了4个缓冲寄存器。在时钟上升沿来临前,保存当前的值,在时钟上升沿来临之后再赋给相应的值。4个缓冲寄存器分别设立在ALU的两个输入,一个输出,寄存器回写DB处。四个寄存器功能相同,可以使用一个模块实例得到。
表 16 buffer模块实现
输入 | 时钟信号,缓冲数据X |
---|---|
输出 | 赋值数据data_X |
功能 | 在时钟上升沿来临时,将X赋值给data_X,使数据稳定 |
该模块为多周期CPU的核心模块。在CU中,我设置了一个CPU的状态机,通过对不同操作吗的译码,当前状态以及时钟信号,来决定它的输出信号和下一个状态。例如J, Jal, Jr, Halt这些指令只有两个状态,即取指令和指令译码,所以在指令译码完成后不需要进行后面的指令执行,访存,结果写回等,而是状态变为取指令。Beq, Bltz 指令有三个状态,分别是取指令,指令译码,指令执行。SW指令有四个状态,分别是取指令,指令译码,指令执行,访存。LW指令有五个状态,分别是取指令,指令译码,指令执行,访存,结果写回。其它指令为四个状态,分别是取指令,指令译码,指令执行,结果写回。不同指令对应于不同的状态体现了多周期CPU的核心。
图 4 CPU状态机的五个状态
因为CU是控制中心,负责发送各种信号。所以程序输入时钟信号,复位信号,以及是否为零的标志位,和指令对应的六位操作码,经过对OP的分析,对其赋值不同的当前状态和下一个状态。并且还有如下功能:经过译码,发出PC和指令是否可更改信号,是否IR指令可以被分割信号,在ALU运算中是移位运算还是立即数算数运算信号,结果写回的数据是来自于寄存器还是存储器信号,求next_PC时的两位指令信号,以及是否是保护现场信号,是否是符号扩展信号,是否是存储器读写信号,是否是跳转信号,以及跳转的方式信号。
输入 | 时钟信号,复位信号,零标志位信号,指令操作码 |
---|---|
输出 | 各种其它模块要使用的控制信号 |
功能 | 依据当前的状态、操作码以及零标志位修改并且输出控制信号。 |
测试数据如下表:
表 17 模拟仿真的测试数据
地址 | 指令代码 | 汇编代码 | |||
---|---|---|---|---|---|
OP(6) | Rs(5) | Rt(5) | Rd(5)/Immediate (16) | ||
0x00 | 000010 | 00010 | 00001 | 0000 0000 0001 0000 | Addi,$1,$2,16 |
0x04 | 010000 | 00000 | 00001 | 0000 0000 0000 0000 | Or $1,$0,$1 |
0x08 | 010010 | 00011 | 00010 | 0000 0000 0000 0011 | Ori $2,$3,3 |
0x0C | 011000 | 00000 | 00010 | 0001 1000 0100 0000 | Sll $3,$2,1 |
0x10 | 010001 | 00010 | 00011 | 0010 0000 0000 0000 | And $4,$2,$3 |
0x14 | 000001 | 00011 | 00001 | 0010 1000 0000 0000 | Sub $5,$3,$1 |
0x18 | 110100 | 00001 | 00001 | 0000 0000 0000 0001 | Beq $0,$1,1 |
0x1C | 111010 | 00000 | 00000 | 0000 0000 0000 1101 | Jal 0x34 |
0x20 | 110110 | 00101 | 00000 | 0000 0000 0000 0001 | Bltz $5,1 |
0x24 | 111010 | 00000 | 00000 | 0000 0000 0000 1101 | Jal 0x34 |
0x28 | 110000 | 00010 | 00001 | 0000 0000 0000 0001 | Sw $0,1($2) |
0x2C | 110001 | 00010 | 00110 | 0000 0000 0000 0001 | Lw $6,1($2) |
0x30 | 111000 | 00000 | 00000 | 0000 0000 0000 1101 | J 0x34 |
0x34 | 111111 | 00000 | 00000 | 0000 0000 0000 0000 | halt |
指令PC=0x00为Addi加法操作指令,将 2 号寄存器的数据(0)与立即数 16 相加,并且将运算结果(16) 写回 1 号寄存器。需要取指令,指令译码,指令执行,结果写回四个步骤,四个周期即可完成。如图所示,1号寄存器的值为(16)。
图 5 指令PC=0x00仿真结果图
指令PC=0x04为Or或操作指令,将0号寄存器的数据(0)和1号寄存器的数据(16)或运算,将结果(16)写回1号寄存器。需要取指令,指令译码,指令执行,结果写回四个步骤,四个周期即可完成。如图所示,1号寄存器的值为(16)。
图 6指令PC=0x04仿真结果图
指令PC=0x08为Ori或操作指令,将3号寄存器的数据(0)与立即数 3 进行或操作,并且将运算结 果(3)写回 2 号寄存器。需要取指令,指令译码,指令执行,结果写回四个步骤,四个周期即可完成。如图所示, 2号寄存器的值为(3)。
图 7 指令PC=0x08仿真结果图
指令PC=0x0C为Sll移位操作,将2号寄存器的数据(3)左移1位,并将运算结果(6)写回到3号寄存器。需要取指令,指令译码,指令执行,结果写回四个步骤,四个周期即可完成。如图所示, 3号寄存器的值为(6)。
图 8 指令PC=0x0C仿真结果图
指令PC=0x10为 And与操作指令,将 2 号寄存器的数据(3)与 3 号寄存器的数据(6)进行与操作,并且将运算结果(2)写回 4 号寄存器。需要取指令,指令译码,指令执行,结果写回四个步骤,四个周期即可完成。如图所示,4号寄存器的值为(2)。
图 9 指令PC=0x10仿真结果图
指令PC=0x14为Sub减法操作指令,将 3 号寄存器的数据(6)与 1 号寄存器的数据(16)进行减法操作,并且将运算结果(—10)写回 5 号寄存器。需要取指令,指令译码,指令执行,结果写回四个步骤,四个周期即可完成。如图所示,5号寄存器的值为(—10)。
图 10 指令PC=0x14仿真结果图
指令PC=0x18为Beq为跳转指令,将 1号寄存器的数据(16)与 1 号寄存器的数据(16)进行比较,如果二者相等则跳转(跳转到地址:0x20),否则继续顺序执行(地址0x1C)。本条指令将发生跳转,因为$0 与$1 所存储的数据相等。需要取指令,指令译码,指令执行,三个周期即可完成。如图所示,下一条指令地址为0x20。
图 11 指令PC=0x18仿真结果图
指令PC=0x20为Bltz为跳转指令,5 号寄存器的数据(-10)小于零,则跳转(跳转到地址:0x28),否则继续顺序执行(地址0x24)。本条指令将发生跳转,需要取指令,指令译码,指令执行,三个周期即可完成。如图所示,下一条指令地址为0x28。
图 12 指令PC=0x20仿真结果图
指令PC=0x28为Sw存储器写指令。该指令将2号寄存器内容(3)和立即数符号扩展后的数(1)相加作为内存地址(4),将1号寄存器的值16赋值给该内存单元。写入模式为大端。指令需要取指令,指令译码,指令执行,存储器访问,寄存器写回五个步骤,五个周期即可完成。如图所示(16)被赋值给内存
图 13 指令PC=0x28仿真结果图
指令PC=0x2C为Lw存储器读指令。该指令将2号寄存器内容(3)和立即数符号扩展后的数(1)相加作为内存地址(4),从内存中取出值将值(16)赋值给6号寄存器。写入模式为大端。指令需要取指令,指令译码,指令执行,存储器访问,寄存器写回五个步骤,五个周期即可完成。如图所示,6号寄存器的值为(16)。
图 14 指令PC=0x2C仿真结果图
指令PC=0x30为J 跳转指令,程序跳转到0x34地址,指令需要取指令和指令译码,不需要指令执行、存储器访问和数据写回,因此两个周期即可完成。如图所示,下一条指令的地址为0x34
图 15指令PC=0x30仿真结果图
指令PC=0x34为Hlat停机指令,PC后续都不发生改变。
图 16 指令PC=0x34仿真结果图
过本次实验,我熟悉了verilog语言的语法,可以熟练的使用vivado。同时也培养了我的动手能力,“实验就是为了让你动手做,去探索一些你未知的或是你尚不是深刻理解的东西。每个步骤我都亲自去做,不放弃每次锻炼的机会。经过这一周,让我的动手能力有了明显的提高。计算机组成原理的课程历时大半个学期,做实验时通过自己编写、运行程序,不仅可以巩固了以前所学过的知识,而且学到了很多在书本上所没有学到过的知识。以前对于编程工具的使用还处于一知半解的状态上,但是经过一段上机的实践,对于怎么去排错、查错,怎么去看每一步的运行结果,怎么去了解每个寄存器的内容以确保程序的正确性上都有了很大程度的提高。多周期CPU,加深了我们对初学对verilog指令的熟悉和理解。通过学习和使用,向上为理解各种软件系统打好基础,打下技术理论基础;向下为掌握硬件系统的原理,打下实践应用基础。不仅巩固了书本所学的知识,还具有一定的灵活性,发挥了我们的创造才能。
通过这次课程设计使我懂得了理论与实际相结合是很重要的,只有理论知识是远远不够的,只有把所学的理论知识与实践相结合起来,从理论中得出结论,才能真正提高自己的实际动手能力和独立思考的能力。在设计的过程中遇到问题,可以说得是困难重重,这毕竟第一次做的,难免会遇到过各种各样的问题,同时在设计的过程中发现了自己的不足之处,对以前所学过的知识理解得不够深刻,掌握得不够牢固。这次课程设计终于顺利完成了,在设计中遇到了很多编程问题,最后在自己的思考以及和同学的讨论中,终于迎刃而解。