常见CPU指令系统

文章目录

    • 一、指令系统简介
    • 二、指令系统组成
      • 2.1 地址空间
      • 2.2 操作数
      • 2.3 指令操作和编码
    • 三、RAISC指令比较
      • 3.1 指令格式比较
      • 3.2 寻址方式比较
      • 3.3 公共指令功能
    • 四、RAISC常见指令类型
      • 4.1 基础整数指令
      • 4.2 基础浮点指令
      • 4.3 特权指令

一、指令系统简介

依据指令长度的不同,指令系统可分为复杂指令系统(Complex Instruction Set Computer,简称CISC)、精简指令系统(Reduced Instruction Set Computer,简称RISC)和超长指令字(Very Long Instruction Word,简称VLIW)指令集三种。

CISC中的指令长度可变;RISC中的指令长度比较固定;VLIW本质上来讲是多条同时执行的指令的组合,其“同时执行”的特征由编译器指定,无须硬件进行判断。

早期的CPU都采用CISC结构,如IBM的System360、Intel的8080和8086系列、Motorola的68000系列等。这种设计简化了软件和编译器的设计,但也显著提高了硬件的复杂性。当硬件复杂度逐渐提高时,CISC结构出现了一系列问题。大量复杂指令在实际中很少用到,典型程序所使用的80%的指令只占指令集总指令数的20%,消耗大量精力的复杂设计只有很少的回报。同时,复杂的微代码翻译也会增加流水线设计难度,并降低频繁使用的简单指令的执行效率。

RISC指令系统的最本质特征是通过load/store结构简化了指令间关系,即所有运算指令都是对寄存器运算,所有访存都通过专用的访存指令(load/store)进行。这样,CPU只要通过寄存器号的比较就能判断运算指令之间以及运算指令和访存指令之间有没有数据相关性,而较复杂的访存指令相关判断(需要对访存的物理地址进行比较)则只在执行load/store指令的访存部件上进行,从而大大简化了指令间相关性判断的复杂度,有利于CPU采用指令流水线、多发射、乱序执行等提高性能。

VLIW结构的最初思想是最大限度利用指令级并行(Instruction Level Parallelism,简称ILP),VLIW的一个超长指令字由多个互相不存在相关性(控制相关、数据相关等)的指令组成,可并行进行处理。VLIW可显著简化硬件实现,但增加了编译器的设计难度。

二、指令系统组成

2.1 地址空间

处理器可访问的地址空间包括寄存器空间系统内存空间。寄存器空间包括通用寄存器、专用寄存器和控制寄存器。寄存器空间通过编码于指令中的寄存器号寻址,系统内存空间通过访存指令中的访存地址寻址。

  • 通用寄存器是处理器中最常用的存储单元,包括浮点和整数寄存器,一个处理器周期可以同时读取多条指令需要的多个寄存器值(对于多发射处理器而言)。
  • 除了通用寄存器外,有的指令系统还会定义一些专用寄存器,仅用于某些专用指令或专用功能。如MIPS指令系统中定义的HI、LO寄存器就仅用于存放乘除法指令的运算结果。
  • 控制寄存器用于控制指令(如特权指令)执行的环境,比如是核心态还是用户态。其数量、功能和访问方式依据指令系统的定义各不相同。
  • 广义的系统内存空间包括IO空间内存空间,不同指令集对系统内存空间的定义各不相同。X86指令集包含独立的IO空间和内存空间,对这两部分空间的访问需要使用不同的指令:内存空间使用一般的访存指令,IO空间使用专门的in/out指令。而MIPS、ARM、LoongArch等RISC指令集则通常不区分IO空间和内存空间,把它们都映射到同一个系统内存空间进行访问,使用相同的load/store指令。处理器对IO空间的访问不能经过Cache,因此在使用相同的load/store指令既访问IO空间又访问内存空间的情况下,就需要定义load/store指令访问地址的存储访问类型,用来决定该访问能否经过Cache。如果采用页式地址映射方式,那么同一页内的地址定义为相同的存储访问类型,通常作为该页的一个属性信息记录在页表项中,如MIPS指令集中的页表项含有CCA域,LoongArch指令集中的页表项含有MAT域。如果采用段式地址映射方式,那么同一段内的地址定义为相同的存储访问类型。

