一、16位流水线CPU结构
概括:本文介绍一个16位的流水线CPU设计与模拟过程,该流水线CPU由IF、ID、EXE、MEM和WB五个功能段组成,结构如下图所示。在各功能段之间分别设计了四个锁存段,即IF_LATCH、ID_LATCH、EXE_LATCH和 WB_LATCH。各基本模块使用VHDL硬件描述语言进行描述,各段的大部分功能模块本身都采用组合逻辑实现,而流水线的时序控制主要通过各段之间的锁存器实现,流水线中的寄存器文件、指令存储器、数据存储器等功能部件则采用时序逻辑实现。
流水线CPU结构
五个流水段的功能分别为:
1、取指令IF;
2、指令译码和读寄存器文件ID;
3、执行或计算地址EXE;
4、存储器访问MEM;
5、写回WB。
每两段之间设计一个流水线寄存器。为了能够存储通过流水线寄存器的数据,流水线寄存器的位数必须足够宽。例如,IF/ID寄存器要存储从ROM 中取出的16位的指令和16位的下一个取指令地址PC+1,寄存器宽度必须达到32位。而ID/EXE寄存器要存储从RegFile取出的两个16位操作数、16位的分支转移地址、经过符号扩展后的16位操作数和一些其他信息,它的数据宽度要超过64位。
在正常情况下,流水线中的数据是按照顺序流动的,但是当程序出现分支转移,或者CPU响应外部设备的中断请求时,这种顺序就会被打乱。当流水线中有多条指令要同时对同一资源进行读/写操作时,就会出现对资源的争用,发生数据冲突或资源冲突。为了保证流水线CPU能顺利工作,在进行CPU整体设计时还考虑了冲突检测单元和相关处理单元的设计,以及中断响应单元的设计。
控制器是流水线CPU的重要组成部分,控制器被安排在指令译码段。控制器对从存储器中取出的指令进行译码,然后向各段发出操作执行命令,当流水线发生冲突时,也由控制器来协调各部分的工作。
二、指令系统设计
本实验采用的指令集主要有四种形式,即R型、RI型、I型、S型。下图给出了所采用的指令及其功能。
1、寄存器型(R型)
R型是指令中最常见的类型。R型指令中包含一个4位的操作码(opcode),第一个3位寄存器(R1)是写寄存器,后面两个3位寄存器(R2,R3)是读寄存器,最后3位被用作功能位(func)。操作码用来确定哪一种类型的指令将被执行,功能位指出将要执行的是哪种实际的指令。也就是说,单一一个R型操作码最多可以有8条(2的3次方)不同的指令。
2、寄存器立即数型(RI型)
RI-型指令 ADDI R1,R2,0FH
RI型与R型类似,不同点在于将R型中的第二个读寄存器和最后的3位功能位用一个6位立即数代替(0FH)。每一个RI型操作码只有一条指令,因为没有像R型指令那样的功能位。
3、立即数型(I型)
I型指令用于两条移动立即数指令和两条PC相对转移指令。
I-型指令 MVIL R1,FFH
I型指令中包含一个4位的操作码(opcode),一个3位的寄存器值、一个8位的立即数域8-bit Immediate和一个1位的功能位。功能位允许对每一个I型操作码赋值。图中的指令为 MVIL R1,FFH。该指令的指令代码w为0100 001 1111 1111 0,opcode=0100,表示MVI指令,其中R1=001,8-bit Im=1111 1111,功能位=0,表示对寄存器的低8位赋值,即指令为MVIL。同理,若功能位=1,表示对寄存器的高8位赋值,即指令为MVIH。
4、立即移位型 (SI-型)
SI型指令用于移位指令。
SI-型指令 RORI R1,R2,2H
SI型指令包含一个4位的操作码(opcode),一个5位的立即数域5-bit Im和一个1位的功能位。5位的立即数域用于将源寄存器按照所期望的方向,从-15到+16的位置移位。如图,是一个SI-型指令 RORI R1,R2,2H,指令代码为 1000 001 010 00010 1,其中opcode=1000,R1=001,R2=010,5-bit Im=00010,fun=1。十六进制表示为8285H。
三、数据通路设计
完成指令集设计后,就可以开始设计数据在处理器中流动的数据通路了。把所有指令的数据通路组合起来就形成了处理器的总的数据通路。最常用的数据通路的形式有:R型数据通路、RI型数据通路、装入字数据通路、存储字数据通路和寄存器转移数据通路。
1、R-型数据通路
![](https://i-blog.csdnimg.cn/blog_migrate/65e750289347224415eb2a945828b565.png)
在R型数据通路中,指令从存储器(Memory)取出被分解成为一个4位的操作码(opcode),一个3位寄存器--写寄存器,两个3位寄存器--读寄存器,3位功能位(func)。两个操作数Reg1和Reg2,是来自于指令中指定的寄存器(两个读寄存器),从寄存器文件取出数据。ALU执行由指令指定的操作,然后将ALU输出的结果回写到寄存器文件(RegFile)中。
2、RI-型数据通路
RI-型 ALU 指令的数据通路
RI型数据通路如图所示。RI型数据通路与R型数据通路类似,只是第二个读寄存器(R型指令数据通路中的Reg2)被一个包含在指令中的数值所代替。这个立即数是一个由6位(取指时是6位)扩展成16位的有符号数,并作为第2操作数送入 ALU。ALU执行由指令指定的操作,并将执行结果回写到寄存器文件中(RegFile)。
3、装入字数据通路
装入字数据通路
装入字数据通路与RI型数据通路类似,区别在于从ALU出来的执行结果不是写入寄存器而是作为访问存储器的地址。该指令从存储器中取出一个数值,然后将这个数值装入到寄存器文件中。
4、存储字数据通路
存储指令的数据通路
存储字数据通路类似于装入字数据通路,区别在于写寄存器指向要写入的存储器的地址而不是寄存器文件。存储字数据通路如上图所示。
5、寄存器转移数据通路
寄存器转移指令数据通路
寄存器转移数据通路如上图所示。在寄存器转移数据通路中,一个寄存器与零比较。如果转移类型为零转移并且寄存器的内容为零,则将第二个寄存器(Reg2)的内容装入程序计数器(PC)。如果寄存器的内容不为零,则装入下一条程序计数器的值(PC+1的值),执行的指令继续流动。不为零转移指令也与此类似。
四、流水线各段设计和功能描述
流水线CPU由IF、ID、EXE、MEM和WB五个功能段组成。下面对各段的功能、模块划分和接口信号进行简要介绍。
1、Stage1 取指令段IF
IF Stage 1的结构
取指令IF段的结构如上图所示。IF段执行从存储器ROM中取指令的操作,并将已取出的指令机器码与程序计数器(PC)的输出值存储在IF/ID流水线寄存器中,作为临时保存,以便在下一个时钟周期开的时候为下一步所用。
下面对取指令IF段的功能和各组成模块的划分及实现进行具体的分析。
1)功能描述
取指令IF段的功能主要有以下几个:
① 取指令及锁存。根据程序计数器(PC)的值从指令存储器(ROM)中取出指令,并将取出的指令送至本段的锁存单元IF/ID流水线寄存器锁存。
② 地址计算。根据pcselector选择信号sel[3..0]的值,从incPC、 branchPC、retiPC、和retPC四个地址转移源中选择程序计数器PC的下一个值。
若流水线中写回(WB)段的指令是跳转指令或者分支成功的分支指令,则选择branchPC的值,以程序跳转的目标地址作为地址计算的结果;
若是非转移指令或者分支失败的分支指令,则PC取PC+1的值(incPC),指向指令存储器中的下一条指令;
若是中断返回指令,则取retiPC的值;
若是子程序返回指令,则取retPC的值。
③ 检验指令的合法性。检验指令的操作码和功能码是否符合指令集设计中的定义,如果指令不正确,则返回一个异常。
④ 同步控制。用时钟clk 对外部信号进行同步。
2)模块划分
Stage 1由五个基本模块组成,即incPC、lpm_rom0、progc、pcselector和 ifid。incPC模块完成PC+1的操作,即完成程序计数器(PC)自动加1,指向当前指令之后的下一条指令的存储地址; lpm_rom0是程序存储器模块,用于存放应用程序;progc是程序计数器模块;pcselector 是下一条指令地址选择控制单元:;ifid 是Stage 1的流水线段锁存器。
各模块之间的信号连接关系如下图所示,此图是基于Quartus 的电路原理图。
Stage 1取指令段的各模块信号连接电路
pc值传到lpm_rom0,有个clk,输出pc中的[5..0]作为地址对应rom的值--------->opr(仿真图有),也作为ins,也作为pre_1:inst2的writedata输入。
ifid有两个控制信号ifid_enable,id_flush,
(ifid_enable<------hazard id_flush <--------cpu_controller)
1 ,0 (高电平,低电平):流水线正常工作,无数据相关,clk👆,insOut=insIn ,pcOut=pcIn;
1 ,1(高电平,高电平):发生数据相关,clk👆,insOut=0000H,空操作指令,pcOut=pcIn;
冲突过后,恢复正常状态。1 ,0 (高电平,低电平),clk👆,insOut=insIn ,pcOut不变;
0,0(低电平,低电平):流水线stop工作,insOut不变 ,pcOut不变。
三个输出insOut,pcout,pcValue的去向作用不明
2、Stage2 译码段ID
stage 2指令译码段ID的基本结构
指令译码ID段的结构如上图所示。ID段要完成的主要功能有,从程序存储器(PC)取出的指令被送到控制单元;控制单元对指令进行译码,将译码后产生的各种控制命令送到处理器的各个部件;指令中的读寄存器命令从寄存器文件 RegFile中取出数据;分支控制模块 Branch Unit进行分支转移判断。
下面对指令译码ID段的功能和各组成模块划分及实现做具体的分析。
1)功能描述
指令的执行进入译码ID段之后,从取指令IF段获得了当前指令ins[15..0]。ID段对指令进行分析,为执行EXE 段准备立即数操作数和寄存器操作数。立即数操作数的产生是根据译码结果,对指令中的8位和6位立即数字段进行符号位扩展,得到16位的立即数操作数。寄存器操作数的产生是根据译码结果,从寄存器文件中读取相应的16位操作数。
(指令的译码操作和读寄存器操作是并行进行的。因为指令格式中操作码的位置是固定的,这就为并行提供了可能。值得注意的是,在上述过程中,可能读出了一些在后面周期中并不会使用到的寄存器内容,但是这并不会影响指令执行的正确性。相反,却可以降低问题的复杂度。立即值的提前形成与此类似。)
ID段的第二个功能是监测流水线中的指令序列之间对寄存器文件操作的数据相关性,以及指令之间存在的结构相关性,并产生流水线控制信号pipeline flush和 control flush。
[当流水线中的指令序列之间存在先写后读数据相关的时候,产生信号pipelines flush,并送到流水线其他各段的锁存段,暂停流水线中指令序列的流动,直到产生写后读相关的指令执行完毕,才把结果写回寄存器文件中的相应寄存器。
当检测到IF段送出的指令是跳转或分支指令时,则置位流水线控制信号control flush,并进行分支目标地址计算和分支成功与否的判断。
目前设计的ID段仅负责对指令中的操作数部分进行译码,对于指令中的操作码和功能码的译码,则放在后面的执行过程中进行。]
本段的主要功能归纳如下:
(1)访问寄存器文件。从寄存器文件中读取寄存器操作数并送往ID latch 段。
(2)向寄存器文件回写数据。把流水线中已经执行完毕的指令所需要回写寄存器文件的执行结果,写入到寄存器文件中。
(3)符号位扩展。对指令中的8位或6位立即数操作数进行零扩展或者符号位扩展,把扩展后得到的16位操作数送往ID latch 段。
(4)相关性检测。对正在ID段进行处理的指令和流水线中EXE、MEM、WB段中的指令进行数据相关检测和控制相关检测。如果检测到数据相关性,就对流水线各段发出pipeline flush信号;如果检测到分支指令,就对流水线各段发出control flush信号。
2)模块划分
Stage 2段的各基本模块采用VHDL描述。
本段主要由四个模块组成: branch、regFile、signext 和 idex,各模块之间的信号连接图如下图所示。
Stage 2 译码段ID的各模块信号连接电路
本段的主要输入信号有:指令pc[15..0]、instruction[15..0]、寄存器选择信号regselect[1..0]、写回数据Rf_WriteData[15..0]、分支转移信号Bu_register[15..0]、分支选择信号bsel、时钟信号clk和用于数据相关检测的从前面三个流水段送回的信号组。
主要输出信号有:从寄存器取出的两个操作数regOneOut[15..0]和regTwoOut[15..0]、写回寄存器文件的地址 writebackOut[2..0]、经过扩展以后的立即数操作数immed16Out[15..0]、immed8Out[7..0]、分支转移地址 Bu_PC[15..0]、通用寄存器读出地址readregOne[2..0]、readregTwo[2..0]和readregsters[5..0]。
Stage 2段各模块的端口和功能描述如下。
(1)符号扩展模块。其中,输入端口: immed6In[5..0],immed8In[7..0];输出端口: immed6Out[15..0],imme86Out[ 15..0],immed5Out[15..0]。该模块的功能是判断指令功能码数据部分immed6In[5..0]和immed8In[7..0],对I-型指令中的立即数操作数进行零扩展或者符号位扩展,生成立即数操作数。
第一种情况
IF immed6In(5) = ‘0’ THEN immed6Out=10个0+immed6In[5..0]
ELSE immed6Out=10个1+immed6In[5..0]
第二种情况
IF immed8In(7) = ‘0’ THEN immed8Out=8个0+immed8In[7..0]
ELSE immed8Out=8个1+immed8In[7..0]
第三种情况
IF immed8In(4) = ‘0’ THEN immed5Out=11个0+immed8In[5..1]
ELSE immed5Out=11个1+immed8In[5..1]
(2)寄存器文件模块。寄存器文件模块regFile的功能是根据指令操作码,按读出寄存器的要求,从寄存器文件中读出数据,并将写回数据写入寄存器文件。
在寄存器文件模块regFile中包含8个16位的寄存器,用3位二进制信号作为寄存器的选择信号(即寄存器名)。在向寄存器写入数据时,写入的16位数据WritData[15..0],根据写入寄存器WriteReg[2..0]的3位二进制编号,写入到相应的寄存器中。但寄存器Reg(0)中存储的数据始终为0。
regFile的时序仿真波形图如下图所示,图中给出了regFile 的几种工作情况:
① 读寄存器。从寄存器读出数据,分为两种情况:
- 当WriteEnable为高电平时,即写允许有效。
- 若WriteReg[2..0]=RegOne[2..0],则回写数据WritData[15..0]写入寄存器RegOne,并且ReadOne[15..0]输出与WriteData[15..0]相同的数据;
- 若WriteReg[2..0] =RegTwo[2..0],则回写数据WritData[15..0]写入寄存器RegTwo,并且 ReadTwo[15..0]输出与WriteData[ 15..0]相同的数据;
- 若 WriteReg[2..0]与RegOne[2..0]和RegTwo[2..0]均不相同时,则WritData[15..0]不能改写寄存器文件中的数据。ReadOne[15..0]输出与 RegOne[2..0]对应的寄存器的内容,ReadTwo[15..0]输出与RegTwo[2..0]对应的寄存器的内容。
- 当WriteEnable为低电平时,即写允许无效。ReadOne[15..0]输出与RegOne[2..o]对应的寄存器的内容,ReadTwo[15..0]输出与RegTwo[2..0]对应的寄存器的内容。
②写寄存器。向寄存器写入数据,也分为两种情况:
- 写入信号有效。当WriteEnable为高电平,WriteReg = RegOne时,数据WritData写入与 RegOne 相对应的寄存器,同时 ReadOne 输出数据与WriteData输入数据一致;而ReadTwo 则输出与RegTwo编号所对应的寄存器的内容。
- 注意:REG(0)中的数据始终为0000H,不能被改写。
- 写信号无效。当 WriteEnable 为低电平时,输入数据 WriteDat不能写入寄存器;ReadOne和 ReadTwo分别输出与RegOne和RegTwo所对应寄存器中原来的内容。
regFile的时序仿真图
(3)分支控制模块。分支控制模块的功能是根据分支选择信号bsel,输出转移地址newpc[15..0]。地址输出端newPC根据bsel的状态,选择输出来自寄存器reg 的地址或是来自 pc+displacement构成的转移地址。当bsel=0时,newPC[15..0]=reg[15..0],转移地址来自寄存器;当bsel=1时,newPC[15..0]=pc[15..0]+ displacement[15..0],通过计算后获得转移地址,通常用于程序的相对转移。
(4)ID/EX段的流水线寄存器。模块功能是Stage 2段的信号锁存。
indexpc<---idexpcIn[15..0] readRegisters<---readOneIn[2..0]+readTwoIn[2..0]
shiftImmOut<---shiftImm[15..0] regOneOut<---regOne[15..0] regTwoOut<---regTwo[15..0]
immed16Out<----immed16In[15..0] immed8Out<----immed8In [7..0]
writebackOut<---writeback[2..0]
从程序存储器(PC)取出的pcValue被送到控制单元,指令。
指令的执行进入译码ID段之后,从取指令IF段的insOut获得了当前指令ins[15..0]。
Satge2输入去向:
insOut(Satge1)--->instrument[15..0](Satge2 ),pcValue(Satge1)--->pc[15..0](Satge2 )
instrument[15..0](Satge2)--->immed8In[7..0] + writeback[11..9](ID/EX段的流水线寄存器Stage2)
pc[15..0](Satge2 )--->pc[15..0](branch_S2) / idexpcIn(ID/EX段的流水线寄存器Stage2)
Bu_register(Satge2 )-->reg[15..0](branch的输入)(newPc有可能=reg[15..0],看bsel为1还是0)
Rf_WriteData[15..0],WriteEnable,WriteReg[2..0]
bsel是cpu_controller里的buselect控制
regselect是cpu_controller里的regselect控制
regOneOut[15..0](Satge2输出 )--->retpc[15..0](Stage1输入)
BU_PC[15..0](Satge2输出 )--->branchPc[15..0](Stage1输入)
符号扩展模块immediate进行判断和输出,对I-型指令中的立即数操作数进行零扩展或者符号位扩展,生成立即数操作数。--->分支控制模块。当bsel=0时,newPC[15..0]=reg[15..0],转移地址来自寄存器;当bsel=1时,newPC[15..0]=pc[15..0]+ displacement[15..0],通过计算后获得转移地址,通常用于程序的相对转移。
控制单元对指令进行译码,将译码后产生的各种控制命令送到处理器的各个部件;指令中的读寄存器命令从寄存器文件 RegFile中取出数据;分支控制模块 Branch Unit进行分支转移判断。
3、Stage 3执行有效地址计算段(EXE)
Stage 3执行EXE段进行各种计算,其基本结构如下图所示。执行段的主要部件是ALU。ALU由算术逻辑单元、移位寄存器、寄存器数据输入模块组成。执行段的其他部件还包括:EX/MEM段流水线寄存器和总线多路数据选择器。
stage 3执行EXE段的基本结构
下面对执行段EXE的功能和各组成模块划分及实现做具体的分析。
1)功能描述
在前一个周期Stage 2已经准备好了指令要处理的操作数之后,就开始执行有效地址计算。本段主要的功能单元为算术逻辑运算单元 ALU。根据对指令操作码部分的译码结果,ALU对两个16位的操作数完成算术、逻辑、移位或置位的运算,然后输出结果。根据指令类型的不同,可以将该周期的操作分为以下几种类型:
(1)存储器访问指令(LOAD/STORE):Rl -- (R2 + imm)。
当指令为存储器访问指令的时候,该周期的操作是:ALU将操作数相加形成有效地址,并将结果放入寄存器R1中。
(2)R型ALU操作:R1 -- R2 op R3。
当指令为R型ALU操作指令时,该周期的操作是:ALU根据操作码指出的功能对寄存器R2和R3的值进行处理,并将结果送入寄存器R1中。
(3)R-I型ALU操作:R1—R2 op imm。
当指令为R-I型ALU指令时,该周期的操作是:ALU根据操作码指出的功能对寄存器R2和 imm的值进行处理,并将结果送入寄存器R1中。
(4)分支操作:Branch condition - R1 op 0。
当指令为分支指令BZ或BNZ时,该周期的操作为:对R1的值进行检测,若R1=0或Rl<>0,则转移到R2所指向的目标地址;若不满足条件,则顺序执行后续指令。
(5)跳转操作: Branch conditione-- O。
指令JAL是无条件转移。该周期的操作是:直接将分支成功标志 branch condition置位。由于该指令给出的是绝对偏移量,故上一个周期准备的立即数操作数就是跳转的目标地址。
执行EXE段进行了分支转移成功与否的判断,得到分支转移成功标志branchcondition,并送到F段,用作程序计数器选择多路器的输入npc_sel。同时,本段提供了定向路径到F段,把 ALU_result的值直接回送到多路数据选择器NPC的输入端,这条定向路径可以使分支指令的执行节拍数从4拍减少到3拍,从而消除部分控制相关。
如果pipeline_ stall信号被置位,则取指段F停顿,译码段ID输出空指令,执行段EXE正常执行,访存段MEM 正常执行,回写段WB正常执行。
2)模块划分
运算器ALU_1与流水线寄存器EX/MEM的连接如下图所示。本段信号输入输出的时序控制由ID段锁存器ID/EXE和EXE段锁存器EXE/MEM完成。
Stage 3 EXE执行段的各模块信号连接电路
本段输入的主要数据信号有:由寄存器取出的两个操作数RegA[15..0]和RegB15..0]、由译码段形成的立即数操作数A_Immediat[15..0]、S_Immediat[15..0]、M_Immediat[7..0]和用作分支指令目标地址形成的下一个PC值idex_pc[15..0]。
输入的控制信号和其他信号有:运算数据选择信号Alu_Sel、输出结果选择信号res_sel、运算功能选择信号Func[3..0]、回写寄存器Wb[2..0]、分支转移地址选择信号Jal_control和时钟信号clk。
主要输出信号是:ALU运算单元的运算结果resultOut[15..0]和流水段寄存器的输出。本段主要由exmem、BUSMUX和ALU_1三个模块组成,它们之间的信号连接关系如图8.21所示。本段各主要模块的端口和功能描述如下。
(1)EX/MEM段流水线寄存器。EX/MEM段流水线寄存器exmem的 Verilog 描述如例8.9所示。模块功能是作为执行段Execution/存储段Memory的流水线寄存器。在时钟上升沿将EXE段的值写入EX/MEM流水线寄存器。
参考教材:《现代计算机组成原理》潘松 潘明著
博客内容参考教材第八章