【计算机组成与设计 硬件/软件接口-4】处理器体系结构

本文详细介绍了MIPS处理器的数据通路设计,包括简单数据通路和流水线数据通路。针对算术逻辑、存取和分支指令,阐述了指令执行流程和控制信号的作用。同时,探讨了流水线技术,如五级指令流水线的结构、数据冒险的检测与解决、控制冒险的处理以及分支预测技术。最后,提到了指令级并行(ILP)和多发射技术在提升处理器性能上的应用。
摘要由CSDN通过智能技术生成

在这里插入图片描述

处理器

引言

在这一章节中,我们将着力搭建起包含核心MIPS子集的处理机。所谓核心MIPS子集,指的是:

  • 算逻指令:add sub and or slt
  • 存取指令:lw sw
  • 分支指令:beq j

通过前面MIPS指令的学习,我们也大概知道了上面这些指令的格式大概长啥样。

指令的执行,其实有规程可循,一般来讲,所有的指令都有如下的指令流程:

  • 从指令存储单元取出指令
  • 根据寄存器的编号从寄存器文件中读取寄存器内容
  • 再往后的操作和具体的指令有关,例如使用ALU进行运算,对内存进行读写操作,修改PC进行跳转等

对算术逻辑指令来说,一个简单的数据通路如下:
在这里插入图片描述
品味一下此图,我们要认识到几点:

  • 对算逻指令,PC正常自增,指向下一条指令的地址
  • 算逻指令不访存,这实际上体现在整个MIPS架构中,就是访存和运算是分离的,不杂糅在一起

对存取访问指令来说,一个简单的数据通路如下:
在这里插入图片描述

  • 访问存取指令根据基址寄存器加上立即数偏移,确定内存中操作数的地址,再根据读信号或写信号将相应数据load到寄存器或store到内存。同样,PC正常自增。

对分支指令来说,一个简单的数据通路如下:
在这里插入图片描述
在这里,分支条件需要经过ALU运算之后进行决断,从而决定PC是正常自增还是进行跳转。

其实,上面图仅是一个十分简单的示意,一些细节都还没有加上,例如,对于PC是自增还是跳转的操作,实际上需要一个复选器来辅助控制。
在这里插入图片描述
加上了控制信号和复选器的简单数据通路如下:
在这里插入图片描述
下面将会带大家探寻上图的诸多细节,意思就是说现在对上图存有疑惑并没关系 (: D)

数据通路

  1. 关于取指令
    在这里插入图片描述

  2. 关于运算指令
    回顾一下运算指令的格式:
    在这里插入图片描述
    在这里插入图片描述

  3. 关于存取指令
    存取指令使用基址寄存器,并从指令中取得16位立即数,这个立即数需要做符号扩展,回忆一下存取指令的格式:
    在这里插入图片描述
    在这里插入图片描述

  4. 关于分支指令
    决策分支指令需要读入两个寄存器的值进行比较,决定是否跳转到立即数指定的地址,分支的本质即由ALU对两数进行减法运算,建立标志位,由标志位决定是否进行分支。
    在这里插入图片描述

上面建立的简单数据通路,可以在一个时钟周期内就完成指令。这么一来,运算、取字和存字的指令的数据通路可以标注如下:
运算
在这里插入图片描述
取字
在这里插入图片描述

存字
在这里插入图片描述
上面图示缺少的一个重要组成部分就是控制信号,控制信号控制运算器件选择哪个来路的数据,下面展开。

控制信号

学习控制信号,我们关注的有几类重要信号,其中,ALU的控制信号比较复杂,仔细回忆一下MIPS指令,我们会发现ALU承担了许多工作——运算、存取计算、分支计算等,这些运算的操作数、运算规则存在不同,因此设计ALU的控制信号相比于其他信号要复杂得多。

ALU的用途:

  • 求地址(基址+偏移的方式)
  • 求差获得标志位(分支决策)
  • R型指令根据funct字段,进行and,add,sub,or,slt运算

归纳起来,ALU需要有如下的控制信号:
在这里插入图片描述
使用一个小的控制单元即可生成4位的ALU控制信号,其输入为指令的funct字段和2位的ALUop字段。ALUop指明要进行的操作是存取指令需要的加法(ALUop=00),beq需要的减法(ALUop=01),还是由指令的funct字段(ALUop=10)决定,列表如下:
在这里插入图片描述
这种多级译码的方法(主控制单元生成ALUop作为ALU控制单元的输入,再由ALU控制单元生成真正控制ALU的信号)是一种常用的实现方式。使用多级译码可以减小主控制单元的规模,使用多个小控制单元还可能提高控制单元的速度。 有多种不同的方法把2位的ALUop和6位的funct字段映射成4位ALU控制信号。

