• 学习《深入理解计算机系统》第四章摘要


                                                                                               第4章 处理器体系结构

    4.1 Y86指令集体系结构

           1. Y86中有8个程序寄存器:%eax、%ecx、%edx、%ebx、%esi、%edi、%esp和%ebp。处理器的每个程序寄存器存储一个字。寄存器%esp被入栈、出栈、调用和返回指令作为栈指针。在其他情况中,寄存器没有固定的含义或固定值。有3个一位的条件码:ZF、SF和OF,它们保存最近的算术或逻辑指令所造成影响的有关信息。程序计数器(PC)存放当前正在执行指令的地址。

            2. 存储器,从概念上来说就是一个很大的字节数组,保存着程序和数据。Y86程序用虚拟地址来引用存储器位置。硬件和操作系统软件联合起来将虚拟地址翻译成实际或物理地址,指明数据实际保存在存储器中哪个地方。

            3. 程序状态的最后一个部分是状态码Stat,它表明程序执行的总体状态,它会指示是正常运行,还是出现了某种异常。

            4. Y86指令的特点:

    •    IA32的MOV指令分成了4个不同的指令:irmovl、rrmovl、mrmovl和rmmovl,分别显式地指明源和目的的格式。源可以是立即数(i)、寄存器(r)或存储器(m),指令名字的第一个字母就表明了源的类型,目的可以是寄存器(r)或存储器(m);指令名字的第二个字母指明了目的类型。在决定如何实现数据传送时,显式地指明数据传送的这4种类型是很有帮助的。

                两个存储器传送指令中的存储器引用方式是简单的基址和偏移量形式。在地址计算中,不支持第二变址寄存器和任何寄存器值的伸缩。

                同IA32一样,不允许从一个存储器地址直接传送到另一个存储器地址。另外,也不允许将立即数传送到存储器。

    •    有4个整数操作指令:addl、subl、andl和xorl。它们只对寄存器数据进行操作,而IA32还允许对存储器数据进行这些操作,这些指令会设置3个条件码ZF、SF和OF(零、符号和溢出)。
    •     7个跳转指令:jmp、jle、jl、je、jne、jge和jg。根据分支指令的类型和条件码的设置来选择分支,分支条件同IA32。
    •     6个条件传送指令:cmovle、cmovl、cmove、cmovne、cmovge和cmovg。这些指令的格式与寄存器-寄存器传送指令一样,但是只有当条件码满足所需要的约束时,才会更新目的寄存器的值。
    •     call指令将返回地址入栈,然后跳到目的地址。ret指令从这样的过程调用中返回。
    •     pushl和popl指令实现了入栈和出栈。
    •     halt指令停止指令的执行,IA32中有一个与之相当的指令halt。IA32的应用程序不允许使用这条指令,因为他会导致整个系统暂停运行。对于Y86来说,执行halt指令会导致处理器停止,并将状态码设置为HLT。

            5. 每条指令需要1~6个字节不等,这取决于需要哪些字段,每条指令的第一个字节表明指令的类型。这个字节分为两个部分,每部分4位:高4位是代码部分,低4位是功能部分,代码值为0~0xB,功能值只有在一组相关指令共有一个代码时才有用。

                      

                  有的指令只有一个字节长,而有的需要操作数的指令编码就更长一些。首先,可能有附加的寄存器指示符字节,指定一个或两个寄存器,这些寄存器字段为rA和rB,从指令的汇编代码表示中可以看到,根据指令类型,指令可以指定用于数据源和目的寄存器,或是用于地址计算的基址寄存器。没有寄存器操作数的指令,例如分支指令和call指令,就没有寄存器指示符字节。那些只需要一个寄存器操作数的指令(irmovl、pushl和popl)将另一个寄存器指示符设为0xF。

                有些指令需要一个附加的4字节常数字,这个字能作为irmovl的立即数数据,rmmovl和mrmovl的地址指示符的偏移量,以及分支指令和调用指令的目的地址。注意,分支指令和调用指令的目的地址是一个绝对地址。处理器使用PC相对寻址方式,分支指令的编码会更简洁,同时这样也能允许代码从存储器的一部分复制到另一部分,而不需要更新所有的分支目标地址。因为我们更关心描述的简单性,所以就使用了绝对寻址方式,同IA32一样,所有整数采用小端法编码,当指令按照反汇编格式书写时,这些字节就以相反的顺序出现。

             6.  程序寄存器存在CPU中的一个寄存器文件,这个寄存器文件就是一个小的、以寄存器ID作为地址的随机访问存储器。在指令编码中以及在我们的硬件设计中,当需要指明不应访问任何寄存器时,就用ID值0xF来表示。

             7. 对Y86来说,程序员可见的状态码Stat,它描述程序执行的总体状态。代码1,命名为AOK,表示程序执行正常,而其他一些代码则表示发生了某些类型的异常;代码2,命名为HLT,表示处理器执行了一条halt指令;代码3,命名为ADR,表示处理器试图从一个非法存储器地址读或者向一个非法存储器地址写,可能是当取指令的时候,也可能是当读或者写数据的时候,我们会限制最大的地址,任何访问超出这个限定值的地址都会引发ADR异常;代码4,命名为INS,表示遇到了非法的指令代码。

             8. pushl指令会把栈指针减4,并且将一个寄存器值写入存储器中,因此,当执行pushl %esp指令时,处理器的行为是不确定的,因为要入栈的寄存器会被同一条指令修改,通常有两种约定,可以压入%esp的原始值,也可以减去4的%esp的值;对popl指令也类似,可以将%esp置为从存储器中读出的值,也可以置为加上4后的栈指针。

     4.2 逻辑设计与硬件控制语言HCL

            1. 要实现一个数字系统需要三个主要的组成部分:计算对位进行操作的函数的组合逻辑、存储位的存储器元素,以及控制存储器元素更新的时钟信号。

             2. 将很多的逻辑门组合成一个网,就能构建计算块,称为组合电路。构建这些网有两条限制:

    •     两个或多个逻辑门的输出不能连接在一起。否则它们可能会使线上的信号矛盾,可能会导致一个不合法的电压或电路故障。
    •     这个网必须是无环的。也就是在网中不能有路径经过一系列的门形成一个回路,这样的回路会导致该网络计算的函数有歧义。

             3. 组合电路从本质上讲,不存储任何信息,它们只是简单地响应输入信号,产生等于输入的某个函数的输出。存储设备都是由同一个时钟控制,时钟是一个周期性信号,决定什么时候要把新值加载到设备中。两类存储设备:

    •     时钟寄存器(简称寄存器),存储单个位或者字。时钟信号控制寄存器加载输入值。
    •     随机访问存储器(简称存储器),存储多个字,用地址来选择该读或者写那个字。随机访问存储器包括:处理器的虚拟存储器系统,硬件和操作系统软件结合起来使处理器可以在一个很大的地址空间内访问任何的字;寄存器文件,在此,寄存器标识符作为地址。在IA32或Y86处理器中,寄存器文件有8个程序寄存器(%eax、%ecx等)。

             4. Y86处理器会用时钟寄存器保存程序计数器(PC)、条件代码(CC)和程序状态(Stat)。

             5. 寄存器文件:

                            

                    寄存器文件有两个读端口(A和B),还有一个写端口(W),这样一个多端口随机访问存储器允许同时进行多个读和写操作。图中所示的寄存器文件中,电路可以读两个程序寄存器的值,同时更新第三个寄存器的状态,每个端口都有一个地址输入,表明该选择哪个程序寄存器,另外,还有一个数据输出或对应该程序寄存器的输入值。

              6. 随机访问存储器:

                           

                    这个存储器有一个地址输入,一个写的数据输入,以及一个读的数据输出。同寄存器文件一样,从存储器中读的操作方式类似于组合逻辑:如果我们在输入address上提供一个地址,并将write控制信号设置为0,那么经过一些延迟之后,存储在那个地址上的值会出现在输出data上,如果地址超出了范围,error信号会设置为1,否则就设置为0。写存储器是由时钟控制的:我们将address设置为期望的地址,将data in设置为期望的值,而write设置为1。

     4.3 Y86的顺序实现

              1. 将处理组织成阶段:  

    •      取值:取值阶段从存储器读取指令字节,地址为程序计数器(PC)的值。从指令中抽取出指令指示符字节的两个四位部分,称为icode(指令代码)和ifun(指令功能)。它可能取出一个寄存器指示符字节,指明一个或两个寄存器操作数指示符rA和rB。它还可能取出一个四字节常数字valC。它按顺序方式计算当前指令的下一条指令的地址valP。也就是说,valP等于PC的值加上已取出指令的长度。
    •      译码:译码阶段从寄存器文件读入最多两个操作数,得到值valA和valB。通常,它读入指令rA和rB字段指明的寄存器,不过有些指令是读寄存器%esp的。
    •      执行:在执行阶段,算数/逻辑阶段(ALU)要么执行指令指明的操作(根据ifun的值),计算存储器引用的有效地址,要么增加或减少栈指针。得到的值即为valE,在此,也可能设置条件码。对一条跳转指令来说,这个阶段会检验条件码和(ifun给出的)分支条件,看是不是应该选择分支。
    •      访存:访存阶段可以将数据写入存储器,或者从存储器读出数据,读出的值为valM。
    •      写回:写回阶段最多可以写两个结果到寄存器文件。
    •      更新PC:将PC设置成下一条指令的地址。

              2. SEQ的实现包括组合逻辑和两种存储器设备:时钟寄存器(程序计数器和条件码寄存器),随机访问存储器(寄存器文件、指令存储器和数据存储器)。其中,程序寄存器、条件码寄存器、数据寄存器和寄存器文件,这些单元通过一个时钟信号来控制,它触发将新值装载到寄存器以及将值写到随机访问存储器。每个时钟周期,程序寄存器都会装载新的指令地址;只有在执行整数运算指令时,才会装载条件码寄存器;只有在执行rmmovl、pushl或call指令时,才会写数据存储器;寄存器文件的两个写端口允许每个时钟周期更新两个程序寄存器,不过我们可以用特殊的寄存器ID 0xF作为端口地址,来表明在此端口不应该执行写操作。

              3. nop指令只是简单地经过各个阶段,除了要将PC加1,不进行任何处理。halt指令使得处理器状态被设置为HLT,导致处理器停止运行。

     4.4 流水线的通用原理   

              1. 从头到尾执行一条指令所需的时间称为延迟。

              2. 减缓时钟不会影响流水线的行为。信号传播到流水线寄存器的输入,但是直到时钟上升时才会改变寄存器的状态;另一方面,如果时钟运行地太快,就会有灾难性的后果,值可能会来不及通过组合逻辑,并且当时钟上升时,寄存器的输入还不是合法的值。

              3. 流水线的局限性:不一致的划分(运行时钟的速率由最慢阶段的延迟限制)、流水线过深收益反而下降(通过流水线寄存器的延迟成了流水线吞吐量的一个制约因素)。    

     4.5 Y86流水线的实现 

                1. 在SEQ中,PC计算发生在时钟周期结束的时候,根据当前时钟周期内计算出的信号值来计算PC寄存器的值。在SEQ+中,我们创建状态寄存器来保存在一条指令执行过程中计算出来的信号,然后,当一个新的时钟周期开始时,这些信号值通过同样的逻辑来计算当前指令的PC。我们将这些寄存器标号为“plcode”、“pCnd”等等,来指明在任一给定的周期,它们保存的是前一个周期中产生的控制信号。

              2. 流水线寄存器按以下方式编号:

    •      F  保存程序计数器的预测值。
    •      D  位于取指和译码阶段之间。它保存关于最新取出的指令的信息,即将由译码阶段进行处理。
    •      E  位于译码和执行阶段之间。它保存关于最新译码的指令和从寄存器文件读出的值的信息,即将由执行阶段进行处理。
    •     M  位于执行和访存阶段之间。它保存最新执行的指令的结果,即将由访存阶段进行处理。它还保存关于用于处理条件转移的分支条件和分支目标的结果。
    •      W  位于访存阶段和反馈路径之间,反馈路径将计算出来的值提供给寄存器文件写,而当完成ret指令时,它还会向PC选择逻辑提供返回地址。

              3. 流水线化设计的目的就是每个时钟周期都发射一条新指令,也就是说每个时钟周期都有一条新指令进入执行阶段并最终完成。

              4. 除了条件转移指令和ret以外,根据取指阶段中计算出来的信息,我们能够确定下一条指令的地址;对于call和jmp(无条件转移)来说,下一条指令的地址是指令中的常数字valC,而对于其他指令来说就是valP。

              5. PIPE-的取指阶段,负责预测PC的下一个值,以及为取指选择实际的PC。标号为“Predict PC”的块会在PC增加器计算出的ValP和取出的指令中得到的ValC中进行选择,这个值存放在流水线寄存器F中,作为程序计数器的预测值。标号为“Select PC”的块从三个值中选择一个作为指令存储器的地址:预测的PC,对于到达流水线寄存器M的不选择分支的指令来说是valP的值(存储在寄存器M valP中),或是当ret指令到达流水线寄存器W(存储在W valM)时的返回地址的值。

             6. 将流水线技术引入一个带反馈的系统,当相邻指令间存在相关时会导致出现问题。这些相关有两种形式:数据相关,即下一条指令会用到这一条指令计算出来的结果;控制相关,一条指令要确定下一条指令的位置,例如在执行跳转、调用或返回指令时。这些相关可能会导致流水线产生计算错误,称为冒险,分为数据冒险和控制冒险。

             7. 用暂停来避免数据冒险:暂停是避免冒险的一种常用技术,暂停时,处理器会停止流水线中一条或多条指令,直到冒险条件不再满足,让一条指令停顿在译码阶段,直到产生它的源操作数的指令通过了写回阶段,这样我们的处理器就能避免数据冒险。

                当指令addl %edx,%eax处于译码阶段时,流水线控制逻辑发现执行、访存或写回阶段中至少有一条指令会更新寄存器% edx或%eax,处理器会暂停该指令,将他阻塞在译码阶段,时间为一个或多个周期,然后沿着流水线进行下去;将addl指令阻塞在译码阶段时,我们还必须将紧跟其后的halt指令阻塞在取值阶段。

                每次要把一条指令阻塞在译码阶段,就在执行阶段插入一个气泡,气泡就像一个自动产生的nop指令——它不会改变寄存器、存储器或程序状态。

            8. 用转发来避免数据冒险:与其暂停直到写完成,不如简单地将要写的值传到流水线寄存器E作为源操作数。对于addl %edx,%eax;译码阶段逻辑发现,寄存器%eax是操作数valB的源寄存器,而在写端口E上还有一个对%eax的未进行的写。它只要简单地将提供到端口E的数据字(信号W_valE)作为操作数valB的值,就能避免暂停。这种将结果值直接从一个流水线阶段传到较早阶段的技术称为数据转发,或简称转发,有时称为旁路。

                译码阶段逻辑能够确定使用来自寄存器文件的值,还是用转发过来的值。与每个要写回寄存器文件的值相关的是目的寄存器ID,逻辑会将这些ID与源寄存器ID srcA和srcB相比较,以此来检测是否需要转发,可能有多个目的寄存器ID与一个源寄存器ID相等,要解决这样的情况,我们必须在各个转发源中建立优先级关系。

             9. 用暂停和转发结合,来避免加载使用数据冒险。当mrmovl £10,%ebx指令通过执行阶段时,流水线控制逻辑发现译码阶段中的指令addl %ebx,%eax需要从存储器中读出结果,它会将译码阶段中的指令暂停一个周期,导致执行阶段中插入一个气泡,之后,从存储器中读出的值可以从访存阶段转发到译码阶段中的addl指令。

             10. 异常可以由程序执行从内部产生,也可以由某个外部信号从外部产生。我们的指令集体系结构包括三种不同的内部产生的异常:halt指令、有非法指令和功能码组合的指令、取指或数据读写试图访问一个非法地址。

             11. 异常处理的一些细节问题:首先,可能同时有多条指令会引起异常,基本原则是,由流水线中最深的指令引起的异常,优先级最高;第二个问题是,当首先取出一条指令,开始执行时,导致了一个异常,而后来由于分支错误,取消了该指令;第三个问题是,因为流水线化的处理器会在不同的阶段更新系统状态的不同部分,有可能会出现这样的情况,一条指令导致了一个异常,它后面的指令在异常指令完成之前改变了部分状态。

             12. 处理ret:流水线必须暂停直到ret指令到达写回阶段。——译码阶段插入气泡

                   加载使用冒险:在一条从存储器中读出一个值的指令和一条使用该值的指令之间,流水线必须暂停一个周期。——保持流水线寄存器F和D固定不变,执行阶段插入气泡。

                   预测错误的分支:在分支逻辑发现不应该选择分支之前,分支目标出的几条指令已经进入流水线了,必须从流水线中去除这些指令。——译码和执行阶段插入气泡,并取出跳转指令后面的指令。               异常:当一条指令导致异常,我们想要禁止后面的指令更新程序员可见的状态,并且在异常指令到达写回阶段时,停止执行。——每个流水线寄存器中会包含一个状态码stat,随着每条指令经过流水线阶段,它会记录指令的状态。当异常发生时,我们将这个信息作为指令状态的一部分记录下来,并且继续取指、译码和执行指令,就好像什么也没出错似的。当异常指令到达访存阶段时,我们会采取措施防止后面的指令修改程序员可见的状态:禁止执行阶段中的指令设置条件码;向寄存器阶段中插入气泡,以禁止向数据存储器中写入;当写回阶段中有异常指令时,暂停写回阶段,因而暂停了流水线。

             13. 当ret指令通过流水线时,要想发现它,只要检测译码、执行和访存阶段中指令的指令码;发现加载使用冒险要检查执行阶段中的指令类型(mrmovl或popl),并把它的目的寄存器与译码阶段中指令的源寄存器相比较;当跳转指令在执行阶段时,流水线控制逻辑应该能发现预测错误的分支,这样当指令进入访存阶段时,它就能设置从错误预测中恢复所需要的条件。当跳转指令处于执行阶段时,信号e_Cnd指明是否要选择分支;通过检查访存和写回阶段中的指令状态值,就能发现异常指令,对于访存阶段,我们使用在这个阶段中计算出来的信号m_stat,这个内部信号包含着可能的数据存储器地址。

     

  • 相关阅读:
    php isset()与empty()的使用
    , , 的区别
    让IE6 IE7 IE8 IE9 IE10 IE11支持Bootstrap的解决方法
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    当导航条滚动到顶部时固定到顶部
    JS:window.onload的使用
    jquery里面.length和.size()有什么区别
    2017.4.13(内置函数)作业
    文件内容的增删改查
    用户登陆程序,密码三次错误自动锁定用户名。
  • 原文地址:https://www.cnblogs.com/zizaijiapu/p/8407211.html
Copyright © 2020-2023  润新知