根据指令使用数据的方式,指令系统可分为堆栈型累加器型寄存器型。寄存器型又可以进一步分为寄存器-寄存器型(Register-Register)和寄存器-存储器型(Register-Memory)。下面分别介绍各类型的特点:

  • 堆栈型。堆栈型指令又称零地址指令,其操作数都在栈顶,在运算指令中不需要指定操作数,默认对栈顶数据进行运算并将结果压回栈顶。
  • 累加器型。累加器型指令又称单地址指令,包含一个隐含操作数——累加器,另一个操作数在指令中指定,结果写回累加器中。
  • 寄存器-存储器型。在这种类型的指令系统中,每个操作数都由指令显式指定,操作数为寄存器和内存单元。
  • 寄存器-寄存器型。在这种类型的指令系统中,每个操作数也由指令显式指定,但除了访存指令外的其他指令的操作数都只能是寄存器。

下图给出了四种类型的指令系统中执行C=A+B的指令序列,其中A、B、C为不同的内存地址,R1、R2等为通用寄存器。
常见CPU指令系统_第1张图片
寄存器-寄存器型指令系统中的运算指令的操作数只能来自寄存器,不能来自存储器,所有的访存都必须显式通过load和store指令来完成,所以寄存器-寄存器型又被称为load-store型。

2.2 操作数

计算机中常见的数据类型包括整数、实数、字符,数据长度包括1字节、2字节、4字节和8字节。下图是不同指令集整数类型的名称和数据长度:
常见CPU指令系统_第2张图片
在执行访存指令时,必须考虑的问题是访存地址是否对齐和指令系统是否支持不对齐访问。所谓对齐访问是指对该数据的访问起始地址是其数据长度的整数倍,例如访问一个4字节数,其访存地址的低两位都应为0。对齐访问的硬件实现较为简单,若支持不对齐访问,硬件需要完成数据的拆分和拼合。但若只支持对齐访问,又会使指令系统丧失一些灵活性,例如串操作经常需要进行不对齐访问,只支持对齐访问会让串操作的软件实现变得较为复杂。以X86为代表的CISC指令集通常支持不对齐访问,RISC类指令集在早期只支持对齐访问,现在也开始支持不对齐访问。

不同的机器可能使用大尾端或小尾端,最高有效字节的地址较小的是大尾端,最低有效字节的地址较小的是小尾端。Motorola的68000系列和IBM的System系列指令系统采用大尾端,X86、VAX和LoongArch等指令系统采用小尾端,ARM、SPARC和MIPS等指令系统同时支持大小尾端。

寻址方式指如何在指令中表示要访问的内存地址。下表列出了计算机中常用的寻址方式,其中数组mem表示存储器,数组regs表示寄存器,mem[regs[Rn]]表示由寄存器Rn的值作为存储器地址所访问的存储器值:
常见CPU指令系统_第3张图片

2.3 指令操作和编码

现代指令系统中,指令的功能由指令的操作码决定,可分为四大类:

  • 第一类为运算指令,包括加减乘除、移位、逻辑运算等;
  • 第二类为访存指令,负责对存储器的读写;
  • 第三类是转移指令,用于控制程序的流向;
  • 第四类是特殊指令,用于操作系统的特定用途。

转移指令包括条件转移、无条件转移、过程调用和过程返回等类型。转移条件和转移目标地址是转移指令的两个要素,两者的组合构成了不同的转移指令:条件转移要判断条件再决定是否转移,无条件转移则无须判断条件;相对转移是程序计数器(PC)加上一个偏移量作为转移目标地址,绝对转移则直接给出转移目标地址;直接转移的转移目标地址可直接由指令得到,间接转移的转移目标地址则需要由寄存器的内容得到。程序中的switch语句、函数指针、虚函数调用和过程返回都属于间接转移。由于取指译码时不知道目标地址,因此硬件结构设计时处理间接跳转比较麻烦。

转移指令有几个特点:第一,条件转移在转移指令中最常用;第二,条件转移通常只在转移指令附近进行跳转,偏移量一般不超过16位;第三,转移条件判定比较简单,通常只是两个数的比较。条件转移指令的条件判断通常有两种实现方式:采用专用标志位和直接比较寄存器。采用专用标志位方式的,通过比较指令或其他运算指令将条件判断结果写入专用标志寄存器中,条件转移指令仅根据专用标志寄存器中的判断结果决定是否跳转。采用直接比较寄存器方式的,条件转移指令直接对来自寄存器的数值进行比较,并根据比较结果决定是否进行跳转。X86和ARM等指令集采用专用标志位方式,RISC-V指令集则采用直接比较寄存器方式,MIPS和LoongArch指令集中的整数条件转移指令采用直接比较寄存器方式,而浮点条件转移指令则采用专用标志位方式。

