计算机组成原理笔记-第五章-中央处理器

基于里昂学长的计算机组成原理课程

笔记目录

第一章-计算机系统概述
第二章-数据的表示和运算
第三章-存储系统
第四章-指令系统
第五章-中央处理器
第六章-总线
第七章-输入输出系统

第五章 中央处理器

5.1 CPU的功能和基本结构

5.1.1 CPU的基本结构

CPU主要由运算器控制器两部分组成。

运算器

运算器主要由算数逻辑运算单元(ALU,Arithmetic Logic Unit)、通用寄存器(GPR,General-Purpose Register)、程序状态字寄存器(PSW,Program Status Word)等组成,其主要功能是根据控制器所发出的控制命令对数据进行加工执行算术运算逻辑运算逻辑测试

image-20250625201634475

控制器

控制器主要由控制单元(CU,Control Unit)、程序计数器(PC,Program Counter)、指令寄存器(IR,Instruction Register)、指令译码器(ID,Instruction Decoder)、存储器数据寄存器(MDR,Memory Data Register)、存储器地址寄存器(MAR,Memory Address Register)、时序产生器等组成。

时序产生器:

指令执行过程中的所有操作必须按照一定的次序执行,不能有任何差错。对执行的先后次序有规定,而且什么时候送操作数、什么时候执行操作、什么时候送出结果也有规定。因此需要引入时序的概念,要对完成指令而执行的微操作信号进行时间调制,严格规定各信号的产生时间和持续时间

image-20250625202231926

汇编程序员可见的寄存器

CPU中的寄存器按汇编语言是否可访问,分为两类:

一类是用户可见寄存器可对这类寄存器编程,以通过使用这类寄存器减少对主存储器的访问次数,例如通用寄存器组(含基址/变址寄存器)、程序状态字寄存器、程序计数器、累加寄存器。

另一类是用户不可见寄存器,对用户是透明的,不可对这类寄存器编程。它们被控制部件使用,以控制CPU的操作,如MDR、MAR、IR、暂存寄存器。

image-20250625202631542

image-20250625202643529

  • 累加寄存器(ACC)

累加寄存器(ACC,Accumulator)是通用寄存器的一种,用来暂存ALU运算的结果信息。在现在的计算机中可以用通用寄存器代替专门的累加器。

  • 暂存寄存器

暂存寄存器用来暂存由数据总线或通用寄存器送来的操作数,以便在取出下一个操作数时将其送入ALU,暂存寄存器对程序员是透明的(不可见)。

5.1.2 CPU的功能

中央处理器(CPU,Central Processing Unit)负责解释计算机指令,协调并控制计算机各组件正确地执行指令序列,并根据指令对数据进行加工。

  1. 指令控制
    从程序执行的角度,指令控制保证了指令按照程序预先规定好的顺序执行。
    从指令本身的角度,指令控制还确保了取指令、分析指令和执行指令的顺序执行。
  2. 操作控制
    产生完成一条指令所需要的操作信号,并把操作信号送到CPU相应的执行部件,使执行部件按指令规定的操作正确执行。
  3. 时序控制
    对每个操作信号进行定时,严格控制操作信号的产生时间、持续时间。
  4. 中断控制
    及时响应内部异常和外部中断请求,例如缺页异常和键盘中断。
  5. 数据加工
    对数据进行算术运算、逻辑运算,或将数据在相关部件之间传送。

以上5个功能中,前4个由控制器实现第5个由运算器实现

5.2 指令执行过程

5.2.1 一条一般指令执行的一般过程

指令执行时,

  1. 首先以PC为地址访问主存取指令,同时更新PC的值作为后续顺序指令的地址。
  2. 通常指令译码会根据指令功能进入不同的执行路径,如果是分支指令且满足分支条件,只需重新修改PC的值作为分支目标地址即可。对于其他指令可能要经历取操作数、执行指令、存操作数的基本过程。
  3. 指令执行完毕后还需要进行中断异常判断,如果不存在中断异常,直接进入取指令、执行指令的循环。
  4. 如果控制器检测到中断异常,处理器就会进入中断响应阶段,此时需要关中断、保存断点、修改PC值作为中断服务入口地址,然后转去执行中断服务程序。

中断指计算机在执行程序的过程中,由于某些紧急事件的发生,使得程序的执行暂停,转去处理这些紧急事件,处理完毕后在回到原位继续执行的过程。

5.2.2 指令周期与机器周期

通常将一条指令从取出到执行所需要的时间称为指令周期,各种指令的指令周期是不尽相同的。

