计算机组成-3.处理器

307 阅读11分钟

物理实现

  • 门电路
    • 晶体管
      • N型MOS管
      • P型MOS管
    • 逻辑门
  • 寄存器
    • D触发器
      • 具有存储信息能力的基本单元
      • 由若干逻辑门构成,有多种实现方式
      • 主要有一个数据输入、一个数据输出和一个时钟输入
      • 在时钟clock的上升沿(0→1),采样输入D的值,传送到输出Q,其余时间输出Q的值不变
    • 寄存器的构成

算术逻辑单元

  • 逻辑运算的实现
  • 加法和减法的实现
  • 乘法器和除法器的实现

单周期处理器

67d07855e97917d3cc13f5a2d57acc1c.png

  • 物理设计步骤
      1. 分析指令系统,得出对数据通路的需求
      • 算术逻辑单元(ALU)
        • 运算类型:加、减、或、比较相等
        • 操作数:2个32位的数,来自寄存器 或 扩展后的立即数
      • 立即数扩展部件
        • 将一个16立即数扩展为32位数
        • 扩展方式:零扩展、符号扩展
      • 程序计数器(PC)
        • 一个32位的寄存器
        • 支持两种加法:加4 或 加一个立即数
      • 寄存器堆
        • 每个寄存器为32位宽,共32个
        • 支持读操作:rs 和 rt
        • 支持写操作:rt 或 rd
      • 存储器
        • 一个只读的指令存储器,地址和数据均为32位
        • 一个可读写的数据存储器,地址和数据均为32位
      1. 为数据通路选择合适的组件
      • 寄存器堆
        • 内部构成
          • 32个32位的寄存器
        • 数据接口信号
          • busA,busB:两组32位的数据输出
          • busW:一组32位的数据输入
        • 读写控制
          • Ra(5位):选中对应编号的寄存器,将其内容放到busA
          • Rb(5位):选中对应编号的寄存器,将其内容放到busB
          • Rw(5位):选中对应编号的寄存器,在时钟信号(clk)的上升沿,如果写使能信号有效(WriteEnable==1),将busW的内容存入该寄存器
      • 存储器
        • 数据接口信号
        • 读写控制
      1. 连接组件建立数据通路
      1. 分析每条指令的实现,以确定控制信号
      1. 集成控制信号,形成完整的控制逻辑

流水线处理器

  • 原理
    • 执行指令的主要步骤MIPS
      • 1、取指(Fetch)
        • 从存储器取指令,更新PC
      • 2、译码(Decode)
        • 指令译码,从寄存器堆读出寄存器的值
      • 3、执行(Execute)
        • 运算指令:进行算术逻辑运算
        • 访存指令:计算存储器的地址
      • 4、访存(Memory)
        • Load指令:从存储器读数据
        • Store指令:将数据写入存储器
      • 5、回写(Write-back)
        • 将数据写入寄存器堆
    • 性能分析
      • 流水线中的各个处理部件可并行工作,从而可使整个程序的执 行时间缩短
      • 流水线并不会缩短单条指令的执行时间(甚至会增加时间), 而是提高了指令的吞吐率
  • “超级流水线”技术 (Super Pipelining)
    • 将五级流水线细分为更多的阶段,增加流水线的深度
    • 提升时钟频率,从而提高指令吞吐率
    • 深度
      • 流水线的级数是越多越好吗?◦ 否!
  • 超标量流水线
    • 超标量(Superscalar)
      • 通常,具有两条或两条以上并行工作的流水线结构称为超标量结构 • 亦称为“超标量流水线”或直接称为“超标量”,• 与之相对,之前的流水线则称为“标量流水线”
      • 使用超标量结构的处理器称为超标量处理器
    • 标量流水线和超标量流水线
      • 单周期→标量流水线:时间并行性的优化,主要是对现有硬件的切分
      • 标量流水线→超标量流水线:空间并行性的优化,需成倍增加硬件资源
    • 现代的多核CPU通常是在一个CPU芯 片中集成了多个超标量处理器核
  • 流水线的“冒险”
    • “冒险”(Hazard)
      • 阻止下一条指令在下一个时钟周期开始执行的情况
      • 1 结构冒险
        • ◦ 所需的硬件部件正在为之前的指令工作
          1. 如果指令和数据放在同一个存储器中,则不能同时读存储器
          • 解决方案1:流水线停顿(stall),产生空泡(bubble)
          • 解决方案2:指令和数据放在不同的存储器中
          1. 如果读寄存器和写寄存器同时发生,如何处理?
          • 解决方案:前半个时钟周期写,后半个时钟周期读,并且设置独立的读写口
      • 2 数据冒险
        • ◦ 需要等待之前的指令完成数据的读写
          1. 一条指令需要使用之前指令的运算结果,但是结果还没有写回
          • 解决方案1:流水线停顿(stall),产生空泡(bubble)
          • 解决方案2:数据前递(Forwarding)
          1. 一条指令需要使用之前指令的访存结果(Load-Use Harzard)
          • 解决方案:流水线停顿+数据前递
      • 3 控制冒险
        • ◦ 需要根据之前指令的结果决定下一步的行为
          1. 尚未确定是否发生分支,如何进行下一次取指?
          • 解决方案1:流水线停顿(stall),产生空泡(bubble)