下图是LoongArch指令集的编码格式:
常见CPU指令系统_第4张图片

三、RAISC指令比较

3.1 指令格式比较

下图是五种RISC指令集的指令编码格式。在寄存器类指令中,操作码都由操作码(OP)和辅助操作码(OPX)组成,操作数都包括两个源操作数(RS)和一个目标操作数(RD);立即数类指令都由操作码、源操作数、目标操作数和立即数(Const)组成,立即数的位数各有不同;
常见CPU指令系统_第5张图片

3.2 寻址方式比较

MIPS、SPARC和LoongArch只支持四种常用的寻址方式,PowerPC和PA-RISC支持的寻址方式较多。
常见CPU指令系统_第6张图片

3.3 公共指令功能

load-store指令。load指令将内存中的数据取入通用寄存器,store指令将通用寄存器中的数据存至内存中。当从内存中取回的数据位宽小于通用寄存器位宽时,后缀没有U的指令进行有符号扩展,即用取回数据的最高位(符号位)填充目标寄存器的高位,否则进行无符号扩展,即用数0填充目标寄存器的高位。

ALU指令。ALU指令都是寄存器型的,常见的ALU指令包括加、减、乘、除、与、或、异或、移位和比较等。

控制流指令。控制流指令分为绝对转移指令和相对转移指令。相对转移的目标地址是当前的PC值加上指令中的偏移量立即数;绝对转移的目标地址由寄存器或指令中的立即数给出。在条件转移指令中,转移条件的确定有两种方式:判断条件码和比较寄存器的值。SPARC采用条件码的方式,整数运算指令置条件码,条件转移指令使用条件码进行判断。MIPS和LoongArch的定点转移指令使用寄存器比较的方式进行条件判断,而浮点转移指令使用条件码。

四、RAISC常见指令类型

4.1 基础整数指令

算数运算类指令:加减乘除、取余、位运算(与、或、异或)、比较、及特殊运算(alsl、pcaddi等)。

移位运算累指令:逻辑左移、逻辑右移、算数右移、循环右移。

位操作指令:截取某个范围的数据,如ext.b rd rj将rj的[7:0]符号扩展后写入rd。计量连续比特1或0的个数,如clo.w rd rj对rj的[31:0]数据,从第31位方向开始向第0位统计连续比特1的个数,clz.w是连续比特0。

转移指令

  • 所有的跳转都是相对跳转,也就是指令中的目标值左移2位、符号扩展后与PC相加。
  • 有条件跳转,一种是两个寄存器之间比较,一种单个寄存器与0比较,根据比较结果决定是否跳转至目标地址。
  • 无条件跳转,一种是跳转至目标地址后不回来,一种是跳转至目标地址后要回来,即将下一条指令的地址存放到返回寄存器(ra = PC + 4)或栈上。

普通访存指令{ld/sd}.{b/h/w/d}[u] rd, rj, si12

  • 从内存中取回数据放入寄存器,或将寄存器中的值放入内存。若访存地址自然对齐,不触发非对齐例外。若访存地址不自然对齐,如果硬件支持非对齐访问,且当前运算环境配置也允许对齐访问,则不触发非对齐例外,否则触发。
  • 从内存预取一个Cache行的数据进入Cache中,访存地址落在待预取的Cache行内,见preld和preldx指令。该类指令不会触发任何与MMU或是地址相关的例外。

边界检查访存指令{ld/sd}{gt/le}.{b/h/w/d} rd rj rk。从内存中取回数据放入寄存器。访存地址直接从rj中取,且要自然对齐,否则触发非对齐例外。还要比较寄存器rj和rk的值的大小,如果指令预设条件,则进行访存,不满足条件则会终止访存并触发边界检查例外。