指令周期常常用若干个CPU周期数来表示,CPU周期又称为机器周期。CPU访问一次内存所花费的时间较长,因此通常取内存中读取一个指令字的最短时间来规定CPU周期。(有的教材:机器周期为CPU取出一个存储字所需的最短时间,即存取周期

一条指令的取出阶段需要一个CPU周期,而一个CPU周期又包含有若干个时钟周期(又称T周期节拍脉冲,它是处理操作的最基本单位)。这些$T_i$周期的总和规定了一个CPU周期的时间宽度。

image-20250626093832543

大多情况下,CPU按照取指-执行-再取指-再执行…的顺序自动工作。

取指阶段完成取指令和分析指令的操作,又叫取指周期;执行阶段完成执行指令的操作,又叫执行周期

各种指令的指令周期不同:

image-20250626094131601

image-20250626094148933

当遇到间接寻址的指令时,由于指令字中只给出操作数有效地址的地址,为了取出操作数还需要访存一次取出有效地址,然后再访存取操作数。

这样,间接寻址的指令周期还要加上一个间址周期,介于取指周期和执行周期之间。

image-20250626094350033

当CPU采用中断方式实现主机与IO交换信息时,每条指令的执行阶段结束前,都要发中断查询信号,检测是否有某个IO提出中断请求。如果有请求还需要进入中断相应阶段,又称中断周期

image-20250626094521730

以上四个周期也可称为CPU的基本工作周期(机器周期),每个周期都有访存操作,但是访存的目的不同。

image-20250626094606388

5.2.3 指令周期的数据流向

取指周期

取指令阶段完成的任务是将当前指令从主存中取出来并送到指令寄存器去,数据流向:

  1. PC->MAR->地址总线->存储器
  2. CU发出读命令->控制总线->存储器
  3. 主存->数据总线->MDR->IR
  4. CU发出控制信号->PC内容+1

image-20250626095031692

间址周期

取出指令后,CU检查IR中的内容,对IR中的指令字进行指令译码、识别指令类型、判断其是否有间址操作。取指阶段,IR是从MDR取出,所以如果需要间址操作,可以直接将MDR/IR中的形式地址(记为Ad(IR))送到MAR,再送到地址总线,此后CU通过控制总线向存储器发读命令,以过去操作数有效地址并存入MDR。

数据流:

  1. Ad(IR)->MAR->地址总线->存储器
  2. CU发出读命令->控制总线->存储器
  3. 主存->数据总线->MDR(存放有效地址)

Ad(IR)表示取出IR中存放的指令字的地址字段。

image-20250626095643545

执行周期

执行周期,控制器向ALU及数据通路其他相关部件发出操作控制命令,对取出的操作数进行加工处理,并将处理状态信息记录到PSW中。由于不同指令在执行周期的操作不同,因此执行周期也可能不同,因此没有统一的数据流向。

中断周期

中断周期的主要任务是对异常事件(内中断)和请求中断事件(外中断)进行响应。通常在执行周期结束时,CPU会查询当前是否有中断请求,有则进入中断周期。

中断周期的主要工作为:关中断、保存断点、根据中断源寻找中断程序的入口地址。

假设程序断点保存在堆栈指针所指内存单元,假设SP指针指向栈顶元素,进栈先修改指针后存入数据(满栈)。

数据流向:

  1. CU控制将SP-1->MAR->地址总线->存储器
  2. CU发出写命令->控制总线->存储器
  3. PC->MDR->数据总线->主存(程序断点存入存储器)
  4. CU(中断服务程序的入口地址)->PC

image-20250626100354079

5.2.4 指令执行方案

不同处理器采用不同方案安排指令的执行步骤。

单周期处理器

单周期处理器对所有指令都选用相同的执行时间来完成,此时每条指令都在一个时钟周期内完成(CPI=1),指令之间串行执行,指令周期取决于执行时间最长的指令的执行时间。对于那些本来可以更快的指令,也要使用这个较长的周期,会拖慢整个系统的运行速度。

此外,在一个时钟周期内,控制器发出的控制信号不可以改变

image-20250626114039213

多周期处理器

多周期处理器对不同类型的指令选用不同的执行步骤。指令需要几个周期就为其分配几个周期,可以选用不同的时钟周期来完成不同的指令的执行过程(CPI>1),指令之间仍然是串行执行。时钟周期以最复杂步骤用时为准,通常是一次存储器读写的时间。

流水线处理器

流水线处理器采用指令之间并行执行的方案,目的是力争在每个时钟周期完成一条指令的执行过程(理想情况CPI=1)。这个方案通过在每个时钟周期启动一条指令,尽量让多条指令同时运行,但是处于不同的执行步骤中。

5.3 数据通路

通常把许多寄存器之间传送信息的通路称为数据通路。CPU内部可视为由数据通路控制部件两大部分组成。

5.3.1 操作原件与状态元件

数据通路中指令执行相关的元件有:组合逻辑元件(也称为操作元件)和存储元件(也称为状态元件时序逻辑元件)。

组合逻辑元件

image-20250626102901387

组合逻辑元件的输出只取决于当前的输入,不受时钟信号控制,有输入信号后,经过一定的逻辑门延迟,输出端的值被改变,并一直保持不变,直到输入信号改变。

常见的组合逻辑元件有:多路选择器(MUX)、算术逻辑部件(ALU)、译码器(Decoder)、三态门等。

image-20250626103102887

存储元件

存储元件由时序逻辑电路构成,具有存储功能,输入状态在时钟控制下被写到电路中

image-20250626103339743

数据通路中的寄存器是一种典型的状态存储元件,由n个D触发器可构成一个n位寄存器。根据功能和实现方式的不同,有各种不同类型的寄存器,如通用寄存器组、程序计数器、状态/移位/暂存/锁存寄存器等,它们都属于时序逻辑元件。

5.3.2 数据通路与时序控制

早期计算机的三级时序系统

早期计算机采用机器周期节拍脉冲三级时序对数据通路操作进行定时控制,一个指令周期可分为取指令读操作数执行并写结果等多个基本工作周期,被称为机器周期

一个机器周期内要进行若干动作,因此要将一个机器周期再分为若干节拍,每个动作在一个节拍内完成。

而为了产生操作控制信号并使某些工作能在一拍时间内配合工作,常常在一个节拍内设置一个或多个脉冲。例如,要在一个节拍内使某个寄存器的内容送到另一个寄存器中就需要设置先后两个工作脉冲,以产生打开数据通路脉冲和接收脉冲。

image-20250626105043162

image-20250626105055027

现代计算机中的时钟信号

现代计算机已经不采用上述三级时序系统,整个数据通路中的定时信号就是时钟,一个时钟周期就是一个节拍。

数据通路可看做由组合逻辑元件和状态元件交替组合而成。

只有状态元件能存储信息,通用数据通路模型可以利用一个操作元件连接两个状态元件构成,操作元件从前一个状态元件接受信息,结果送到后一个状态元件保存,两个状态单元采用统一时钟进行同步

微操作序列

微操作是比指令更基本、更小的操作步骤。指令需要分解为一系列时间上先后有序的最基本、最简单的微操作序列。而对于任何一个微操作,都需要控制器发出相应的控制信号。

image-20250626105813145

5.3.3 数据通路的基本结构

不同的数据通路有不同的微操作序列。数据通路的基本结构有两种:专用数据通路方式以及CPU内部总线方式

专用数据通路方式

部件之间采用分散连接,避免使用共享的总线。每个部件都有自己专用的数据通路,这些数据通路并不共享。

这种设计可以简化数据通路的管理,但是会增加硬件的复杂性和成本。

image-20250626115036646.png

CPU内部单总线方式

总线(Bus)指一组信号线,用于在计算机的各个组件之间传输数据、地址和控制信号,可以分为几种类型,主要包括:

数据总线(Data Bus):用于传输实际的数据,例如CPU和内存之间传输的数据。

地址总线(Address Bus):用于指定数据传输的内存地址。

控制总线(Control Bus):传输控制信号,如读写指令、中断请求等。

总线的宽度以(bit)位单位。

image-20250626115929851

CPU内部单总线方式的所有寄存器的输入输出都连接到一条公共通路上。该结构虽然简单,但是数据在传输的过程中,存在较多的冲突现象,因此性能相对较低。

内部总线是指CPU内部连接各寄存器及运算部件之间的总线;系统总线是指同一台计算机系统的各部件,如CPU、内存和各类I/O接口间相互连接的总线。

总线上信号流动的规则:每个时刻只允许一个器件发出信息,允许一个或多个器件接收信息

image-20250626151118671

CPU内部多总线方式

CPU内部有两条或更多的总线时,构成双总线结构多总线结构。将所有寄存器的输入端和输出端都连接到多条公共通路上,相比之下单总线中一个时钟内只允许传送一个数据,因而指令执行效率很低,因此采用多总线方式,同时在多个总线上传送不同的数据,提高效率。

image-20250626152215313

和单总线结构相比,多总线结构在执行指令时所需要的步骤大为减少。

例如,假定三操作数命令op R1,R2,R3的功能为R[R3]<-R[R1] OP R[R2],则可用总线A和B传送源操作数,总线C传送目的操作数。

源总线和目的总线之间的连接通过ALU实现。假定所需操作通过ALU一次就可完成,那么,该指令可在一个时钟周期内完成。

5.4 控制器的功能和工作原理

实现微操作的关键是在正确的时钟周期发出正确的控制信号,而控制信号实际上是由控制单元发出的。因此,在控制器的组成部件中,最核心也是最复杂的部分就是控制单元(CU,Control Unit)。

对于控制单元的实现方式不唯一,一种叫硬布线方式,一种叫微程序方式。基于两种方式构造的控制器分别为硬布线控制器微程序控制器

控制器是计算机系统的指挥中心,主要功能有:

  1. 从主存中取一条指令,并指出下一条指令在主存中的位置。
  2. 对指令进行译码或测试,产生相应的操作控制信号。
  3. 指挥并控制CPU、主存、输入和输出设备之间的数据流动方向。

5.4.1 简单的CPU模型

image-20250626154312026

image-20250626154710939

image-20250626155029136

image-20250626155037167

image-20250626155047003

5.4.2 硬布线控制器

硬布线控制器的基本原理

硬布线控制器也称组合逻辑控制器,既可以结合传统三级时序系统实现,也可结合现代时序系统实现。

主要包括指令寄存器指令译码器时序发生器硬布线控制器组合逻辑单元等,其中硬布线控制器组合逻辑单元用于产生指令执行所需要的所有控制信号序列,是控制器的核心。

image-20250626160952168

控制单元CU的输入信号来源如下:

  1. 指令译码器产生的输出$l_1-l_m$。对于n位的OP,经过指令译码可以产生$2^n$种输出。
  2. 时序系统产生的机器周期信号$M_1-M_i$和节拍信号$T_1-T_k$。
  3. 来自执行单元以及I/O总线的反馈信息$B_1-B_j$,例如ZF、OF、SF、CF等标志位信息。

硬布线控制器的输出信号就是微操作控制序号序列,这些控制信号可以看做所有输入信号的逻辑函数:

$C_n=\sum_{m,i,k,j}(I_m\cdot M_i\cdot T_k\cdot B_j)$

也就是说,每个控制信号都是由不同的指令译码信号机器周期信号节拍信号反馈信号构成的逻辑与操作的和。

硬布线控制器的设计流程

硬布线控制器的一般设计流程如下:

  1. 列出每条指令执行过程的微操作。
  2. 将每条指令的每个微操作分配到具体机器周期的具体时间节拍上,即对操作控制信号进行同步控制。
  3. 根据控制信号同步控制方式构造合适的时序发生器。
  4. 对每一个控制信号进行逻辑综合,得到每个控制信号的逻辑表达式。
  5. 采用逻辑门实现逻辑表达式。
  • 微操作的节拍安排

假设机器采用同步控制,每个机器周期包括3个节拍。

image-20250626163330478

同步控制方式固定时序控制方式,各项操作都由统一的时序信号控制,在每个机器周期中产生统一数目的节拍。

异步控制方式可变时序控制方式,各项操作不采用统一的时序信号控制,这是一种应答方式,由前一项操作已完成的结束信号或下一项操作准备好的准备好信号来作为下一项操作的起始信号。

image-20250626163859503

安排微操作节拍应该注意:

  1. 有些微操作的次序不容改变,必须注意微操作的先后顺序。
  2. 凡是被控制对象不同的微操作,若能在一个节拍内执行,应尽可能安排在同一个节拍内,以节省时间。
  3. 如果有些微操作所占的时间不长,应该将他们安排在一个节拍内完成,并允许这些微操作有先后次序。
  • 取指周期的节拍安排

取指周期的操作是公共操作,这些操作可以安排在3个节拍中完成。

image-20250626163905888

指令译码周期较短,也可以将OP(IR)->ID安排在$T_2$节拍内。

  • 间址周期微操作的节拍安排

间址周期完成取操作数有效地址的任务,具体操作如下:

  1. 将指令的地址码部分送至MAR,记作(MDR)->MAR
  2. 向主存发读命令,启动主存读操作,记作Read
  3. 将MAR所指的主存单元中的内容(即有效地址)经数据总线读至MDR,记作M(MAR)->MDR
  4. 将有效地址送至存储器地址寄存器(MAR),记作(MDR)->MAR

image-20250626164352890

  • 执行周期的节拍安排

非访存指令在执行周期只有一个微操作,按同步控制的原则,此操作可以安排在任一节拍内,其他节拍空。

image-20250626165534194

访存指令则需要访问存储器。

image-20250626165556891

  • 转移类指令

转移类指令在执行周期也不访问存储器。

image-20250626170350322

中断周期比较复杂,不考虑。

根据上述微操作的节拍安排,列出微操作命令的操作时间表,然后写出每一个微操作命令的逻辑表达式,最后根据逻辑表达式画出相应的组合逻辑电路图。

  • 列出微操作命令的操作时间表

image-20250626170807815

image-20250626170818644

  • 进行微操作信号综合

列出操作时间表后,即可对他们进行综合分析、归类,根据微操作时间表可以写出各微操作控制信号的逻辑表达式。

image-20250626172507639

  • 画出微操作命令的逻辑图

根据逻辑表达式可以画出对应每一个微操作信号的逻辑电路图,并用逻辑门实现。

image-20250626172600485

优点:硬布线的控制功能一般由逻辑门组合实现,速度主要取决于电路延迟,因此高速计算机中的关键核心部分CPU往往采用硬布线逻辑实现,因此RISC一般选用硬布线控制器

缺点:硬布线控制器的控制信号先用逻辑式列出,经化简后用电路来实现,因此显得零乱复杂,当需要修改或增加指令时就必须重新设计电路,非常麻烦。而且指令系统功能越全,微操作命令就越多,电路也就越复杂,调试也就越困难。为了克服这些缺点,便产生了微程序设计方法

5.4.3 微程序控制器

微程序控制的基本概念

控制器向执行部件发出的各种命令称为微命令,执行部件收到微命令后进行的操作称为微操作微命令和微操作一一对应

微程序的设计思想就是将每条机器指令编写成一个微程序,每个微程序包含若干微指令,每条微指令对应一个或几个微操作命令。执行一条指令的过程就是执行一个微程序的过程,这些微程序存储在一个控制存储器中。目前大多数计算机都采用微程序设计技术。

image-20250626173402888

硬布线控制器和微程序控制器中均有微命令微操作的概念,并非微程序控制器专属。但是微指令为微程序控制器专属。

微程序的基本组成

一个微程序的基本结构主要包括:

  1. 起始和转移地址形成部件(或简称微地址形成部件)。用于产生初始和后继微地址,以保证微指令的连续执行。
  2. 微程序计数器(uPC)。接受微地址形成部件传来的微地址,为后续读取微指令做准备。(类似PC)
  3. 控制存储器(CM,也称CS)。它是微程序控制器的核心部件,用于存放各指令对应的微程序。(类似主存)
  4. 微指令寄存器(uIR),其位数等于微指令字长。(类似IR)

image-20250626183345760

任何机器指令的取指令操作都是相同的,因此可将取指令操作的微指令统一编成一个微程序,这个微程序只负责将指令从主存单元中取出并送至指令寄存器

此外也可编写出对应间址周期的微程序和中断周期的微程序。这样,控制存储器中的微程序个数应为机器指令数再加上对应取指、间址和中断周期等公共的微程序数

任何一条指令,逻辑上分为多个阶段,但是在具体实现时,实际上只要实现其执行阶段,其他阶段都是公共的。

微程序控制器的工作过程

一条指令的执行实际上就是在微程序控制器的控制下计算机执行机器指令的过程,这个过程可以描述为:

  1. 最开始IR寄存器为空,还未取指令。
    image-20250626184243706
  2. 执行取指令公共操作。在机器开始运行时,自动将取微指令的入口地址送入uPC,并从CM中读出相应的微指令并送入uIR。取指微程序的入口地址一般为CM的0号单元。
    image-20250626184405428
  3. 一次将取指微程序的多条微指令取出并执行完成,从主存中取出的机器指令ADD存入指令寄存器中。
    image-20250626184443506
  4. 机器指令的操作码字段,通过微地址形成部件产生该机器指令所对应的微程序的入口地址,并送入uPC。
    image-20250626184530801
  5. 从CM中逐条取出对应的微命令并执行。
    image-20250626184601685
  6. 执行完对应于一条机器指令的一个微程序后,又回到取指微程序的入口地址,继续第1步,以完成取下一条机器指令的公共操作。
    image-20250626184705905

总结:

  1. 执行取指令公共操作。在机器开始运行时,自动将取微指令的入口地址送入uPC,并从CM中读出相应的微指令并送入uIR。取指微程序的入口地址一般为CM的0号单元,取指微程序执行完成后,从主存中取出的机器指令就已经存入指令寄存器中。
  2. 由机器指令的操作码字段,通过微地址形成部件产生该机器指令所对应的微程序的入口地址,并送入uPC。
  3. 从CM中逐条取出对应的微命令并执行。
  4. 执行完对应于一条机器指令的一个微程序后,又回到取指微程序的入口地址,继续第1步,以完成取下一条机器指令的公共操作。
  • 微指令与微周期

微指令是若干微命令的集合,一条微指令通常至少包含两大部分信息:

  1. 操作控制字段,也称微操作码字段,用于产生某一步操作所需的各种操作控制信号。
  2. 顺序控制字段,也称微地址码字段,用于控制产生下一条将要执行的微指令地址。

微周期是指从存储控制器中取出并执行一条微指令所需的全部时间,通常为一个时钟周期。

  • 主存储器与控制存储器

主存储器用于存放程序和数据,在CPU外部,用RAM实现。

控制存储器CM用于存放微程序,在CPU内部,用ROM实现。

  • 程序与微程序

微程序和程序是两个不同的概念。

程序是指令的有序集合,用于完成特定的功能

微程序是微指令的有序集合,用于描述机器指令,一条指令的功能由一段微程序实现。

微程序实际上是机器指令的实时解释器,是由计算机设计者事先编制好并存放在控制存储器中的,一般不提供给用户。对于程序员来说,系统中的微程序的结构和功能是透明的。

程序最终由机器指令组成,并且由软件设计人员事先编制好并存放在主存储器或者辅助存储器中。

微指令的编码方式

微指令的编码方式也称微指令的控制方式,是指如何对微指令的控制字段进行编码,以形成控制信号。编码的目标是在保证速度的情况下,尽量缩短微指令字长。

  • 直接编码(直接控制)方式

直接编码法无需进行译码,微指令的操作控制字段中每一位都代表一个微命令。

设计微指令时,选用或不选用微命令只需要将对应位设置为1/0。

每个微命令对应并控制数据通路中的一个微操作。

image-20250626192000626

优点:简单、直观,执行速度快,操作并行性好。

缺点:微指令字长过长,n个微命令就要求微指令的操作字段有n位,造成控制存储器容量极大。

  • 字段直接编码方式

字段直接编码方式将微指令的操作控制字段分成若干小字段,把互斥性微命令放在同一字段中,把相容性命令放在不同字段中,每个字段统一编码,每种编码代表一个微命令且各字段编码含义单独定义,与其他字段无关。

image-20250626192310915

假设总共7个微命令互斥,则只需要编码成3位字段就行,通过一个3/8译码器就可以将3位输入信号转换为8根输出信号中的一根有效信号。这种方式可以缩短微指令字长,但是要通过译码电路再发出微命令,因此比直接编码方式要慢

微命令字段分段的原则:

  1. 互斥性微命令分在同一段内,相容性微命令分在不同段内。
  2. 每个小段中包含的信息位不能太多,否则将增加译码电路的复杂性和译码时间。
  3. 一般每个小段还要留出一个状态,表示本字段不发出任何微命令。
  • 字段间接编码方式

一个字段的某些微命令需由另一个字段中的某些微命令来解释,由于不是靠字段直接译码发出的微命令,因此称为字段间接编码,也称为隐式编码。这种方式可进一步缩短微指令字长,但因削弱了微指令的并行控制能力,因此作为字段直接编码方式的一种辅助手段。

image-20250626193446003

  • 最短编码法

直接控制法使微指令字过长,而最短编码法使得微指令字最短。它将所有的微命令同一编码,每条微指令只定义一个微命令。若微命令总数为N,操作控制字段的长度为L,则最短编码法应满足:$L>=log_2N$。

最短编码法的微指令字长最短,但是要通过一个微命令译码器译码以后才能得到需要的微命令。微命令数目越多,译码器就越复杂。

这种方法在同一时刻只能产生一个微命令,不能充分利用机器硬件所具有的并行性,使得机器指令对应的微程序变得很长,而且对于某些要求在同一时刻同时动作的组合型微操作将无法实现。

微地址的形成方式
  • 增量方式(计数器法)

使用一个专门的微程序计数器uPC,将下条微指令的地址隐含地放在uPC中。

顺序执行时,根据uPC+1->uPC,获得下一条微指令地址。

转移执行时,在当前微指令后添加一条转移微指令,并在微指令中添加专门的转移控制字段,将转移微指令或转移控制字段中的控制信息送到微指令地址发生器,与相应的指令操作码和条件码等组合,生成转移地址送uPC。

当转移分支很多时,相应微地址生成逻辑电路很复杂。

image-20250626195712351

  • 断定法(下地址法)

计数器法的缺点是必须在不连续执行的微指令之间加入转移微指令,这样,在增加微指令条数的同时,还严重影响指令执行速度。

如果微指令直接明确制定下一条微指令地址,这样相当于每条都是转移微指令。

断定法加快了指令执行速度,但是增加了微指令的长度,从而影响控制存储器的有效利用。

image-20250626200134756

优点:消除了专门的转移微指令,而且在给微指令分配地址时不需要考虑如何排列,也不需要对uPC增量,而用一个简单的微指令地址寄存器uAR来存放当前微地址。

微指令的格式

微指令格式与微指令的编码方式有关,通常为水平型微指令和垂直型微指令两种。

  • 水平型微指令

从编码方式看,直接编码、字段直接编码和字段间接编码都属于水平型微指令,指令字中的一位对应一个控制信号,有输出时为1,否则为0。一条水平型微指令定义并执行多个并行操作的微命令

image-20250626200735461

优点:微程序短,并行能力强,执行速度快。

缺点:微指令长,编写微程序较麻烦。

  • 垂直型微指令

采用类似机器指令操作码的方式,在微指令字中设置微操作码字段,通常只能定义并执行一种微命令。

image-20250626200952814

优点:微指令短、简单规整,便于编写微程序。

缺点:微程序长,执行速度慢,效率低。

水平型和垂直型微指令比较:

  1. 水平型微指令并行能力强、效率高、灵活性强;垂直型微指令较差。
  2. 水平型微指令执行一条指令的时间短;垂直型微指令执行的时间长。
  3. 用水平型微指令编写的微程序,微指令字较长但微程序短;垂直型微指令正好相反。
  4. 水平型微指令难以掌握;而垂直型微指令与机器指令比较相似,相对容易掌握。
硬布线和微程序控制器的特点
  • 硬布线控制器的特点

优点:由于控制器的速度取决于电路延迟,所以速度快;

缺点:由于将控制器视为专门产生时序控制信号的逻辑电路,所以把最少元件和取得最高速度作为设计目标,一旦设计完成,就不可能通过其他额外修改添加新功能。

  • 微程序控制器的特点

优点:具有规整性、灵活性和可维护性

缺点:由于微程序控制器采用了存储程序原理,所以每条微指令都要从控制存储器中取一次,影响速度。

image-20250626202016064

5.5 异常和中断机制

5.5.1 异常和中断的基本概念

中断CPU暂停正在执行的程序,转去执行处理终端事件的中断服务程序,等待执行完后再返回中断的程序继续执行。

由CPU内部产生的意外事件叫做异常,也叫做内中断。由CPU外部的设备向CPU发出的中断请求称为中断,也称为外中断

异常和中断响应过程

CPU中断响应的过程分为关中断保存断电和程序状态识别异常和中断并转到响应的处理程序

  • 关中断

在保存断点和程序状态期间,不能被新的中断大端,因此要禁止响应新的中断,即关中断。通常通过设置“中断允许(IF)”标志位来实现。

IF为1,则为开中断,表示允许响应中断。IF为0,则为关中断,表示不允许响应中断。

  • 保存断点和程序状态

将程序的断点(返回地址)送到栈或者特定的寄存器中去,中断处理后还要回到被中断的程序继续执行,被中断时的PSW内容也要保存在栈或者特定的寄存器中,中断返回时恢复到PSW中。

  • 判断中断源,转向中断服务程序

不同设备有不同的中断服务程序,每个服务程序都有一个入口地址,CPU必须找到这个入口地址,即中断向量,将其送入PC中,这样CPU就能执行中断处理程序。

image-20250628155903070

  • 执行中断服务程序

不同中断源的中断服务程序不同,具体的中断处理工作在此程序中实现。

通常,单重中断下的中断处理程序如下所示:

  1. 保护现场。
  2. 执行中断处理逻辑相关的指令。
  3. 恢复现场。
  4. 开中断。
  5. 执行中断返回指令,该指令会恢复断电和部分寄存器,返回原程序执行。

image-20250628160300266

异常和中断的分类
  • 根据异常事件来源的不同

内部中断是指在引起故障的指令启动后、执行结束前被检测到的异常事件,例如缺页、除0等。

外部中断是指来自CPU外部的中断,中断源必须是某个硬件,所以又称为硬件中断。CPU有两根引脚用来接受外部中断源发出的中断控制信号,分别是可屏蔽中断请求线INTR以及不可屏蔽中断请求线NMI

image-20250628155903070

image-20250628164043147

  • 按中断请求是否可屏蔽

可屏蔽中断:通过INTR向CPU发出中断请求,在关中断情况下不会被响应,属于硬件中断。

非屏蔽中断:通过NMI向CPU发出中断请求,通常是非常紧急的硬件故障,这类中断请求信号不可被屏蔽,即使在关中断也会被响应,属于硬件中断。

  • 按中断过程是否能被打断

单重中断:CPU执行中断服务程序过程中不能被其他中断请求打断的中断。

多重中断:CPU执行中断服务程序过程中可以去响应更高优先级的中断请求的中断,又称为中断嵌套

  • 能否直接提供中断服务地址

向量中断:指中断事件可以提供中断服务入口地址的中断。

非向量中断:指中断事件不能直接提供中断服务程序入口地址的中断。

中断本质上和异常一样,它们的不同点在于:

  1. “缺页”“溢出”等异常事件是在特定指令执行过程中产生的,而中断不和任何指令关联,也不阻止任何指令的完成。
  2. 异常的检测由CPU自身完成,不必通过外部的某个信号通知CPU。对于中断,CPU必须通过中断请求线获取中断源的信息,才能知道哪个设备发生了何种中断。所有的异常和中断时间都是由硬件检测发现的。

5.6 指令流水线

5.6.1 指令流水线的基本概念

基本概念

单周期处理器和多周期处理器的指令执行都是串行方式,这种方式没有充分利用执行部件的并行性。指令的执行也可以采用流水线方式,将多条指令的执行相互重叠起来,以提高CPU执行指令的效率。

计算机中的流水线技术是把一个复杂的任务分解为若干个阶段,每个阶段与其他阶段并行处理。RISC都采用流水线技术,大部分CISC也采用流水线处理技术。

可以从两方面提高计算机内部的并行性:

一方面是采用时间上的并行性处理技术,通过将一个任务划分成几个不同的子过程,并且各子过程在不同的功能部件上并行执行,使在同一个时刻能同时执行多个任务,从而提升系统性能,这种方法即为流水线处理技术。

image-20250628171246783

另一方面是采用空间上的并行性处理技术,即在一个处理机内设置多个执行相同功能的独立操作部件,并且让这些操作部件并行工作,这种处理机被称为多操作部件处理机超标量处理机

MIPS指令流水线

MIPS是典型的RISC指令系统,其内部通常采用指令流水线结构。

以MIPS32指令系统为例,其指令的执行过程通常分为5个阶段,每个阶段由对应的功能部件完成,每个阶段也称为功能段

  1. 取指令(IF):负责从指令存储器中取出指令。
  2. 指令译码(ID):操作控制器对指令字进行译码,同时从寄存器堆中取操作数。(x86架构下往往在执行阶段才会取操作数,注意区别)
  3. 指令执行(EX):执行运算操作或计算地址。
  4. 访存(MEM):对存储器进行读写操作。
  5. 写回(WB):将指令执行结果写回寄存器堆。

在MIPS单周期中,这五个功能段是串行连接在一起的。单周期CPU的时钟频率取决于数据通路中的关键路径(最长路径),也就是五个阶段的延迟总和$T_{total}$所以单周期CPU执行效率不佳。

image-20250628171823112

MIPS流水线在每个执行阶段的后面都要增加一个流水寄存器,用于锁存本段处理完成的所有数据或结果,以保证本段执行结果能在下一个时钟周期给下一个阶段使用。

image-20250628172043041

程序计数器、流水寄存器均采用统一的公共时钟进行同步,每来一个时钟,各组合逻辑功能部件处理完成的数据都将锁存到段位的流水寄存器中,作为后段的输入。同时当前段也会接收到前段流水线寄存器传递过来的新指令或新数据。

image-20250628172233702

当第一条指令进入WB后,流水线各段都包含一条不同的指令,流水线中将同时存在5条不同的指令并行执行。

理想情况下,每隔一个时钟周期,都会有一条新的指令进入流水线,同时也会有一条指令执行完毕流出流水线。

5.6.2 流水线的性能指标

吞吐率

吞吐率(TP),指的是单位时间内流水线完成的任务数(指令数)或输出的结果数

$TP=\frac{n}{T_k}$

其中,n是任务数,$T_k$是完成n个任务所需的时间。

image-20250628182848722

假设5个功能段的最大延迟时间为$T_{max}$,则指令流水线时钟周期至少为$T_{max}$,执行n条指令需要的时间为$(n+4)T_{max}$。如果考虑流水线寄存器的延迟,则时钟周期大于$T_{max}$。

$TP=\frac{n}{T_{max}*(n+4)}$

image-20250628183116234

指令执行的CPI为$\frac{n+4}{n}$,当n较大时,流水线的CPI约等于1。

加速比

流水线的加速比指的是不使用流水下的情况下,n个任务顺序执行的总用时T比上使用流水线下n个任务的总用时T’。

$S_p=\frac{T}{T’}=\frac{n\times m\times\Delta t}{m\times \Delta t+(n-1)\times \Delta t}=\frac{n\times m}{m+n-1}$

其中,$\Delta t=T_{max}$,$T’$是使用m段流水线下,n个任务的执行总用时。当$n\rightarrow \infty$时,$S_p=m$

时空图表示

img-20250628685fd605c6097

单周期的CPU由于各功能段完全串联,无法进行并行处理,指令周期为5T。

image-20250628195022262

image-20250628195056809

5.6.3 MIPS架构的流水线数据通路

image-20250628201450838

5.6.4 五段式流水线的常见指令

MIPS中只有load以及store允许访存,访存属于I型指令(立即数型)。

image-20250628201732924

I型指令中,对于访存指令,将rs的内容和立即数符号扩展后的内容相加的结果作为内存单元地址进行loadstore

MIPS中包含了R型指令(寄存器型)算逻运算类指令。

image-20250628201913932

以及I型算逻运算类指令。

image-20250628201942682

MIPS中还包含了条件分支指令,属于I型指令。

image-20250628202013908

在指令流水线中,不同指令使用的数据通路不一样。

image-20250628202052934

但是需要注意的事,所有指令都需要完整经过流水线的各功能段,只不过某些指令在某些功能段内没有任何实质性的操作,只是等待一个时钟周期。

5.6.5 流水线冒险与处理

流水线流水过程可能会出现由于指令无法正确进行而造成的堵塞,这种现象称为流水线冒险。根据冒险原因不同分为结构冒险(也称资源冲突)、数据冒险控制冒险

结构冒险

结构冒险又称为资源冲突/相关,指的是多条指令进入流水线后在同一时间周期内争用同一个功能部件所发生的冲突。

image-20250628210024739

解决结构冲突有两种办法:

  1. 前一指令访存时,使后一条相关的指令(及其后续指令)暂停一个时钟周期。
  2. 设置多个独立的部件,对于寄存器访问冲突,可以将寄存器的读写口分离;对于访存冲突,单独设置数据存储器和指令存储器。在现代Cache中,L1级Cache通常采用数据Cache和指令Cache分离的方式,也就避免了资源冲突的发生。
数据冒险(重点)

当前指令要用到先前指令的结果,而这个结果未产生或未送到指定的位置,会导致当前指令无法继续执行,称为数据冒险/冲突/相关

  • 写后读(RAW,Read After Write)冲突

当按照流水线方式运行的时候,指令l2要用到l1执行的结果,在l1写回之前,l2就读取了旧的值,因此会导致读数据错误。

若l2的源操作数是l1的目的操作数,则该数据冲突称为写后读冲突

image-20250628211107898

在按序执行的基本流水线中,所有数据冒险都是写后读(RAW)数据冒险。但是在乱序情况下,RAW、WAR、WAW情况都可能出现。

写后读数据冒险的解决措施:

插入空操作指令,在软件上插入“nop”指令。

image-20250628211518176

插入气泡,通过硬件阻塞(stall)方式组织后续指令执行。气泡(Bubble)或停顿(Stall)是指流水线的一个时钟周期,在该周期内流水线并没有执行任何有用的工作。

image-20250628211526937

采用转发技术,将数据通路中生成的中间数据直接转发到ALU的输入端,这种技术也称为数据旁路技术。

image-20250628211624106

  • 读后写(WAR,Write After Read)冲突

在乱序调度的流水线中,l2可能会先执行,使l1在读取r2寄存器的值之前,l2就已经写入了新的内容,这会导致l1读数据出错,l2的目的操作数是l1的源操作数,这种冲突被称为读后写冲突

image-20250628211935855

  • 写后写(WAW,Write After Write)冲突

在乱序调度的流水线中,l2的写操作可能发生在l1的写操作之前,从而发生顺序错误,最终写入r3的内容是l1执行的结果,l1和l2具有相同的目的操作数,称为写后写冲突

image-20250628212115989

load-use数据冒险的处理

load指令只有在MEM段结束时才能得到贮存中的结果,在WB段的前半周期才能存入R2的值,但随后的add指令在EX阶段就要取R2的值,因此得到的是旧值。

如果load和其后紧邻的运算类指令存在数据相关问题,则无法通过转发技术来解决,称为load-use数据冒险

image-20250628212555985

最简单的做法是由编译器在add指令之前插入一条nop指令。最好的办法是在程序编译时进行优化,通过调整指令顺序以避免出现load-use现象。

image-20250628212602603

控制冒险

遇到改变指令执行顺序时,例如执行转移或返回指令时,会改变PC值,造成断流,也称为控制冲突

image-20250628212618266

image-20250628212623937

解决控制冲突的办法:

  1. 对于由转移指令引起的冲突,可采用和解决数据冲突相同的软件插入“nop”指令和硬件阻塞的方法。延迟损失多少时间片,就插入多少条nop指令。
  2. 对转移指令进行分支预测,尽早生成转移地址,分为简单/静态预测动态预测
    • 简单预测假定分支总是不发生或者发生,每次预测结构是一样的。
    • 动态预测根据程序转移的历史情况,进行动态预测调整,有较高的预测准确率。

5.6.6 高级流水线技术

有两种增加指令级并行的策略:一种是多发射技术,它通过增加流水线级数来使更多的指令同时在流水线中重叠执行。另一种超流水线技术,它通过采用多个内部功能插件,使流水线功能段能同时处理多条指令,处理机一次可以发射多条指令进入流水线执行。

超流水线技术

超流水线技术主要通过增加流水线功能段数目,尽可能减少各段关键延迟时间,从而提高流水线主频的方式来提升流水线性能。采用超流水线技术的CPU在流水线充满后每隔一个时钟周期可以执行一条机器指令,CPI=1,但主频更高。

image-20250628214952244

但是流水线级数越多,用于流水寄存器的开销越大,因此流水线级数是有限制的。

多发射技术

多发射技术类似工业流水线中增加产能的方式,通过复制内部功能部件,使各流水线功能段能同时处理多条指令,处理器一次可以发射多条指令到流水线中进行处理。

使用一个三发射处理器,如果采用3段流水线,流水线充满后同一时刻流水线会有9条指令并行执行,CPI<1。但是这必然会引起更多冒险问题。

超长指令字技术

如果将这些冲突全部交给编译器静态解决,就是静态多发射技术,如传统的超长指令字技术,由编译器将多条无相关的常规指令存储在一个超长的指令字中,将他们同时发射到流水线中处理。

这种方式硬件不用处理冲突,完全由编译器处理,生成的代码可移植性较差,程序从一个处理器移植到另一个处理器上运行时,可能需要重新编译。

image-20250628215556499

动态多发射技术

动态多发射技术由硬件动态处理多发射流水线运行过程中出现的各种冲突,也称为超标量流水线技术

5.7 多处理器

5.7.1 Flynn分类(理解)

传统单处理器系统的串行计算方式不能满足实际算力要求。

并行系统可以从不同角度划分,包括按指令和数据的处理方式划分,按地址空间访问方式划分等角度。

按指令和数据的处理方式划分的Flynn分类法分为以下四种。

SISD

单指令流单数据流(SISD,Single Instruction stream Single Data stream)的计算机通常仅包含一个处理器和存储器,按指定规定的顺序串行完成若干指令的执行。为了提高速度,有些SISD采用流水线的方式,可能会设置多个功能部件,并采用多模块交叉方式组织存储器。

SIMD

单指令流多数据流(SIMD,Single Instruction stream Multiple Data stream)是指一条指令同时处理多个数据流,通常是一个指令控制部件、多个处理单元和多个存储器组成。各处理单元和各存储器之间通过系统内部的互联网络进行通信。

程序执行时,指令控制部件执行的仍然是一个串行的指令流,所有处于执行状态的处理单元同时执行相同的指令,所需的数据从连接在各个处理单元上专用的存储器取得。每个单元都有自己的地址寄存器,这样每个单元就都有不同的数据地址。因此,不同处理单元执行的同一条指令的所处理的数据是不同的。

image-20250629100147981

一个顺序应用被编译后,既可能按SISD组织并运行于串行硬件上,又可能按SIMD组织并运行于并行硬件上。

SIMD在使用for循环处理数组时非常有效;

SIMD在使用case或switch语句时效率最低,此时每个执行单元必须根据不同的数据执行不同的操作。

image-20250629100527855

多指令单数据流

MISD是指同一时刻由多个指令在执行,并且处理的是同一种数据,实际上不存在这种计算机。

多指令多数据流

MIMD是指同时有多个指令分别处理不同的数据。MIMD分为多计算机系统多处理器系统。MIMD是目前大多数并行处理计算系统的处理方式

多计算机系统中的每台计算机节点都有各自私有的存储器,并具有独立的主存地址空间,不能通过存取指令来访问不同节点的私有存储器,而要通过消息进行数据传递,称为消息传递MIMD

多处理器系统是共享存储多处理器(SMP)系统的简称,它通过存取指令来访问系统中的所有存储器,也称为共享存储MIMD

总结

根据Flynn分类法,并行计算系统由SIMD和MIMD两种并行计算模式。

其中,SIMD适用于在一条指令下同时处理多个数据元素,适合处理数据密集型任务

MIMD适用于多个处理单元独立执行不同的指令序列和数据处理,适合处理更复杂的并行计算任务

5.7.2 硬件多线程(理解)

不支持硬件多线程的超标量处理器中,指令发射槽的使用收到指令级并行性的限制。而绝大多数阻塞会使整个处理器空闲。

硬件多线程用于实现在线程阻塞时切换到另一个线程

在支持硬件多线程的CPU中,必须为每个线程提供单独的通用寄存器组、单独的程序计数器等,线程的切换只需要激活选中的寄存器,从而省略了和存储器数据交换的环节,大大减少线程切换的开销。

细粒度多线程

多个线程轮流交叉执行指令,多个线程之间的指令相关,可以乱序执行。处理器在每个时钟周期切换线程。线程的交叉执行几乎不会出现发射槽全空的情况,但是指令级并行的限制还是会导致某些时钟周期出现空闲发射槽。

粗粒度多线程

仅在当前线程出现了较大开销的阻塞时,才切换线程。新线程的指令开始执行前需要重载流水线,线程切换的开销比细粒度大

以上两种多线程技术都实现了指令级并行,线程级不并行。

同时多线程

同时多线程(SMT)是上述两种多线程技术的变体。它实现了线程级并行,在同一个时钟周期中,发射多个不同线程中的多条指令执行。

多发射处理器中通常有单线程难以充分利用的多个并行单元。在SMT中,线程级并行和指令级并行都得到充分利用,在一个时钟多个线程共同使用发射槽,大大减少了空闲周期。

image-20250629104532156

5.7.3 多核处理器(理解)

多核处理器是指将多个处理单元集成到单个CPU中,每个处理单元称为一个,通常也成片上多处理器。每个核既可以有自己的Cache,又可以共享同一个Cache,所有核共享主存储器。

image-20250629104813312

在多核计算机系统中,如果想充分发挥硬件性能,必须采用多线程/多进程执行,使得每个核在同一时刻都有线程在执行。

与单核上的多线程不同,多核上的多个线程是在物理意义上并行执行的,是真正意义上的并行执行,在同一时刻有多个线程在并行执行。而单核上的多线程是一种多线程交错执行,实际上同一时刻只有一个线程在执行

共享内存多处理器的基本概念(了解)

具有共享的单一物理地址空间的多处理器被称为共享内存多处理器(SMP)。处理器通过存储器中的共享变量互相通信,所有的处理器都能通过存取指令访问存储器的任何位置。这些系统共享同一个物理地址空间,它们仍然可以在自己的虚拟地址空间中单独地运行程序。

  • 统一存储访问(UMA)多处理器

每个处理器对所有存储单元的访问时间是大致相同的,访问时间与哪个处理器提出访存请求以及访问哪个字无关。

  • 非统一访问(NUMA)多处理器

某些处理器的访存速度要比其他的快,具体取决于哪个处理器提出访问请求以及访问哪个字,这是由于主存被分割给了不同处理器。

早期的计算机,内存控制器没有整合进CPU。访存需要经过北桥芯片(集成了内存控制器),CPU通过前端总线和北桥芯片相连,这就是UMA框架。

随着越来越多CPU对前端总线的争用使得前端总线称为瓶颈,NUMA框架诞生,内存控制器被集成到CPU内部,每个CPU都有独立的内存控制器,每个CPU都独立连接到一部分内存,和CPU直连的这部分内存被称为本地内存

CPU之间通过总线相连,可以通过总线访问其他CPU的远程内存

在NUMA架构下,内存的访问出现了本地和远程的区别,访问本地内存要明显快于访问远程内存。

在UMA架构下,所有CPU共享同一内存空间,每个CPU中的Cache都是共享内存的一部分副本,因此多核系统的Cache一致性既包括Cache和内存之间的一致性,还包括各CPU的Cache之间的一致性。对于内存同一位置的数据,不同CPU的Cache应该有一致的内容。

当前文章:计算机组成原理笔记-第五章-中央处理器
作者:ikuyo
暂无评论

发送评论 编辑评论

|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