Skip to content

Single-Cycle Processor

Intro

指令执行的步骤概述:

  • 最开始的两步是相同的:
  1. Fetch 从指令内存中拿出指令

  2. Decode and Read Operands 解码并读寄存器

    • 在进行指令解码完成前就可以读取数据,因为数据位置是固定的,并且读数据不会破坏数据,所以无论是否使用,都会先拿出对应的register operands。
  3. Executive Control

    所有的指令都会经过ALU,根据指令的类型进行不同的ALU操作:

    • 内存引用指令:Memory Reference
    • 算术逻辑指令:Arithmetic and Logic
    • 条件分支指令:判断两数是否相等
  4. Memory Access

    通过load和store指令访问数据

    • 内存引用指令:访问内存进行读写
    • 算术逻辑指令/加载指令:将内存或者寄存器中的数据写入寄存器
    • 条件分支指令:根据比较结果更新PC:PC+4 或到指定目标地址
  5. Write results

  6. Update PC

simple processor

co-40

三个红色圆圈是多路选择器,因为需要对不同的数据来源进行选择,比如ALU中的输入既可以是R型指令中的两个源寄存器,也可以是I型指令中的一个源寄存器和一个立即数。

with control

co-41

Clock Methodology

Datapath

构建数据通路时,我们关心的是指令中的存储数据而不是控制信号。

Instruction Fetch

co-42

64bit寄存器,需要64个D触发器。

R Inst

co-43

  • Register File: 寄存器堆,用于存储所有寄存器
    • 进行选择的wire是5位,因为寄存器有32个。
    • 读取数据只需要进行选择(在R型指令前已经有ld指令将数据写入寄存器),写入数据需要受RegWrite信号控制
  • ALU: 进行算术逻辑运算
    • 有一个四位的MUX进行控制,但事实上只需要三位,有一位是浪费的(但我们必须使用四位,因为MUX的输入端口数必须是2的幂次)

Load Store Inst

除了R型指令用到的两个元件,还需要两个

co-44

  • Data Memory: 数据内存,不同于Register File,Data Memory同时具有读写控制
    • 读取数据ld需要受MemRead信号控制,写入数据sd需要受MemWrite信号控制
      • 内存的读取需要控制信号的原因是:并不是所有指令都会有访存操作,所以那些用不到内存值的指令就没必要读取内存数据,因此需要控制信号来关闭这扇门;而所有指令都要从寄存器堆里读取数据,因此寄存器堆的读取无需控制信号
  • Imm Gen: 从 32 位指令中提取出与立即数相关的位,将这些位按正确的顺序拼接起来,同时对其符号扩展至 64 位

Branch Inst

这里只考虑了beq指令

co-45

  • 寄存器用于存放两个被比较的源操作数,进入ALU进行比较
  • ALU:第一个ALU的作用是进行比较,并且有一个zero用于判断是否相等进而选择PC+4还是branch target address;第二个ALU只有加法,用于计算branch target address
  • Shift left 1:根据之前学到的,在进行branch target address计算时,我们所需的offset在立即数中是真实值的一半,因此对于Imm Gen输出的立即数,我们需要进行左移一位的操作乘2

Compose

  • 每个数据通路都是单周期的

datapath

co-46

All types' datapath

co-47

R-type 的指令是完全建立在Register File上的,没有与内存的交互,使用R指令一定是在内存读取后进行的。

co-48

co-49

co-50

co-51

Control Unit

只有7+4根信号

实际上只需要处理八个信号

8 signals

co-52

ALU Control

ALU的控制信号一共有4位:

  • 其中2位分别来自funct3和funct7
  • 另外2位是来自ALUop,它来自主控制单元 (main control unit),用于指定具体执行何种指令,不同的值对应不同的类型:
    • 00:加载/存储指令
    • 01:条件分支指令
    • 10:R型指令
ALU control
Instruction opcode ALUOp Operation Funct7 Funct3 Desired ALU action ALU control Input
ld 00 load doubleword XXXXXXX XXX add 0010
sd 00 store doubleword XXXXXXX XXX add 0010
beq 01 branch if equal XXXXXXX XXX subtract 0110
R-type 10 and 0000000 111 AND 0000
R-type 10 or 0000000 110 OR 0001
R-type 10 add 0000000 000 add 0010
R-type 10 sub 0100000 000 subtract 0110
R-type 10 slt 0000000 010 slt 0111
R-type 10 srl 0000000 101 srl 0101
R-type 10 xor 0000000 011 xor 0011

采用两级解码,根据opcode就可以判断7根信号线的值,这时候 ALU 再看funct3和funct7就可以判断出剩下的4根信号线的值。

co-53

Main Control Unit

余下的六个控制信号的作用如下:

co-55

  • ALUSrc: 决定ALU的第二个输入,0时从寄存器堆中读取数据,1时从立即数中读取数据
  • PCSrc: 决定PC的值,0时选择PC+4,1时选择branch target address
  • MemtoReg: 0时将ALU的结果返回给目标寄存器,1时将内存中的数据返回给目标寄存器

最终:

co-56


Operation of Datapath

灰色的元件代表没有被使用

co-57

add x1, x2, x3
  1. IF:取指,递增PC
  2. ID:解码,从寄存器堆中读取寄存器
  3. EX:执行,ALU根据操作码确定运算类型,然后对上一步的数据进行计算
  4. MEM:无
  5. WB:写回,将结果写入x1

co-58

ld x1, offset(x2)
  1. IF:取指,递增PC
  2. ID:从寄存器堆中读取x2
  3. EX:ALU计算x2的数据和经过imm符号扩展的offset的和,得到内存地址
  4. MEM:根据地址得到对应数据
  5. WB:将数据写入x1

co-59

beq x1, x2, offset
  1. IF:取指,递增PC
  2. ID:从寄存器堆中读取x1和x2
  3. EX:ALU将两个读取的数据相减,同时将PC与左移一位后的offset相加,得到branch target address,根据ALU的zero信号判断如何更新PC

Pipelining Process

流水线Pipeline的本质是提高吞吐量,通过将指令的执行过程分解为多个阶段,每个阶段在不同的时钟周期内执行,从而在每个时钟周期内完成多条指令的执行。

在RISC-V中,流水线Pipeline的实现方式是:

pipeline

  • IF: Instruction Fetch 从指令内存中取出指令
  • ID: Instruction Decode 解码指令
  • EX: Execute 执行指令
  • MEM: Memory Access 访问内存
  • WB: Write Back 写回寄存器

co-54

图形左半边阴影是写入,右半边阴影是读取,全阴影是都有(这里假设在一个时钟周期内,元件的前半个周期可以进行写操作,后半个周期可以进行读操作)

但是不是所有指令都需要执行所有阶段的指令的,比如load和store指令就不需要执行EX和MEM阶段。

性能方面:理想条件下

\[ \text{Time between instructions}_{pipelined} = \frac{\text{Time between instructions}_{non-pipelined}} {\text{Number of stages}} \]

Hazards

Structure Hazards

流水线问题导致存储器或者ALU访问冲突(内部资源使用冲突)

Data Hazards

需要等待上一条指令完成数据写入或者读取完成才能进行后续操作