为了设计这个逻辑单元,有必要为ALUop和funct字段有意义地组合生成一张真值表,真值表的实际规模是2(2+6) = 256行,并不是所有的项我们都关心,只需要用到使ALU控制信号有效的部分表项即可。如下图(X表示无关项)
在这里插入图片描述
以上我们就完成了ALU控制单元的讲述,现在来看看其他的控制部分,首先我们还是要复习一下R型指令、存取指令和分支指令的指令格式,好让我们在后面将其与控制信号对应上。
在这里插入图片描述
下面细节最好记住:

  • Op字段(操作码字段),总是为31:26位
  • 对于R型指令、分支指令和存取指令,要读取的两个寄存器为rs,rt,分别为25:21位和20:16位
  • 存取指令的基址寄存器在25:21位
  • 相等则分支、存取指令的16位偏移量在15:0位
  • 有两个地方放目标寄存器!取数指令为20:16位,R型指令为15:11位。所以需要有一个多选器,指示要写的寄存器号在哪个字段中。

根据上面的分析,我们可以给数据通路加上指令标记并增加控制信号和控制元件,如下:
在这里插入图片描述
控制信号的作用如下(看图说话,非常简单):
在这里插入图片描述
贴心一点,我把它标注在图上:
在这里插入图片描述

注:除了PCSrc外,其他信号都可以在指令读入后通过操作码的译码就可以确定。

上面图实际上花得比较清晰明了,如果把主控单元加上,线看上去就多了:
在这里插入图片描述
总结归类一下,需要记住的有:

  • 7个1 bit的控制信号:3个Mux控制信号(RegDst、ALUSrc、MemToReg),3个存储读写控制信号(MemRead、MemWrite、RegWrite),1个分支控制信号(PCSrc)
  • 1个2 bit的控制信号:ALUop

对于不同的指令,需要有不同的信号进行控制,这些信号相对固定,一般如下:
在这里插入图片描述
上面讨论了指令的操作,控制单元的功能可以由上表精确定义,其输入为6位操作码31:26,输出为控制信号。这样,可以基于操作码的二进制编码为每个输出建立一张真值表。下表就是融合了op code和上图,分别代表输入和输出的对应关系:
在这里插入图片描述

流水线概述

单时钟周期实现:一个时钟周期执行一条指令的实现机制,在现实中因为慢而不实用。究其原因,是在单周期设计中,时钟周期对所有指令等长,这样时钟周期要由执行时间最长的那条指令决定。

关于流水线就不需要再举例子了,就是类似于工厂流水线那样:在同一条生产线上,有人干这活,有人干那活,都不要闲着。(996?然而是机器996无所谓→_→)计算机中的流水线通过指令间并行,来增加处理机对指令的吞吐率,以实现性能的提升。
在这里插入图片描述

五级指令流水线:

  • IF:从内存中取指令
  • ID:指令解码,同时读取寄存器
  • EX:执行运算或计算地址(反正就是和ALU相关)
  • MEM:访问内存操作
  • WB:将结果写回寄存器

这里会涉及到的一个简单计算就是计算流水线的加速比:一般来讲,在各级等长,执行指令足够多的情况下,N级流水线的加速比为N(无限逼近)。下面是其简单推导(假设N=5,x代表流水线一级花费的时间,m代表指令条数):
在这里插入图片描述
MIPS-32指令集架构有下面的优点,能够适应流水线:

  • 所有指令都等长,这样更容易在一个时钟周期内取指和译码
  • 指令格式较少,且规整
  • 访存操作仅存在于lw/sw中
  • 操作数内存地址对齐,内存访问仅需要一个周期