CPU执行过程

CPU总是周而复始地做同一件事:从内存取指令,然后解释执行它,然后再取下一条指令,再解释执行。CPU最核心的功能单元包括:

  • 寄存器(Register),是CPU内部的高速存储器,像内存一样可以存取数据,但比访问内存快得多。随后的几章我们会详细介绍x86的寄存器eax、esp、eip等等,有些寄存器只能用于某种特定的用途,比如eip用作程序计数器,这称为特殊寄存器(Special-purpose Register),而另外一些寄存器可以用在各种运算和读写内存的指令中,比如eax寄存器,这称为通用寄存器(General-purpose Register)。

  • 程序计数器(PC,Program Counter),是一种特殊寄存器,保存着CPU取下一条指令的地址,CPU按程序计数器保存的地址去内存中取指令然后解释执行,这时程序计数器保存的地址会自动加上该指令的长度,指向内存中的下一条指令。

  • 指令译码器(Instruction Decoder)。CPU取上来的指令由若干个字节组成,这些字节中有些位表示内存地址,有些位表示寄存器编号,有些位表示这种指令做什么操作,是加减乘除还是读写内存,指令译码器负责解释这条指令的含义,然后调动相应的执行单元去执行它。

  • 算术逻辑单元(ALU,Arithmetic and Logic Unit)。如果译码器将一条指令解释为运算指令,就调动算术逻辑单元去做运算,比如加减乘除、位运算、逻辑运算。指令中会指示运算结果保存到哪里,可能保存到寄存器中,也可能保存到内存中。

  • 地址和数据总线(Bus)。CPU和内存之间用地址总线、数据总线和控制线连接起来,每条线上有1和0两种状态。如果在执行指令过程中需要访问内存,比如从内存读一个数到寄存器,执行过程可以想像成这样:

  • CPU内部将寄存器对接到数据总线上,使寄存器的每一位对接到一条数据线,等待接收数据。

  • CPU通过控制线发一个读请求,并且将内存地址通过地址线发给内存。

  • 内存收到地址和读请求之后,将相应的内存单元对接到数据总线的另一端,这样,内存单元每一位的1或0状态通过一条数据线到达CPU寄存器中相应的位,就完成了数据传送。

往内存里写数据的过程与此类似,只是数据线上的传输方向相反。

