赞
踩
转载请注明出处
作者:雪天鱼
更多博客、资料和业务承接发布在我的个人网站上,欢迎交流学习。
此设计要求来源于一个课程设计任务书。
设计实现一个字长8位的CPU,所设计的系统能调试通过,进行仿真测试后在FPGA开发板上运行一段程序,通过检查程序结果的正确性来判断所设计计算机系统的正确性。
CPU架构
(1)自行设计指令格式
序号 | 指令 | 功能 |
---|---|---|
0 | NOP | 无操作 |
1 | LDAC | AC <— M[T] |
2 | STAC | M[T] <— AC |
3 | MOVAC | R <— AC |
4 | MOVR | AC <— R |
5 | JUMP | GOTO |
6 | JMPZ | IF(Z=1) GOTO T |
7 | JPNZ | IF(Z=0) GOTO T |
8 | ADD | AC <— AC+R |
9 | SUB | AC <— AC-R |
10 | INAC | AC<— AC+1 |
11 | CLAC | AC <— 0 |
12 | AND | AC <— AC^R |
13 | OR | AC <— AC or R |
14 | XOR | AC <— AC xor R |
15 | NOT | AC <— ~AC |
注:T是16位地址,M是数据存储器。
上述指令中包含:
单字指令:
三字指令:
(2)自行设计数据通路
(3)能运行由自己所设计的指令系统构成的一段程序,程序执行功能正确。
(4)仿真测试后,综合下载到FPGA开发板上测试。
(5)在FPGA开发板上测试程序时,可单步执行,应能通过数码管适时显示信息。
首先进行指令的系统的设计。
(1)参考mips的指令类型,先将要实现的指令进行分类:
类型 | 指令 |
---|---|
RR类型(寄存器-寄存器) | MOVR、MOVAC、ADD、SUB、INAC、CLAC、AND、OR、XOR、NOT(单字指令) |
RS类型(寄存器-存储器) | LDAC(装入字)、STAC(存储字)(三字指令) |
J类型 | JUMP、JMPZ、JPNZ(三字指令) |
O类型(其他指令) | NOP(单字指令) |
由于要求设计的CPU的通用寄存器就两个,一个AC(目的寄存器),一个是R(源寄存器),所以RR类型指令都不需要rd,rs字段来指定对应寄存器,直接根据指令操作码就可以完成对应指令功能,所以为单字指令;而分类到RS类型和J类型的指令则需要提供16位地址才能完成对应功能,所以为三字指令。
(2)字长为8bit,一共16条指令,故可设计指令格式为:
序号 | 指令 | 格式 | 功能 |
---|---|---|---|
0 | NOP | 00_0000_00 | 空操作 |
1 | MOVAC | 01_0000_00 | R <— AC |
2 | MOVR | 01_0001_10 | AC <— R |
3 | ADD | 01_0010_10 | AC <— AC + R |
4 | SUB | 01_0011_10 | AC <— AC - R |
5 | INAC | 01_0100_10 | AC <— AC + 1 |
6 | CLAC | 01_0101_10 | AC <— 0 |
7 | AND | 01_0110_10 | AC <— AC and R |
8 | OR | 01_0111_10 | AC <— AC or R |
9 | XOR | 01_1000_10 | AC <— AC xor R |
10 | NOT | 01_1001_10 | AC <— ~AC |
11 | LDAC | 10_0000_10 T | AC <— M[T] |
12 | STAC | 10_0001_00 T | M[T] <— AC |
13 | JUMP | 11_0000_00 T | GOTO T |
14 | JMPZ | 11_0001_00 T | IF(Z=1) GOTO T |
15 | JPNZ | 11_0010_00 T | IF(Z=0) GOTO T |
即将16条指令分类为上述四类,通过指令的高两位指示。其中0 ~ 10为单字指令,11~15为三字指令,T为16位地址。
(3)将指令信号名设置为instr,位宽为8bit
则
type(instr[7:6]) | op(instr[5:2]) | reserve(instr[1:0]) |
---|
type(instr[7:6]) | op(instr[5:2]) | rd(instr[1]) | reserve(instr[0]) |
---|
type字段指示指令类型
op即为操作码,指示具体指令;
rd指示目的寄存器为哪个, 自定义rd=1时为AC;rd=0时为R
instr[0]为保留位,默认为0
type(instr[7:6]) | op(instr[5:2]) | dir(instr[1]) | reserve(instr[0]) | T(16bit) |
---|
dir指示数据传输方向,自定义dir=1时数据从M[T]传输到AC;dir=0时数据从AC传输到M[T]
type(instr[7:6]) | op(instr[5:2]) | reserve(instr[1:0]) | T(16bit) |
---|
由于有些RR指令的目的寄存器和操作数所用的寄存器一致并且存在三字指令,所以,打算搭建的是多周期CPU,其中单字指令单时钟周期执行完毕,而三字指令需要三时钟周期才能执行完毕。由2.2指令系统设计可知,需要执行的指令集,存在R、I、J、O四种类型的指令,需要的组件有:
(1)基础组件:NPC、PC、指令存储器IM、寄存器文件RF(即AC和R)、运算器ALU、数据存储器DM、控制单元Control unit
(2)添加组件:
(1)取指(Fetch):
PC的输出信号输入到指令存储器地址端,取出指令instr
(2)译码(Decode):
指令instr输入到控制器CTRL和地址寄存器AR,在控制器中译码,输出控制信号到NPC、ALU、AR、RF和DM
(3)执行(Execute):
ALU从AC和R中读出数据,根据对应控制信号进行相应的计算,并输出计算结果和标志信号
(4)存储器(Memory)
RS类型的指令需要读写存储器
(5)写回(Writeback):
RR类型的指令会将数据写回寄存器文件,即写入AC或者R
编译后的原理图如下图所示:
控制器为硬布线控制器,输出的控制信号如表所示:
信号名 | 作用 |
---|---|
T | 三字指令标志 |
RegDst | 选择RF数据写入的寄存器 |
RegWrite | 寄存器文件RF写使能 |
RegData | RF写入数据来源选择 |
MemWrite | 数据存储器Mem写使能 |
ALUOp | 运算器ALU控制信号 |
NPCOp | Next PC计算控制信号 |
指令 | T | RegDst | RegWrite | RegData | MemWrite | ALUOp | NPCOp |
---|---|---|---|---|---|---|---|
NOP | 0 | x | 0 | x | 0 | 0000 | 00 |
MOVAC | 0 | 0 | 1 | 1 | 0 | 0001 | 00 |
MOVR | 0 | 1 | 1 | 1 | 0 | 0010 | 00 |
ADD | 0 | 1 | 1 | 1 | 0 | 0011 | 00 |
SUB | 0 | 1 | 1 | 1 | 0 | 0100 | 00 |
INAC | 0 | 1 | 1 | 1 | 0 | 0101 | 00 |
CLAC | 0 | 1 | 1 | 1 | 0 | 0110 | 00 |
AND | 0 | 1 | 1 | 1 | 0 | 0111 | 00 |
OR | 0 | 1 | 1 | 1 | 0 | 1000 | 00 |
XOR | 0 | 1 | 1 | 1 | 0 | 1001 | 00 |
NOT | 0 | 1 | 1 | 1 | 0 | 1010 | 00 |
LDAC | 1 | 1 | 1 | 0 | 0 | 1111 | 00 |
STAC | 1 | x | 0 | x | 1 | 1111 | 00 |
JUMP | 1 | x | 0 | x | 0 | 1111 | 01 |
JMPZ | 1 | x | 0 | x | 0 | 1111 | 01 |
JPNZ | 1 | x | 0 | x | 0 | 1111 | 01 |
注:x表示任意值均可(此时该控制信号无效)
编译后的原理图如下图所示:
这里我用Verilog实现了此字长8位的简单CPU。
下面进行仿真测试
(1)测试RR类型指令:
可以看到寄存器R的值在执行完MOVAC指令后变为AC存储值2,说明指令执行没问题。
可以看到寄存器AC的值在执行完MOVR指令后,AC存储值变为5,说明指令执行没问题。
可以看到寄存器AC的值在执行完ADD指令后,AC存储值变为7,说明指令执行没问题。
可以看到寄存器AC的值在执行完INAC指令后,AC存储值变为3,说明指令执行没问题。
可以看到寄存器AC的值在执行完CLAC指令后,AC存储值变为0,说明指令执行没问题。
可以看到寄存器AC的值在执行完AND指令后,AC存储值变为0,说明指令执行没问题。
可以看到寄存器AC的值在执行完OR指令后,AC存储值变为7,说明指令执行没问题。
可以看到寄存器AC的值在执行完NOT指令后,AC存储值变为-3,说明指令执行没问题。
(2)测试RS类型指令
可以看到寄存器AC的值在执行完LDAC指令后,AC存储值变为2,且三个时钟周期执行完毕,说明指令执行没问题。
可以看到在执行完STAC指令后,M[4]存储单元值变为2,且三个时钟周期执行完毕,说明指令执行没问题。
(3)测试J类型指令
可以看到在执行完JUMP指令后,指令instr变为04,且三个时钟周期执行完毕,说明指令执行没问题。
可以看到在执行完JPNZ指令后,指令instr变为04,且三个时钟周期执行完毕,说明指令执行没问题。
可以看到在执行完JMPZ指令后,指令instr变为04,且三个时钟周期执行完毕,说明指令执行没问题。
编写一个简易的程序同时进行多条指令的测试,如下所示:
AC初始值为2 ,R初始值为5 M[0]~M[127]初始值分别对应 0 ~127
地址 | 指令 | 机器码 | 结果 |
---|---|---|---|
0 | MOVAC | 40 | AC:2 R:2,其余不变 |
1 | ADD | 4A | AC:4 R:2,其余不变 |
2 | MOVR | 46 | AC:2 R:2, 其余不变 |
3 | INAC | 52 | AC:3 R:2,其余不变 |
4 | SUB | 4E | AC:1 R:2,其余不变 |
5 | XOR | 62 | AC:3 R:2,其余不变 |
6 | AND | 5A | AC:2 R:2,其余不变 |
7 | NOP | 00 | AC:2 R:2,其余不变 |
8 | NOT | 66 | AC:-3 R:2,其余不变 |
9 | LDAC 16’h0004 | 82 0004 | AC:4 R:2,其余不变 |
10 | OR | 5E | AC:6 R:2,其余不变 |
11 | STAC 16’h0002 | 84 0002 | AC:6 R:2,M[2]=6 |
12 | JUMP 16’h000E | C0 000E | 跳转到地址0E |
13 | JPNZ 16’h0010 | C8 0010 | 跳转到地址10(程序结束) |
14 | CLAC | 56 | AC:0 R:2,其余不变 |
15 | JMPZ 16’h000D | C4 000D | 跳转到地址0D |
仿真测试波形图如下图所示:
可以看到AC值和R值变化与预期一致,且最终在ff地址处结束,说明程序执行正确无误。8位字长的简单CPU搭建完毕!
开发板如图20所示:
首先需要设置好顶层模块的输入输出接口,即如何显示输入指令和显示运行结果。如下表:
即通过拨码开关输入指令,通过按键控制指令执行和复位,数码管和LED灯指示执行结果和执行状态。
CPU分为三个状态:IN状态,CHECK状态和RUN状态。IN状态为输入状态:从switch开关上输入指令并保存;CHECK状态为检查状态:检查输入的指令是否正确;RUN状态:依次执行之前输入的指令。
(1)如何搭建数据通路?
首先遇到的问题就是搭建怎样的数据通路,要能满足任务书中所提到的要求。我的解决思路是通过查阅教材,查找执行不同类型指令所必须的组件,然后将它们组合到一起,设置控制信号,得到了最后所设计的数据通路。
(2)如何设计指令系统?
其次遇到的问题是怎么为指令编码,编码要有意义,这样才好扩展指令集,虽然现在就只是实现16条指令,但思路要按照可扩展的指令集来,培养个人能力。所以通过翻阅资料,参考mips的指令集,先对指令进行分类,再根据不同类型设置不同的字段,自行设计了一个简单指令系统。
(3)如何处理三字指令?
单字指令好处理,一个周期就可以执行完,但三字指令不行,需要添加一个地址寄存器,用来储存后面的二字,即地址,再通过控制信号T来指示三字指令。三字指令用三个时钟周期执行完毕。
(4)如何上板测试?
上板测试需要设置CPU有对应的输入和输出端口,能接收外部的信号输入,并输出对应的结果以便查看验证。通过研究板卡的资源设置和CPU构造,最终敲定了输入输出端口分别与板子上的什么硬件相连接。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。