2complexcpudesign.docx
《2complexcpudesign.docx》由会员分享,可在线阅读,更多相关《2complexcpudesign.docx(17页珍藏版)》请在冰点文库上搜索。
2complexcpudesign
根据上一节设计得的简单的CPU我们增加一定的指令,设计一个相对复杂一点的CPU。
这个CPU的设计流程与前面相似。
1定义一个CPU
本章我们介绍组成这个CPU的指令体系结构的存储器模型、寄存器和指令集:
首先我们来看它的存储器M模型,这个CPU能够访问的地址空间为64Kbytes,其中1byte=8bits;这个CPU的输出输出端口用存储器的访问模式(也就是与存储器统一编址,或者叫做存储器影射IO)。
这个CPU有3个通用寄存器a、AC[7:
0](与simple中相同),b、R[7:
0]通用寄存器,用于提供第二个操作数,也可以用来暂存AC的值。
这个CPU还有c、一个“0”标志寄存器Z(只有1bit),用于标志运算结果为0。
其他的程序员不能访问的寄存器有:
16bit的地址寄存器AR[15:
0]保存16位地址。
16bit的程序计数器PC[15:
0]保存下一条指令的地址。
8bit的数据寄存器DR[7:
0]接受指令和存储器来的数据。
8bit的指令寄存器IR[7:
0]存储指令操作码
8bit的数据暂存器TR[7:
0]
最后这个CPU的指令集如下:
Instruction
Code
Operation
NOP
00000000
NoOperation
LDAC
00000001T
AC<—M[T]
STAC
00000010T
M[T]<—AC
MOVAC
00000011
R<—AC
MOVR
00000100
AC<—R
JUMP
00000101T
GOTOT
JMPZ
00000110T
IF(Z=1)GOTOT
JPNZ
00000111T
IF(Z=1)GOTOT
ADD
00001000
AC<—AC+R,IF(AC+R=0)Z<—1
ELSEZ<—0
SUB
00001001
AC<—AC—R,IF(AC+R=0)Z<—1
ELSEZ<—0
INAC
00001010
AC<—AC+1,IF(AC+1=0)Z<—1
ELSEZ<—0
CLAC
00001011
AC<—0,Z<—1
AND
00001100
AC<—AC^R,IF(AC^R=0)Z<—1
ELSEZ<—0
OR
00001101
AC<—ACorR,IF(ACorR=0)Z<—1
ELSEZ<—0
XOR
00001110
AC<—ACxorR,IF(ACxorR=0)Z<—1
ELSEZ<—0
NOT
00001111
AC<—!
AC,IF(!
AC=0)Z<—1
ELSEZ<—0
2取指和译码指令
这个CPU的取指方式和前面所讲的simple相同,只是指令寄存器是8bit取值周期如下:
Fetch1:
AR<—PC[15:
0]
Fetch2:
DR<—M[7:
0],PC<—PC+1[15:
0]
Fetch3:
IR<—DR[7:
0],AR<—PC[15:
0]
然后我们用分析simpleCPU的方法分析各个执行例程,可得状态转化图如下:
3指令执行
接下来我们来为状态图中的各个执行子例程进行分析:
3.1NOP指令
NOP指令时最容易实现的一条指令,CPU什么都不作,而只是去下一条指令就可以了。
我们创造一个空状态,什么都不做NOP1
3.2LDAC指令
LDAC指令包括三个部分:
操作码、地位地址、高位地址。
执行时必须从存储器取出地址然后再从相应的地址取数据到AC中。
在这里,我们注意到:
在取指的执行过程中(Fetch2),存储地址T的低8位已经放入PC中,并在Fetch3的时候load到AR中,所以此时CPU要做的事情就是
1〉PC+1,以便取下一条指令(在这里便是T的高8位地址)
2〉AR+1,取T的高8位地址
LDAC1:
DR<—M,PC<—PC+1,AR<—AR+1
接下来CPU可以取T的高8位地址了,同时它必须用数据暂存器暂存低8位的地址,并且PC+1:
LDAC2:
TR<—DR,DR<—M,PC<—PC+1
这时CPU得到了T的地址,可以进行从存储器读取数据的操作了,首先把地址复制到AR,然后把要去的数载入到DR,最后复制数据到AC中。
LDAC3:
AR<—DR,TR
LDAC4:
DR<—M
LDAC5:
AC<—DR
3.3STAC指令
STAC指令的执行与LDAC相反,指令执行过程如下:
STAC1:
DR<—M,PC<—PC+1,AR<—AR+1
STAC2:
TR<—DR,DR<—M,PC<—PC+1
STAC3:
AR<—DR,TR
STAC4:
DR<—AC(仅4、5与LDAC相反)
STAC5:
M<—DR
3.4MVAC和MOVR指令
这两条指令都是比较直接明了的,CPU只是按要求进行数据传送:
MVAC1:
R<—AC
MOVR1:
AC<—R
3.5JUMP指令
JUMP指令执行时,CPU先按照LDAC的取址方式取出要跳转的地址,然后送PC
JUMP1:
DR<—M,AR<—AR+1
JUMP2:
TR<—DR,DR<—M
JUMP3:
PC<—DR,TR
3.6JMPZ和JPNZ指令
JMPZ和JPNZ指令都有两种不同的可能,需要判断“0”标志寄存器来决定执行哪个子例程。
如果发生跳转,CPU将执行类似于JUMP的子例程,如果不发生跳转,CPU不能简单地回到取指例程,因为此时PC中是没有发生跳转的跳转地址的低8位,如果不发生跳转CPU必须使PC+2来跳过跳转地址,然后进入取指子例程。
JMPZ执行状态如下:
JMPZY标志执行跳转,JMPZN表示不执行跳转:
JMPZY1:
DR<—M,AR<—AR+1
JMPZY2:
TR<—DR,DR<—M
JMPZY3:
PC<—DR,TR
JMPZN1:
PC<—PC+1
JMPZN2:
PC<—PC+1
JPNZ的执行状态如下:
JPNZY表示执行跳转,JPNZN表示不执行跳转
JPNZY1:
DR<—M,AR<—AR+1
JPNZY2:
TR<—DR,DR<—M
JPNZY3:
PC<—DR,TR
JPNZN1:
PC<—PC+1
JPNZN2:
PC<—PC+1
3.7其余指令
其余指令都是在一个状态完成的,CPU完成两件事情:
计算结果送AC,置“0”标志寄存器Z。
指令执行状态如下:
ADD1:
AC<—AC+R,IF(AC+R=0)Z<—1ELSEZ<—0
SUB1:
AC<—AC-R,IF(AC-R=0)Z<—1ELSEZ<—0
INAC1:
AC<—AC+1,IF(AC+1=0)Z<—1ELSEZ<—0
CLAC1:
AC<—0,Z<—1
AND1:
AC<—AC^R,IF(AC^R=0)Z<—1ELSEZ<—0
OR1:
AC<—ACorR,IF(ACorR=0)Z<—1ELSEZ<—0
XOR1:
AC<—ACxorR,IF(ACxorR=0)Z<—1ELSEZ<—0
NOT1:
AC<—!
AC,IF(!
AC=0)Z<—1ELSEZ<—0
完整的状态图如下:
4建立数据路径
类似前面的SimpleCPU,这个CPU也使用内部总线来传递数据。
首先我们按照寄存器来安排数据传输如下:
AR:
AR<—PC;AR<—AR+1;AR<—DR,TR
PC:
PC<—PC+1;PC<—DR,TR;
DR:
DR<—M;DR<—AC
IR:
IR<—DR
R:
R<—AC
TR:
TR<—DR
AC:
AC<—DR;AC<—R;AC<—AC+R;AC<—AC-R;
AC<—AC+1;AC<—0;AC<—AC^R;AC<—ACorR;
AC<—ACxorR;AC<—!
AC
Z:
Z<—1;Z<—0;
由上面的这些操作我们可以定义以下组件的功能为:
ØAR和PC:
必须能够执行并行的载入(load),加一(inc),并且数据来自总线。
ØDR、IR、R和TR:
必须能够并行的载入(load),数据来自总线。
ØAC和Z:
与ALU相关的动作执行。
ALU接受AC中的数据作为一个操作数,数据总线作为另外一个操作数,AC的输入来自ALU,Z的值是由ALU根据计算结果的判断来设定的。
AC寄存器设计成一个有并行载入的寄存器,他的加一功能将由ALU完成,Z是一个1位的带并行载入的寄存器。
我们把这些数据路径的组件用总线连接起来,得到了这个CPU的数据路径如下:
下面我们来修改数据路径:
1〉AR、IR不提供任何数据给其他组件,所以可以把它们与内部总线的连接去掉。
2〉引脚D[7:
0]为双向引脚,要用双向Buffer。
3〉内部为16位的总线,而很多寄存器是8位的。
需要制定与总线的连接。
4〉“0”标志寄存器Z还没有连接,把ALU的输出NOR一下,如果为1则置Z=1,否则Z=0。
也就是把Z接到ALU输出的NOR上。
注:
总线的占用情况
ØAR、PC是16位寄存器直接连接,剩下的8bit的寄存器连接到16位总线的低8位。
ØFetch3状态IR<—DR,AR<—PC,低8位总线争用,由于IR仅仅从DR接受数据,所以建立一条DR到IR的直接的数据路径,而去掉总线到IR的连接。
ØLDAC2状态和其他一些状态有TR<—DR,DR<—M争用总线,同样TR仅仅从DR接受数据,所以建立一条DR到TR的直接数据路径,去掉总线到TR的连接。
ØLDAC3和其他几个状态有DR和TR同时把数据放到总线上形成16位的地址,必须是DR为高8位TR为低8位,解决的办法就是把DR的数据同时连接到总线的高8位和低8位上,然后增加控制信号。
最后我们可以得到完整的数据路径图如下:
5ALU的设计
要设计ALU,我们首先来看那些指令修改了AC的值:
LDAC5:
AC<—DR
MOVR1:
AC<—R
ADD1:
AC<—AC+R
SUB1:
AC<—AC-R
INAC1:
AC<—AC+1
CLAC1:
AC<—0
AND1:
AC<—AC^R
OR1:
AC<—ACorR
XOR1:
AC<—ACxorR
NOT1:
AC<—!
AC
首先我们来设计算数部分:
LDAC5:
AC<—BUS
MOVR1:
AC<—BUS
ADD1:
AC<—AC+BUS
SUB1:
AC<—AC-BUS
INAC1:
AC<—AC+1
CLAC1:
AC<—0
每条指令都可以用一个带进位的并行加法器实现,如下:
LDAC5:
AC<—0+BUS+0
MOVR1:
AC<—0+BUS+0
ADD1:
AC<—AC+BUS+0
SUB1:
AC<—AC+!
BUS+1
INAC1:
AC<—AC+0+1
CLAC1:
AC<—0+0+0
然后我们来实现逻辑功能,需要一个8位的4选1逻辑。
整个ALU的逻辑框图如下:
6控制单元的设计
本设计中CPU共有37个状态,所以用两个寄存器来生成所需状态,一个用作指令IR译码,另一个用于指名IR译码指令的子例程在执行第几个状态。
指令译码的设计:
因为操作码就存储在IR寄存器中,所以控制单元用IR寄存器的输出作为译码器的输入,由于指令格式为0000xxxx所以我么之需要译码低4位就可以了,NOR高4位用于使能译码器。
用于指名译码后执行的子例程处于第几个状态的计数器前3个状态都一定是Fetch1,fetch2,Fetch3,接下来的状态才是执行子例程的状态。
指令的执行状态要靠译码器和计数器两者组合才能得知。
结构如下图:
控制单元把两者的输出进行AND操作,来选择正确的指令执行。
例如:
LDAC的执行子例程如下:
LDAC1=ILDAC^T3
LDAC2=ILDAC^T4
LDAC3=ILDAC^T5
LDAC4=ILDAC^T6
LDAC5=ILDAC^T7
完整的状态如下表:
State
Function
State
Function
Fetch1
T0
JMPZY1
IJMPZ^T3
Fetch2
T1
JMPZY2
IJMPZ^T4
Fetch3
T2
JMPZY3
IJMPZ^T5
NOP1
INOP^T3
JMPZN1
IJMPZ^T3
LDAC1
ILDAC^T3
JMPZN2
IJMPZ^T4
LDAC2
ILDAC^T4
JPNZY1
IJPNZ^T3
LDAC3
ILDAC^T5
JPNZY2
IJPNZ^T4
LDAC4
ILDAC^T6
JPNZY3
IJPNZY^T5
LDAC5
ILDAC^T7
JPNZN1
IJPNZN^T3
STAC1
ISTAC^T3
JPNZN2
IJPNZN^T4
STAC2
ISTAC^T4
ADD1
IADD^T3
STAC3
ISTAC^T5
SUB1
ISUB^T3
STAC4
ISTAC^T6
INAC1
IINAC^T3
STAC5
ISTAC^T7
CLAC1
ICLAC^T3
MVAC1
IMVAC^T3
AND1
IAND^T3
MOVR1
IMOVR^T3
OR1
IOR^T3
JUMP1
IJUMP1^T3
XOR1
IXOR^T3
JUMP2
IJUMP^T4
NOT1
INOT^T3
JUMP3
IJUMP^T5
生成这些状态之后,我们必须要生成计数器的CLR和INC信号。
我们把每个执行子例程的最后一个状态进行OR得到了CLR信号,再把CLR信号取反得到了INC信号。
接下来我们需要产生数据路径中的所有的寄存器以及Buffer的载入信号及其他控制信号。
下表给出了数据路径中Buffer以及AR的控制信号:
Signal
Value
PCBUS
Fetch1orFetch3
DRHBUS
LDAC3orSTAC3orJUMP3orJMPZY3orJPNZY3
DRLBUS
LDAC5orSTAC5
TRBUS
LDAC3orSTAC3orJUMP3orJMPZY3orJPNZY3
RBUS
MOVR1orADD1orSUB1orAND1orOR1orXOR1
ACBUS
STAC4orMVAC1
MEMBUS
Fetch2orLDAC1orLDAC2orLDAC4orSTAC1orSTAC2orJUMP1
orJUMP2orJMPZY1orJMPZY2orJPNZY1orJPNZY2
BUSMEM
STAC5
ARLOAD
Fetch1orFetch3orLDAC3orSTAC3
ARINC
LDAC1orSTAC1orJUMP1orJMPZY1orJPNZY1
其他的寄存器的控制信号自己推导:
Signal
Value
PCLOAD
JUMP3orJMPZY3orJPNZY3
PCINC
Fetch2orLDAC1orLDAC2orSTAC1orSTAC2orJMPZN1orJMPZN2
orJPNZN1orJPNZN2
DRLOAD
Fetch2orLDAC1orLDAC2orLDAC4orSTAC1orSTAC2orSTAC4
orJUMP1orJUMP2orJMPZY1orJMPZY2orJPNZY1orJPNZY2
TRLOAD
LDAC2orSTAC2orJUMP2orJMPZY2orJPNZY2
IRLOAD
Fetch3
RLOAD
MVAC1
ACLOAD
LDAC5orMOVR1orADD1orSUB1orINAC1orCLAC1orAND1
orOR1orXOR1orNOT1
ZLOAD
ADD1orSUB1orINAC1orCLAC1orAND1orOR1orXOR1orNOT1
READ
Fetch2orLDAC1orLDAC2orLDAC4orSTAC1orSTAC2orJUMP1orJUMP2orJMPZY1orJMPZY2orJPNZY1orJPNZY2
WRITE
STAC5
最后我们生成ALU的控制信号:
ALUS1=ADD1orSUB1orINAC1
ALUS2=SUB1
ALUS3=LDAC5orMOVR1orADD1
ALUS4=SUB1orINAC1
ALUS5=XOR1orNOT1
ALUS6=OR1orNOT1
ALUS7=AND1orOR1orXOR1orNOT1
7设计验证
我们设计如下的测试指令:
0:
LDAC
1:
00000000
2:
00000011
3:
27H
4:
ADD
5:
INAC
6:
JMP
7:
00000000
8:
00001001
9:
JMPZ
10:
00000000
11:
00001100
12:
CLAC
13:
JPNZ
14:
00000000
15:
00101111
16:
AND
17:
NOT
18:
NOP
8需要改进的地方
✓更多的内部寄存器和Cache
✓多条内部总线
✓流水线
✓更大的指令集
✓子程序和中断