流水线也并不总是完美的,因为流水线会遇到冒险:结构冒险、数据冒险和控制冒险。

  • 结构冒险:硬件不支持多条指令在同一时钟周期执行。如果MIPS的流水实现部件只有一个存储器,那么在访问存储器时,PC将无法取指令,因为一个时间内只能有一个存取操作。因此,为了解决这个结构冒险,计算机中通常实用L1-Cache来解决。用CPU-Z查看一下计算机信息,可以发现L1-Cache分为了指令Cache和数据Cache。
    在这里插入图片描述
  • 数据冒险:因无法提供指令执行所需要的数据而导致指令不能在预定的时钟周期内执行。像下图,在没有其他硬件部件的情况下,s0的值只有等到第5个时钟周期(时间为800-1000)才能取得,在这个时间段之前,读取到的s0是脏数据。那么,也就是说,最快只能在时间为1000-1200才能取s0的值。
    在这里插入图片描述
    数据冒险主要通过前推/旁路解决(其他如调整指令顺序、延迟执行也能起到一定效果,但效果有限),就是增加硬件,提前将计算结果送给ALU:
    在这里插入图片描述
    上面就避免了流水线的延迟。但是,我们还要认识到,并不是所有冒险都能通过前推来避免气泡停顿,例如取数-使用型冒险:
    在这里插入图片描述
    lw指令的结果,最多只能在时间为800时拿到,如果按照前面说的前推的方式,则ALU能够取到数据的最早时间是800,这个时候,流水线不得不阻塞一个一条指令的时间。
  • 控制冒险:需要根据前面某条指令的结果来确定分支而导致的延迟。下一条指令IF阶段时,分支指令仍在 ID阶段。在MIPS流水技术中,需要尽早进行寄存器比较运算以及尽快生成目标地址,这就需要在分支指令的ID阶段加入硬件(怎么加后面讲),以支持寄存器比较、分支地址计算。这是一种保守的方法,但长的流水往往不能在第二级完成分支决策,停顿引起的性能下降太过严重。因此,更好的方法是分支预测。如果预测正确,流水线全速前进;如果错误,则撤销进入流水线的指令,重新编排正确的指令进入流水线。
    分支预测的方法有静态预测动态预测两类:静态预测方法行为比较简单,如预测恒不跳转、预测恒跳转等等,它并不根据执行时的条件和历史信息来进行预测,因此预测的准确性不会很高,理论上也就50%的正确率;动态预测方法则根据同一条转移指令过去的转移情况来预测未来的转移情况。在MIPS流水体系结构中,静态分支预测恒预测不跳转,也就是直接取下一条指令进入IF阶段。

流水线数据通路及控制信号

上一部分,我们把处理机流水线分成5级结构,分别为IF,ID,EX,MEM,WB。这五级流水线的硬件结构,其实可以借用普通数据通路的硬件结构加以改造实现。

在这里插入图片描述
之前的数据通路,在一个时刻,只有上述的一级硬件在工作;而现在换成了流水线结构,在一个时刻,各个部件都处于忙状态(硬件的007状态→_→),当然,还有一些情况如阻塞,可能会使硬件部件暂时停滞。

多时钟周期流水线图

通过多时钟周期流水线图,我们可以在时间序列上分析流水指令的执行过程。
在这里插入图片描述
请注意,上面似乎是每一条指令使用一套硬件,但实际上各条指令是分时共享这些硬件。在学习流水线的时候,我们一定要建立时间概念。例如在CC4,第一条指令处于MEM阶段,使用的是DM硬件;第二条指令处于EX阶段,使用的是ALU硬件;第三条指令处于ID阶段,使用的是ID阶段的硬件——Reg。

流水线数据通路,与普通数据通路硬件结构上一个最大的差别就是:流水线数据通路需要增加流水线寄存器,作为数据的缓存。不可以没有流水线寄存器,咱打个比方,如果a指令执行到EX阶段,已经算出结果了,而前面的一条指令的MEM阶段还没有执行完毕,这个时候如果没有寄存器,不仅a指令的计算结果可能会丢失,也有可能影响到下一条指令的执行结果。有了流水线寄存器,中间数据就可以有缓冲,有一个暂栖之地。
在这里插入图片描述
在五级流水中,插入四级流水线寄存器,分别是IF/ID,ID/EX,EX/MEM,MEM/WB寄存器。为了存储穿过它的数据,寄存器的宽度必须足够大。例如,因为IF/ID寄存器必须同时保存从存储器中提取出来的32位指令及32位PC自增地址,所以IF/ID寄存器至少要有64位;ID/EX寄存器必须保存32位PC自增地址,Read-1需要32位,Read-2需要32位,符号扩展后的立即数也可能占掉32位,所以ID/EX寄存器至少要有128位;同样的分析思路,EX/MEM寄存器需要97位,MEM/WB寄存器需要64位。总结起来,就是64+128+97+64=353位。