上图中画了32条地址线和32条数据线,CPU寄存器也是32位,可以说这种体系结构是32位的,比如x86就是这样的体系结构,目前主流的处理器是32位或64位的。地址线、数据线和CPU寄存器的位数通常是一致的,从上图可以看出数据线和CPU寄存器的位数应该一致,另外有些寄存器(比如程序计数器)需要保存一个内存地址,因而地址线和CPU寄存器的位数也应该一致。处理器的位数也称为字长,字(Word)这个概念用得比较混乱,在有些上下文中指16位,在有些上下文中指32位(这种情况下16位被称为半字Half Word),在有些上下文中指处理器的字长,如果处理器是32位那么一个字就是32位,如果处理器是64位那么一个字就是64位。32位计算机有32条地址线,地址空间(Address Space)从0x00000000到0xffffffff,共4GB,而64位计算机有更大的地址空间。

最后还要说明一点,本节所说的地址线、数据线是指CPU的内总线,是直接和CPU的执行单元相连的,内总线经过MMU和总线接口的转换之后引出到芯片引脚才是外总线,外地址线和外数据线的位数都有可能和内总线不同,例如32位处理器的外地址总线可寻址的空间可以大于4GB。

我们结合表 1.1 “一个语句的三种表示”看一下CPU取指执行的过程。

  • eip寄存器指向地址0x80483a2,CPU从这里开始取一条5个字节的指令,然后eip寄存器指向下一条指令的起始地址0x80483a7。

  • CPU对这5个字节译码,得知这条指令要求从地址0x804a01c开始取4个字节保存到eax寄存器。

  • 执行指令,读内存,取上来的数是3,保存到eax寄存器。注意,地址0x804a01c~0x804a01f里存储的四个字节不能按地址从低到高的顺序看成0x03000000,而要按地址从高到低的顺序看成0x00000003。也就是说,对于多字节的整数类型,低地址保存的是整数的低位,这称为小端(Little Endian)字节序(Byte Order)。x86平台是小端字节序的,而另外一些平台规定低地址保存整数的高位,称为大端(Big Endian)字节序。

  • CPU从eip寄存器指向的地址取一条3个字节的指令,然后eip寄存器指向下一条指令的起始地址0x80483aa。

  • CPU对这3个字节译码,得知这条指令要求把eax寄存器的值加1,结果仍保存到eax寄存器。

  • 执行指令,现在eax寄存器中的数是4。

  • CPU从eip寄存器指向的地址取一条5个字节的指令,然后eip寄存器指向下一条指令的起始地址0x80483af。

  • CPU对这5个字节译码,得知这条指令要求把eax寄存器的值保存到从地址0x804a018开始的4个字节。

  • 执行指令,把4这个值保存到从地址0x804a018开始的4个字节(按小端字节序保存)。

cpu的启动

  • x86的cpu在启动的时候都会先进入实模式,可以认为是一个非常快的8086:
  • 1.cpu复位之后会去1M地址空间的最高的16个byte取第一条指令
    1. 这个地址会被南北桥芯片组引导到bios芯片
    1. cpu执行bios芯片上的指令,对主板上的各个设备进行基本的配置
    • 在内存的某个地方先把全局描述符表GDT都填好,然后将这张表的起始地址放在GDTR寄存器
    • 同样地,在主存地址0的另一个地方构建中断向量表,也就是向主存中那最低的1k个字节填写中断向量,构建好了中断向量表,也准备好了中断服务程序,cpu再遇到中断,就可以访问主存中的中断向量表,并调用相应的中断服务程序了
    1. cpu进入保护模式。
    • 保护模式下的寻址
      • 保护模式下逻辑地址写成CS:EIP的形式,但物理地址的产生方式已经与实模式不同了
        • 段基址不在CS中,而是存放在内存中
        • CS寄存器的内存+GDTR寄存器(上一步中已经初始化)的内容得到一个地址,用这个地址去访问存储器,取出这个描述符,再把描述符的第2、3、4、7字节的基地址提取出来,和指令指针寄存器EIP的内容进行组合,得到想要的存储器地址,用这个地址去访问存储器,得到想要的指令编码。