Lecture 20: RISC-V Single-Cycle Control
Review
回顾
我们已经设计了一条完整的数据路径,该数据路径能够在每个周期内执行所有的RISC-V指令。这意味着处理器可以在单个时钟周期内完成一条指令的执行。然而,并不是所有指令都需要使用所有的硬件单元。每个指令的执行过程分为五个阶段:取指(IF)、译码(ID)、执行(EX)、存储器访问(MEM)和写回(WB)。我们还需要设计控制器,控制器的作用是指定如何执行每条指令。
单核处理器概览
我们设计的单核处理器包括两个主要部分:控制单元和数据路径。控制单元负责生成控制信号,以指导数据路径上的操作。数据路径包括程序计数器(PC)、寄存器文件、算术逻辑单元(ALU)等模块。处理器从内存中获取指令和数据,进行处理后将结果输出。
Single-Cycle RV32I Datapath and Control
单周期RV32I数据路径和控制
这张图展示了单周期RV32I处理器的完整数据路径和控制信号。每个模块和信号的功能如下:
缩写 | 英文全称 | 中文解释 |
---|---|---|
PCSel | Program Counter Select | 选择程序计数器(PC)的下一个值的来源,是PC + 4还是分支目标地址 |
ImmSel | Immediate Select | 选择立即数类型(I, S, B, J, U型) |
RegWEn | Register Write Enable | 使能寄存器写入 |
BrUn | Branch Unsigned | 无符号分支控制信号 |
BrLT | Branch Less Than | 分支条件标志,表示小于 |
BrEq | Branch Equal | 分支条件标志,表示等于 |
Bsel | ALU B Select | 选择ALU的第二操作数来源,是寄存器还是立即数 |
Asel | ALU A Select | 选择ALU的第一操作数来源,是PC还是寄存器 |
ALUSel | ALU Operation Select | 选择ALU的操作类型(加法、减法等) |
MemRW | Memory Read/Write | 控制数据存储器的读写操作 |
WBSel | Write Back Select | 选择写回数据的来源,是ALU结果还是内存数据 |
通过组合这些控制信号和数据路径模块,我们可以实现RISC-V指令集的所有指令。这个设计确保了每个指令在一个时钟周期内完成,从而实现了高效的指令执行。
Example: sw reg, offset(regbaseptr)
在这一例子中,我们分析sw
指令的执行过程。sw
指令的格式为:sw x14, 36(x2)
。这个指令将寄存器x14
中的数据存储到由寄存器x2
加上偏移量36所指定的内存地址中。
数据路径分析
- PC + 4:程序计数器(PC)在每次指令执行后递增4,以便指向下一条指令。
- 指令存储器(IMEM):从指令存储器中获取指令(inst[31:0]),并将其分解为多个部分(如rs1, rs2, imm等)。
- 立即数生成(Imm.Gen):生成立即数(imm[31:0]),用于计算存储地址。
- 寄存器文件(Reg[]):读取寄存器中的数据,分别通过rs1和rs2选择读取地址(Reg[rs1], Reg[rs2])。
- ALU:算术逻辑单元(ALU)计算存储地址(R[rs1] + imm)。
- 数据存储器(DMEM):将数据(R[rs2])存储到计算得到的地址中。
控制信号设置
- PCSel = pc + 4:选择下一条指令的地址为PC + 4。
- ImmSel = S:选择立即数类型为S型,即存储指令的立即数格式。
- RegWEn = 0:禁用寄存器写入,因为sw指令只是将数据存储到内存,不需要写回寄存器。
- BrUn, BrLT, BrEq:分支控制信号未用,保持默认值。
- Bsel = 1:选择ALU的输入B为立即数(imm)。
- Asel = 0:选择ALU的输入A为寄存器R[rs1]。
- ALUSel = add:ALU执行加法操作(R[rs1] + imm)。
- MemRW = Write:设置内存为写操作,将数据写入存储器。
- WBSel:未使用的信号保持默认值。
Example: beq reg1, reg2, Label
在这个例子中,我们分析beq
指令的执行过程。beq
指令的格式为:beq x1, x2, Label
。这个指令比较寄存器x1
和x2
的值,如果相等,则跳转到标签Label
指定的地址。
数据路径分析
- PC + 4:程序计数器(PC)在每次指令执行后递增4,以便指向下一条指令。
- 指令存储器(IMEM):从指令存储器中获取指令(inst[31:0]),并将其分解为多个部分(如rs1, rs2, imm等)。
- 立即数生成(Imm.Gen):生成立即数(imm[31:0]),用于计算分支地址。
- 寄存器文件(Reg[]):读取寄存器中的数据,分别通过rs1和rs2选择读取地址(Reg[rs1], Reg[rs2])。
- 分支比较器(Branch Comp):比较寄存器rs1和rs2的值,如果相等,则设置分支条件成立。
- ALU:算术逻辑单元(ALU)计算分支目标地址(PC + imm)。
- 数据存储器(DMEM):不涉及内存操作。
控制信号设置
- PCSel = BrEq:如果分支条件成立(R[rs1] == R[rs2]),则选择分支目标地址(PC + imm),否则选择PC + 4。
- ImmSel = B:选择立即数类型为B型,即分支指令的立即数格式。
- RegWEn = 0:禁用寄存器写入,因为beq指令只是条件跳转,不需要写回寄存器。
- BrUn, BrLT, BrEq:设置分支控制信号以确定分支条件是否成立。
- Bsel = 1:选择ALU的输入B为立即数(imm)。
- Asel = 1:选择ALU的输入A为PC。
- ALUSel = add:ALU执行加法操作(PC + imm)。
- MemRW:不涉及内存操作,保持默认值。
- WBSel:未使用的信号保持默认值。
Instruction Timing
指令时序
Example: add rd, reg1, reg2
数据路径分析
在这个例子中,我们分析add
指令的执行过程。add
指令的格式为:add rd, rs1, rs2
。这个指令将寄存器rs1
和rs2
中的值相加,并将结果存储到寄存器rd
中。
- 指令获取(IF):PC从程序计数器中读取,指向存储指令的内存地址。
- 指令译码(ID):从指令寄存器中提取出操作码、目标寄存器
rd
、源寄存器rs1
和rs2
。 - 读取寄存器值:从寄存器文件中读取寄存器
rs1
和rs2
的值。 - 执行(EX):ALU将
rs1
和rs2
的值相加。 - 写回(WB):将ALU的输出结果写回到目标寄存器
rd
。
控制信号设置
- PCSel:PC + 4,下一条指令地址。
- ImmSel:不适用,没有立即数。
- RegWEn:1,使能寄存器写入。
- BrUn, BrLT, BrEq:分支控制信号未用,保持默认值。
- Bsel = 0:选择ALU的输入B为寄存器R[rs2]。
- Asel = 0:选择ALU的输入A为寄存器R[rs1]。
- ALUSel = add:ALU执行加法操作(R[rs1] + R[rs2] )。
- MemRW:不涉及内存操作,保持默认值。
- WBsel:ALU的输出。
时序图分析
时序图展示了add
指令在单周期RV32I数据路径中的执行过程。每个信号在每个时钟周期中的变化如下:
- Clock:时钟信号,用于同步所有操作。
- PC:程序计数器,指向当前指令的地址。
- PC+4:下一条指令的地址。
- inst[31:0]:当前指令的32位二进制表示。
- Control logic:控制逻辑,生成控制信号以指引数据路径中的各个部件。
- Reg[rs1]:寄存器
rs1
的值。 - Reg[rs2]:寄存器
rs2
的值。 - alu:ALU的操作结果。
- wb:写回信号,表示将结果写回到寄存器文件。
- Reg[rd]:目标寄存器
rd
的值,在写回后更新。
add
指令执行过程
- IF阶段:PC加载当前指令地址,并从内存中读取指令。
- ID阶段:指令被译码,源寄存器
rs1
和rs2
的值从寄存器文件中读取。 - EX阶段:ALU将
rs1
和rs2
的值相加,结果输出到ALU。 - WB阶段:ALU的结果写回到目标寄存器
rd
。
指令时序
Example: add
Timing
数据路径分析
在这个例子中,我们分析add
指令的执行过程。add
指令的格式为:add rd, rs1, rs2
。这个指令将寄存器rs1
和rs2
中的值相加,并将结果存储到寄存器rd
中。
- 指令获取(IF):PC从程序计数器中读取,指向存储指令的内存地址。
- 指令译码(ID):从指令寄存器中提取出操作码、目标寄存器
rd
、源寄存器rs1
和rs2
。 - 读取寄存器值:从寄存器文件中读取寄存器
rs1
和rs2
的值。 - 执行(EX):ALU将
rs1
和rs2
的值相加。 - 写回(WB):将ALU的输出结果写回到目标寄存器
rd
。
关键路径
关键路径决定了整个指令执行的最大延迟。对于add
指令,关键路径为: \[ \text{Critical path} = t_{\text{clk-q}} + \max\{ { t_{\text{Add}} + t_{\text{mux}}, t_{\text{IMEM}} + t_{\text{Reg}} + t_{\text{mux}} + t_{\text{ALU}} + t_{\text{mux}} } \} + t_{\text{setup}} \]
\[ = t_{\text{clk-q}} + t_{\text{IMEM}} + t_{\text{Reg}} + t_{\text{mux}} + t_{\text{ALU}} + t_{\text{mux}}+ t_{\text{setup}} \]
Example: lw reg, offset(regbaseptr)
数据路径分析
在这个例子中,我们分析lw
指令的执行过程。lw
指令的格式为:lw rd, offset(rs1)
。这个指令将内存地址为rs1 + offset
处的值加载到寄存器rd
中。
- 指令获取(IF):PC从程序计数器中读取,指向存储指令的内存地址。
- 指令译码(ID):从指令寄存器中提取出操作码、目标寄存器
rd
、基地址寄存器rs1
和偏移量offset
。 - 计算地址:ALU计算基地址寄存器
rs1
和偏移量offset
的和。 - 内存访问(MEM):从内存中读取地址为
rs1 + offset
的数据。 - 写回(WB):将内存读取的数据写回到目标寄存器
rd
。
关键路径
通过这个时序图和关键路径分析,我们可以清晰地了解add
和lw
指令在单周期RV32I数据路径中的执行过程,以及每个时钟周期中各个信号和部件的状态变化。这种分析对于理解处理器的性能瓶颈和优化数据路径设计具有重要意义。
指令时序
时序图解析
下图展示了单周期处理器中指令的执行时序图,以及各个阶段所需的时间。
- 时钟周期(Clock):
- 每个时钟周期由上升沿和下降沿组成。
- 程序计数器(PC):
PC
的值在每个周期都会更新。PC
指向当前指令,新的PC
值(pc+4)指向下一条指令。
- 指令获取(IF):
- 从内存中取指令,并将其加载到指令寄存器中。
- 指令译码(ID):
- 解析指令,确定操作码和操作数。
- 从寄存器文件中读取操作数。
- 执行(EX):
- 在ALU中执行操作,计算结果。
- 内存访问(MEM):
- 访问数据内存(如果是
lw
或sw
指令)。
- 访问数据内存(如果是
- 写回(WB):
- 将结果写回寄存器文件。
各阶段所需时间
- 指令获取(IF):200 ps
- 指令译码(ID):100 ps
- 执行(EX):200 ps
- 内存访问(MEM):200 ps
- 写回(WB):100 ps
不同指令的执行时间
add
指令:- IF + ID + EX + WB = 200 + 100 + 200 + 100 = 600 ps
beq
指令:- IF + ID + EX = 200 + 100 + 200 = 500 ps
jal
指令:- IF + ID + EX = 200 + 100 + 200 = 500 ps
lw
指令:- IF + ID + EX + MEM + WB = 200 + 100 + 200 + 200 + 100 = 800 ps
sw
指令:- IF + ID + EX + MEM = 200 + 100 + 200 + 200 = 700 ps
时钟频率计算
- 最大时钟频率:\( f_{\text{max}} = \frac{1}{800 \text{ ps}} = 1.25 \text{ GHz} \)
- ALU最大频率:\( f_{\text{max, ALU}} = \frac{1}{200 \text{ ps}} = 5 \text{ GHz} \)
总结
- 单周期处理器的时钟周期由最长的指令决定,即
lw
指令,需800 ps。 - 大多数硬件单元在大部分时间都是空闲的。
- 了解指令的执行时间对于优化处理器性能和设计具有重要意义。这有助于识别瓶颈和潜在的改进点,尤其是考虑如何提高并行度或引入流水线来提升整体效率。
Control Logic Design
控制逻辑设计
控制逻辑真值表
控制逻辑真值表列出了处理器在执行不同指令时,各个控制信号的设置。下面是对各个字段的解释:
- Inst[31:0]:指令的位模式,表示不同的指令类型。
- BrEq:分支相等标志,当
R[rs1]
等于R[rs2]
时,该信号为1。 - BrLT:分支小于标志,当
R[rs1]
小于R[rs2]
时,该信号为1。 - PCSel:程序计数器选择信号,决定下一条指令的地址。
+4
表示程序计数器增加4。ALU
表示使用ALU的输出作为新的程序计数器值。
- ImmSel:立即数选择信号,决定从指令中提取哪部分作为立即数。
I
表示I-型立即数。S
表示S-型立即数。B
表示B-型立即数。J
表示J-型立即数。U
表示U-型立即数。
- BrUn:分支无符号控制信号,决定分支比较是无符号还是有符号。
- ASel:第一个ALU操作数选择信号,决定ALU的A输入。
- BSel:第二个ALU操作数选择信号,决定ALU的B输入。
- ALUSel:ALU操作选择信号,决定ALU执行的操作(加、减等)。
- MemRW:内存读写控制信号,决定是读内存还是写内存。
- RegWEn:寄存器写使能信号,决定是否将结果写入寄存器。
- WBSel:写回选择信号,决定写回寄存器的值来源于哪里。
Control Realization Options
控制实现选项
控制逻辑的实现有两种主要方式:
- 只读存储器(ROM):
- 具有规则结构,可以容易地重新编程。
- 用于手动设计控制逻辑时非常流行。
- 优点:可以轻松修复错误并添加新指令。
- 组合逻辑:
- 现代芯片设计师使用逻辑综合工具将真值表转换为门电路网络。
- 优点:可以自动生成高效的硬件实现。
扩展说明
在设计RISC-V处理器的控制逻辑时,工程师需要详细考虑每条指令的执行过程,并确保控制信号在正确的时间点被激活以驱动数据路径完成所需的操作。真值表是设计控制逻辑的重要工具,通过列出每条指令的控制信号,可以确保处理器的每个部分都在正确的时间执行正确的操作。
此外,选择合适的控制逻辑实现方式也是关键。对于简单的设计,可以使用ROM来实现控制逻辑,因为其结构简单且易于修改。然而,对于更复杂的设计,组合逻辑可能更为高效,因为它可以通过自动化工具生成更优化的硬件结构。
RV32I,一个九位的ISA!
指令编码
在RISC-V指令集架构(ISA)中,RV32I 指令类型的编码只需要9位。这9位分别是:
- inst[30]
- inst[14:12]
- inst[6:2]
这些位用于区分不同类型的指令,例如算术指令、逻辑指令、加载和存储指令、跳转指令等。在图中可以看到,这些位在指令格式中的位置和作用。
详细解释
- inst[30]: 用于区分一些指令的不同操作,例如加法(ADD)和减法(SUB)指令,这个位可以决定使用的是加法运算还是减法运算。
- inst[14:12]: 这3个位通常用于指示指令的功能码(funct3),决定ALU操作的具体类型,如加法、减法、逻辑运算等。
- inst[6:2]: 这5个位是操作码(opcode),用来标识指令类型,如算术运算、逻辑运算、加载、存储等。
通过这9位,处理器可以解码出指令的类型和具体操作,进而驱动数据路径完成指令的执行。
组合逻辑控制
最简单的例子:BrUn
在控制逻辑中,BrUn信号是最简单的例子之一。该信号用于指示分支比较是无符号还是有符号。具体来说,BrUn信号可以由inst[13]
和分支指令类型共同决定。
详细解释
- inst[13]: 在图中,我们可以看到这个位用于区分无符号和有符号比较。在某些分支指令中,这个位决定了是进行无符号比较还是有符号比较。
- Branch: 这是一个标志,用于指示当前指令是否为分支指令。只有在分支指令时,
BrUn
信号才有意义。
逻辑表达式
如何解码BrUn
信号: \[ \text{BrUn} = \text{inst}[13] \cdot \text{Branch} \]
这个逻辑表达式表示,只有当当前指令是分支指令时,BrUn
信号才会根据inst[13]
的值来决定。
具体例子
例如,对于BEQ(Branch if Equal)和BNE(Branch if Not Equal)指令:
- BEQ:
inst[14:12]
为000,inst[6:2]
为11000。 - BNE:
inst[14:12]
为001,inst[6:2]
为11000。
通过观察这些位,我们可以知道当前指令是BEQ还是BNE,并且可以使用inst[13]
来确定是否进行无符号比较。
这些组合逻辑控制信号在整个指令解码和执行过程中起着关键作用,确保处理器能够正确解释和执行每一条指令。通过这种方式,RISC-V处理器能够高效地处理各种复杂的指令和操作。
基于ROM的控制
概述
在基于ROM(只读存储器)的控制系统中,使用预编程的存储器来生成控制信号。通过提供指令和条件信号作为输入,ROM可以输出控制所需的信号,从而控制数据路径执行正确的操作。
输入和输出
- 输入:
Inst[30, 14:12, 6:2]
:这9个位用于指定操作码和功能码,决定具体指令类型。BrEq
:分支条件信号,指示两个寄存器是否相等。BrLT
:分支条件信号,指示一个寄存器是否小于另一个寄存器。
- 输出:
PCSel
:程序计数器选择信号。ImmSel[2:0]
:立即数选择信号。BrUn
:无符号分支选择信号。ASel
:A端口选择信号。BSel
:B端口选择信号。ALUSel[3:0]
:ALU操作选择信号。MemRW
:存储器读/写控制信号。RegWEn
:寄存器写使能信号。WBSel[1:0]
:写回选择信号。
这些输出信号共同决定了每条指令如何在数据路径上执行。
ROM控制器实现
组成部分
- 地址解码器(Address Decoder):根据输入信号生成ROM的地址。输入信号包括指令位(Inst[])、分支条件信号(BrEq、BrLT)。
- ROM:存储预先编程的控制字。每个地址对应一个控制字,用于生成一组控制信号。
过程
- 地址生成:根据输入的指令位和条件信号,地址解码器生成ROM的地址。
- 读取控制字:ROM根据地址输出相应的控制字。
- 生成控制信号:控制字包含的每一位用于控制数据路径上的不同部分,生成所需的控制信号。
详细解释
- 地址解码:地址解码器通过AND操作生成ROM的地址。例如,对于
add
、sub
、or
等指令,根据指令的特定位和条件信号生成唯一的地址。 - 控制字:控制字包含用于控制数据路径的所有必要信号,例如
PCSel
、ImmSel
等。 - 控制输出:控制字的每一位对应一个具体的控制信号,这些信号直接用于控制处理器的数据路径。
通过这种方式,ROM控制器可以高效地管理和生成复杂的控制信号,使处理器能够正确执行各种指令。这种方法特别适用于需要固定控制逻辑的情况,因为ROM可以预先编程和优化。
Control Logic to Decode add
控制逻辑解码add
指令
指令字段
在解码add
指令时,我们主要关注以下字段:
inst[30]
inst[14:12]
inst[6:2]
控制逻辑表达式
图中展示了几条用于解码指令的逻辑表达式:
- add 指令:
add = i[30] ̅ • i[14] ̅ • i[13] ̅ • i[12] • R-type
- 解释:如果指令的第30位为0,第14、13位为0,第12位为1,且操作码对应R型指令,那么这条指令就是
add
指令。
- R型指令:
R-type = i[6] ̅ • i[5] ̅ • i[4] ̅ • i[3] • i[2] • RV32I
- 解释:如果指令的第6、5、4位为0,第3、2位为1,且指令属于RV32I指令集,那么这条指令就是R型指令。
- RV32I指令集:
RV32I = i[1] • i[0]
- 解释:如果指令的第1、0位为1,那么这条指令属于RV32I指令集。
深入讲解
- 指令解码:解码是指令执行的第一步,通过识别指令的各个位段,处理器能够确定指令的类型和操作。
- 控制逻辑:控制逻辑通过组合不同的位段和条件,生成控制信号,用于驱动处理器的各个组件执行相应的操作。
- 位段分析:每个指令位段都有特定的含义,位段组合可以唯一确定一条指令。例如,
add
指令通过特定的位段组合来识别。
总结
我们已经构建了一个处理器!
在本次课程中,我们成功构建了一个能够执行所有RISC-V指令的处理器。以下是我们取得的一些成就和要点:
处理器功能
- 能够在一个周期内执行所有RISC-V指令:我们设计的处理器可以在单个时钟周期内完成所有RISC-V指令的执行。
- 并非所有单元(硬件)都被所有指令使用:不同的指令可能只使用处理器的一部分硬件资源。
- 关键路径变化:在设计过程中,我们需要考虑到不同指令的关键路径可能会有所不同,并对其进行优化。
指令执行的五个阶段
- IF(指令获取):从指令存储器中获取指令。
- ID(指令解码):解码指令并读取寄存器中的数据。
- EX(执行):在算术逻辑单元(ALU)中执行操作。
- MEM(内存访问):访问数据存储器(如果需要)。
- WB(写回):将结果写回寄存器。
控制器指定如何执行指令
- 以ROM或逻辑实现:控制器可以通过ROM(只读存储器)或组合逻辑来实现,用以决定每条指令的执行路径和控制信号。
硬件和软件的联系
在处理器设计中,硬件和软件是紧密联系的。从高级语言(如C)的编写,到编译成汇编语言(如RISC-V),再到汇编器将其转换为机器语言(RISC-V机器码),最后通过硬件架构的实现,将这些指令在实际的硬件电路中执行。
这一过程展示了从高级语言到硬件实现的整个流程,使得我们能够深入理解计算机系统的运行原理。最终,我们达到了硬件和软件的完美结合,构建出了一个功能完整的RISC-V处理器。