我们讲一下这5级的情况:

  1. 取指令(IF阶段):使用PC中的地址,从存储器中读取数据,然后将数据放入IF/ID流水线寄存器中。PC地址+4然后写回PC以便为下个时钟周期做好准备,增加后的地址同时也存入了IF/ID流水线寄存器以备后面的指令使用。
    在这里插入图片描述
  2. 指令译码与寄存器读取(ID阶段):IF/ID阶段可能会取出经符号扩展为32位的立即数和两个从寄存器中读取的数,放入ID/EX流水线寄存器。
    在这里插入图片描述
  3. 执行或地址计算(EX阶段):从ID/EX流水线寄存器中读取由寄存器1传过来的值和寄存器2传过来的值(或寄存器1传过来的值和符号扩展过后的立即数的值),并用ALU将它们相加,结果值存入EX/MEM流水线寄存器。
    在这里插入图片描述
  4. 存储器访问(MEM阶段):可能从EX/MEM流水线寄存器中得到地址读取数据寄存器,并将数据存入MEM/WB流水线寄存器。
    在这里插入图片描述
  5. 写回(WB阶段):从MEM/WB流水线寄存器中读取数据并将它写回图中部的寄存器堆中。

事实上,除了多时钟周期流水线图,还有单时钟周期流水线图。单时钟周期流水线图,说白了就是截取一列的硬件使用情况,单时钟周期流水线图描述了某一时钟周期的流水线状态。
在这里插入图片描述
在这里插入图片描述

控制信号

我们先来看下简化版的带有控制信号的流水线数据通路:
在这里插入图片描述
上面的控制信号好像十分熟悉?是的,在普通数据通路我们见过类似的控制信号(其实就是一样的→_→)由于流水线方式的数据通路并不改变控制信号的意义,因此可以使用与简单数据通路相同的控制信号。为了加深印象,我们在这里对比着看:
ALUop信号:

  • 简单数据通路
    在这里插入图片描述
  • 流水线数据通路
    在这里插入图片描述

其他信号:

  • 简单数据通路:
    在这里插入图片描述
  • 流水线数据通路:
    在这里插入图片描述

不同类型指令的控制信号:

  • 简单数据通路:
    在这里插入图片描述
  • 流水线数据通路(只是分了一下阶段):
    在这里插入图片描述

其控制信号使用阶段如下图图示:
在这里插入图片描述
画上完整的流水线数据通路便是:
在这里插入图片描述
注意在流水线寄存器中,我们同样也需要增加存储位来存储对应指令的控制信号。

数据冒险

前面已经介绍过数据冒险,数据冒险指的是:因无法提供指令执行所需要的数据而导致指令不能在预定的时钟周期内执行。也讲过解决数据冒险最通用的方式是前推,或者说旁路。这一部分需要深入学习一下数据冒险是如何检测到的、以及检测到数据冒险之后如何处理的细节。

检测是否需要启动前推

首先,判断是否发生数据冒险,很重要的前提就是要知道寄存器的编号,因此,流水线硬件需要将读写寄存器号逐级向后传递,从而知道读写寄存器的时机。

有了寄存器编号,可以通过下面的逻辑表达式来判断是否发生数据冒险,我们还给他取了一些名称:
1a:EX/MEM.RegisterRd = ID/EX.RegisterRs
1b:EX/MEM.RegisterRd = ID/EX.RegisterRt
2a:MEM/WB.RegisterRd = ID/EX.RegisterRs
2b:MEM/WB.RegisterRd = ID/EX.RegisterRt

发生了上面任意一条表达式,即说明发生了数据冒险。对于1a和1b,需要从EX/MEM流水线寄存器获取前推数据;对于2a和2b,需要从MEM/WB流水线寄存器获取前推数据。

但是,直接采用总是旁路的方式解决冒险是不正确的,因为某些指令可能不写回寄存器,会产生一些不必要的通路。一种简单的解决办法就是检测流水线寄存器在EX/MEM级的WB控制字段以确定RegWrite是否有效。而且,MIPS要求$0始终为0,这就需要在目标寄存器是$0的情况下,避免把$0按非零结果旁路,从而使得程序员不必考虑$0作为目标寄存器的情况。

旁路数据策略

通过在ALU的输入中加入多选器和正确的控制策略,就可以在存在相关性的情况下仍然全速运行流水线。因为ALU旁路多选器在EX级中,所以旁路控制也在这一级中完成。因此,我们必须通过ID/EX流水线寄存器从ID级中获取操作数寄存器号,以决定是否旁路相应的值。
在这里插入图片描述
ALU的两个操作数前面的多选器MUX控制信号定义如下:
在这里插入图片描述
综合前面和这里讲的控制信号,这样看来,检测数据冒险的逻辑表达式应该完善为:

  • EX冒险:
if (EX/MEM.RegWrite and (
	EX/MEM.RegisterRd ≠ 0) and (
	EX/MEM.RegisterRd = ID/EX.RegisterRs)) 
		ForwardA = 10