原子访存指令am{swap/add/and/or/xor/max/min}[_db].{w/d} rd rj rk

  • 原子访存指令能够原子的完成对某个内存单元的“读-修改-写”操作序列。具体来说,其将内存指定地址处的旧值取回并写入通用寄存器rd,同时将这个内存中的旧值与通用寄存器rk中的值进行一些简单的运算操作,然后将运算结果写回到内存的指定地址处。访存地址直接从rj中取,且要自然对齐,否则触发非对齐例外。

内存屏障指令dbaribar

在大部分场景下,我们不用特意关注内存屏障的,特别是在单处理器系统里,虽然CPU内部支持乱序执行以及预测式的执行,但是总体来说,CPU会保证最终执行结果符合程序员的要求。在多核并发编程的场景下,程序员需要考虑是不是应该用内存屏障指令。下面是一些需要考虑使用内存屏障指令的典型场景。

  • 在多个不同CPU内核之间共享数据。在弱一致性内存模型下,某个CPU乱的内存访问次序可能会产生竞争访问。
  • 执行和外设相关的操作,例如DMA操作。启动DMA操作的流程通常是这样的:第一步,把数据写入DMA缓冲区里;第二步,设置DMA相关寄存器来启动DMA。如果这中间没有内存屏障指令,第二步的相关操作有可能在第一步前面执行,这样DMA就传输了错误的数据。
  • 修改内存管理的策略,例如上下文切换、请求缺页以及修改页表等。
  • 修改存储指令的内存区域,例如自修改代码的场景。

总之,我们使用内存屏障指令的目的是想让CPU按照程序代码逻辑来执行,而不是被CPU乱序执行和预测执行打乱了代码的执行次序。LoongArch有两个内存屏障指令:

  • dbar 0是一个完全功能的同步屏障。只有等到之前所有load/store访存操作彻底执行完毕后,dbar 0指令才能开始执行;且只有dbar 0执行完毕后,其后所有load/store访存操作才能开始执行。
  • ibar 0使用完成单个处理器核内部store操作与取值操作之间的同步。ibar 0能保证其之后的取值一定能观察到其之前所有store操作的执行结果。

4.2 基础浮点指令

浮点运算类指令:浮点加减乘除运算。

浮点比较指令:大于、等于、小于以及无法比较,当有两个操作数中至少有一个NaN时,这两个数就无法比较。比较结果会写入指定的条件标志寄存器

浮点转换指令:单精度与双精度之间转换、定点与浮点之间的转换(舍入模式)。

浮点搬运指令:浮点寄存器之间搬运(直接搬运:fmov.s fd, fj;条件搬运:fsel fd, fj, fk, ca,若ca为0,fd = fj,否则fd = fk)、浮点与通用寄存器之间搬运、浮点与条件标志寄存器 之间搬运、通用与条件标志寄存器 之间搬运、通用与浮点控制状态寄存器 之间搬运。

浮点分支指令:只有条件分支,根据条件标志寄存器 的值决定是否跳转目标地址。

浮点普通访存指令:从内存中取回数据放入浮点寄存器,或将浮点寄存器中的值放入内存。

浮点边界检查访存指令:检查有效地址是否越界,从内存中取回数据放入浮点寄存器,或将浮点寄存器中的值放入内存。

4.3 特权指令

所谓特权指令是指有特权权限的指令,由于这类指令的权限最大,如果使用不当,将导致整个系统崩溃。常见的特权指令有:

  • CSR访问指令。CSR,即Control and Status Register,控制与状态寄存器,属于CPU自带的一类寄存器。关于控制状态寄存器可参考。
  • Cache维护指令。主要对Cache进行初始化以及一致性维护。
  • TLB维护指令,软件页表遍历指令等。

为了保证系统安全,这类指令只能用于操作系统或其他系统软件,不直接提供给用户使用。为了防止用户程序中使用特权指令,用户态下只能使用非特权指令,核心态下可以使用全部指令。当在用户态下使用特权指令时,将产生中断以阻止用户使用特权指令。所以把用户程序放在用户态下运行,而操作系统中必须使用特权指令的那部分程序在核心态下运行,保证了计算机系统的安全可靠。从用户态转换为核心态的唯一途径是中断或异常。

【参考书籍】
《LoongArch 参考手册》
《计算机体系结构基础》第3版
《计算机体系结构量化研究方法》第5版

你可能感兴趣的:(计算机体系结构,cpu,体系结构,指令系统)