if (EX/MEM.RegWrite and (
	EX/MEM.RegisterRd ≠ 0) and (
	EX/MEM.RegisterRd = ID/EX.RegisterRt))  
		ForwardB = 10
  • MEM冒险:
if (MEM/WB.RegWrite and (
	MEM/WB.RegisterRd ≠ 0) and (
	MEM/WB.RegisterRd = ID/EX.RegisterRs)) 
		ForwardA = 01

if (MEM/WB.RegWrite and (
	MEM/WB.RegisterRd ≠ 0) and (
	MEM/WB.RegisterRd = ID/EX.RegisterRt)) 
		ForwardB = 01
双重数据冒险

MEM冒险和EX冒险也有可能同时发生,如下面的指令序列:

add $1,$1,$2
add $1,$1,$3
add $1,$1,$4

这种数据冒险较为复杂,发生在WB级的指令结果、MEM级的指令结果和ALU级的指令源操作数之间。比如上面第1条指令ALU级执行完后,$1的值发生了改变,然而第2条指令的$1的值是脏数据。在这种情况下,由于MEM级的结果是最新的,因而结果是由MEM级旁路得到。这个时候,对MEM冒险的控制策略应该调整为:

if (MEM/WB.RegWrite and (MEM/WB.RegisterRd ≠ 0) 
	and not (EX/MEM.RegWrite and (EX/MEM.RegisterRd ≠ 0) and (
	EX/MEM.RegisterRd = ID/EX.RegisterRs)) 
	and (MEM/WB.RegisterRd = ID/EX.RegisterRs))
		ForwardA = 01

if (MEM/WB.RegWrite and (MEM/WB.RegisterRd ≠ 0)
	and not (EX/MEM.RegWrite and (EX/MEM.RegisterRd ≠ 0) and (
	EX/MEM.RegisterRd = ID/EX.RegisterRt)) 
	and (MEM/WB.RegisterRd = ID/EX.RegisterRt))
		ForwardB = 01

为了支持旁路EX级结果所增加的必要的硬件设备:
在这里插入图片描述

取数-使用型数据冒险

在这里插入图片描述

上面必须阻塞一个时间周期,才能得到正确的从数据存储器中load的结果。在指令译码ID阶段进行检测寄存器的使用,判定条件为:
在这里插入图片描述
发现此类型的冒险后,需要阻塞插入气泡,然后等待旁路电路处理,否则还需一个阻塞。这里所谓的阻塞,在流水线上的实现就是将ID/EX寄存器控制信号全为0,EX, MEM 和 WB 执行空操作 nop。

带有阻塞功能的流水线:
在这里插入图片描述

控制冒险

分支决策的结果最快可以在EX级拿到,但是这可能导致至少两条指令被撤销。通过引入硬件,我们可以让分支决策提前到ID级。
在这里插入图片描述
上图引入了加法器(进行目标地址计算)和比较器(两个寄存器的值比较)。

动态分支预测

为了降低控制冒险所带来的性能损失,一般采用分支预测技术。分支预测技术包含编译时进行的静态分支预测,和执行时进行的动态分支预测。这里,我们着重介绍动态分支预测中的BTB(Branch Target Buffer)技术。
BTB即为分支目标缓冲器,它将分支指令(对应的指令地址)放到一个缓冲区中保存起来,当下次再遇到相同的指令(跳转判定)时,它将执行和上次一样的跳转(分支或不分支)预测。动态分支技术可以用下面图示概括:
在这里插入图片描述
在这里插入图片描述

延迟槽技术

延迟槽技术,名字乍一看上去很高大上,但实际上就是在分支计算和分支跳转之间插入一些无关指令,这些无关指令可以是前面的指令、后续指令或者目标处的指令。

流水线全图

流水线全图是在非流水线图上增加流水线寄存器、冒险电路、旁路电路和分支预测撤销电路。
在这里插入图片描述

指令级并行 (ILP)(了解)

流水线挖掘了指令间潜在的并行性,有两种方法可以增加潜在的指令集并行程度,第一种是增加流水线的深度以重叠更多的指令,另一种方法是复制计算机内部部件的数量,使得每个流水级可以启动多条指令,这种技术一般称为多发射

静态多发射

实现多发射处理器的一种方法,其中决策是在执行前的编译阶段做出的。在一个静态发射处理器种,可以在给定时钟周期内发射多条指令,也称为发射包。发射包可被视为一条完成多个操作的长指令(超长指令字)

动态多发射

实现多发射处理器的一种方法,其中决策是由处理器在执行阶段做出的。动态多发射处理器通常称为超标量处理器,或简称超标量。

  • 13
    点赞
  • 87
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值