CS61C – 5 RISC – V Processor Design

这一部分对应的是 lec 18 ~ 20 (哔大上的参考课程为[Summer 20]),主要涉及的是RISC – V的处理器设计,包括实现框架(lec 18)、数据通路(lec 19)、性能校衡(lec 20).

与前一讲的抽象的功能描述不同,本讲深入到用具体的硬件模块(寄存器、ALU、多路选择器等)和控制信号,并最终搭建一个能执行指令的机器。

具体而言,我们将从ISA规范出发,逐步分析指令执行共性与个性,分别为其设计数据通路组件,再将组件集成并添加控制逻辑,并最终实现具体指令。

数据通路与控制 – CPU

作为指令处理的中心部件,我们先对 CPU 进行剖析。

正如小标题所言, CPU 发挥功能离不开数据通路和控制

  • 数据通路包含执行操作所需的所有硬件组件(寄存器、ALU、加法器、多路选择器等)以及连接这些组件的线路。它负责执行具体的算术、逻辑和数据传输操作。
  • 控制部件则根据当前正在执行的指令产生一系列控制信号,告诉数据通路中的多路选择器如何选择、ALU执行什么操作、寄存器是否写入等。它由控制逻辑电路(未来会讲,本质上是一个有限状态机)构成

控制单元根据 opcodefunct3funct7 等指令字段生成信号,驱动数据通路完成正确操作。一个很简单的理解,数据通路是“怎么做”,而控制单元是“做什么”和“何时做”

指令执行的五个阶段

为了降低设计复杂度,指令执行被分解为五个标准阶段,这也是经典RISC流水线的基础:

  1. 指令获取 (Instruction Fetch, IF):从指令存储器(IMEM)中读取当前PC指向的指令。
  2. 指令译码与寄存器读取 (Instruction Decode/Register Read, ID):解析指令,从寄存器堆(Regfile)中读取rs1rs2指定的寄存器值。
  3. 执行 (Execute, EX):在算术逻辑单元(ALU)中进行计算(如加减法、逻辑运算)。
  4. 存储器访问 (Memory Access, MEM):如果是load或store指令,则访问数据存储器(DMEM)。
  5. 寄存器写回 (Write Back, WB):将结果(来自ALU或DMEM)写回rd指定的寄存器。

需要注意的是,在单周期处理器中一条指令会按顺序经历这五个阶段,并在一个漫长的时钟周期内完成,但这并不完全等同于流水线。

硬件构建块

构建数据通路所需的模块包括:

  • 组合逻辑元件
    • ALU:执行算术和逻辑运算。其操作(加、减、与、或等)由 ALUSel 控制信号选择。
    • 加法器 (Adder):专门用于地址计算,如 PC + 4
    • 多路选择器 (Mux):根据选择信号(如 BSelWBSel )从多个输入中选择一个输出。
  • 状态元件
    • 程序计数器 (PC):一个存储当前指令地址的寄存器。
    • 寄存器堆 (Regfile):包含32个寄存器(x0-x31)的存储器。支持同时读取两个寄存器(rs1rs2)和写入一个寄存器(rd)。写操作由 RegWEn(Register Write Enable)信号控制,且仅在时钟上升沿生效。
    • 存储器 (IMEM & DMEM)
      • IMEM只读,用于取指;
      • DMEM可读写,用于load/store。
    • 读操作是组合逻辑(地址有效后输出即有效),写操作是时序逻辑(需要时钟上升沿和写使能信号)。

Time to Build!

事实上,由于在 ISA 中设计指令时考虑到了相关位的统一和复用,这为我们的处理器设计简化了大量操作。所以对于各类型指令,在完成最基础、最统一的部分后,只需要特化当前类型下所特有的功能即可。

R – Type

首先考虑最简单的 R- Type 类型指令:

  • 操作Reg[rd] = Reg[rs1] op Reg[rs2]
  • 数据流:PC -> IMEM -> 指令字段译码出 rs1,rs2,rd -> Regfile读出 DataA , DataB  -> ALUSel 信号根据 funct7funct3 选择操作 -> ALU计算结果 -> 写回 Regfile 的 DataD 端。同时,PC + 4 计算下一条指令地址。
  • 关键控制信号RegWEn=1 (允许写寄存器),ALUSel(选择 ALU 操作,例如区分 add 和 sub )。

那么当前指令构建完成时,基本设计流程应当如下:

R – Type

I – Type

于 R – Type 相比,我们有了新的需求:第二个ALU操作数不再是来自寄存器的Reg[rs2],而是来自指令中的立即数

为了解决这个问题,我们可以在ALU的第二个输入前添加一个多路选择器BSel Mux)。该Mux的一个输入来自Reg[rs2](用于 R – type),另一个输入来自立即数生成器

  • 立即数生成器 (Imm Gen):负责根据指令的格式(I-type, S-type, B-type等)从32位指令中提取正确的位并进行符号扩展,生成一个32位的立即数。由ImmSel信号控制。

注意此处我们新增了两个控制信号

  • BSel:用于选择ALU的B操作数来源:寄存器 or 立即数;
  • ImmSel:告诉立即数生成器当前指令是哪种格式。
I – Type

S – Type

关于 S – Type,我们的新需求是计算内存地址(Reg[rs1] + imm)并访问数据存储器(DMEM),比如 lw 需要将读出的数据写回寄存器,而 sw 需要将寄存器的值写入内存。

关于内存计算,注意到还是立即数的问题,所以我们可以考虑复用ALU和 BSel Mux,让 ALUSel = Add;而关于 DMEM ,这没办法,老老实实添加 DMEM 模块。其地址来自ALU结果,写数据(DataW)来自Reg[rs2];至于数据写回操作,我们选择添加写回选择多路选择器(WBSel Mux)。对于lwWBSel需选择从DMEM读出的数据(DataR),而对于R-type/I-type,则选择ALU的结果。

所以对于当前轮,我们新增了如下信号:

  • MemRW:控制DMEM的读写(Read/Write)。
  • ImmSel:新增 S 选项,指导立即数生成器从指令的正确位置(inst[31:25]inst[11:7])提取并拼接出S-type的立即数。
  • RegWEn:对于sw指令,该信号必须为0,防止破坏寄存器值。

那么截至目前,我们的构建扩展为了:

S – Type

B – Type

分支指令需要完成两件事:

  1. 计算目标地址PC + imm (B-type的imm是2字节对齐的)
  2. 依据 RS1RS2 更新 PC ,决定是顺序执行还是跳转

关于条件分支部分,这没啥说的,添加一个分支比较器(Branch Comparator)。输入为Reg[rs1]Reg[rs2],输出为 BrEq(相等)和 BrLT(小于)信号。BrUn 信号控制进行有符号还是无符号比较。

而偏移地址就只能再添加一个加法器了,因为复用当前的 +4 加法器会导致结构冲突

既然 PC 的输入有两个来源了,所以新增一个 PC 来源多路选择器(PCSel Mux)也就是理所应当的了。该Mux选择下一个 PC 是来自 PC+4,还是来自分支目标地址计算器(PC + imm ),或是其他来源( 如 JALR )。

此时的构建扩展为:

B – Type

J – Type

  • JAL (Jump and Link)已经在 B – Type 时考虑实现,通过设置 PCSel = takenWBSel = PC+4 即可
  • JALR (Jump and Link Register)PC = Reg[rs1] + immReg[rd] = PC + 4。这需要ALU计算地址,并引入一个新的Mux来选择ALU的A操作数(Asel Mux),使其可以选择PCReg[rs1]
  • LUI/AUIPC (U-type):处理20位高位立即数。LUI直接将立即数写回寄存器,AUIPC则进行PC + imm的计算。这通过ImmSelU选项和ALUSel的直通(Bypass)功能实现。

综上所述,实现 J – Type 的架构为:

J – Type

至此,我们完成了 R, I, S, B, J 五种基本类型的指令设计,在设计完成后,我们同样需要对成品进行性能分析。

控制与性能分析

当前我们的控制单元包含的信号包括:

PCSelImmSelBrUnASelBSelALUSelMemRWRegWEnWBSel

那么如何实现它们呢?讲座给出了两种实现方式:

  • ROM(只读存储器)实现:将“指令->控制信号”的映射关系直接做成一张巨大的查表。将指令和标志位拼接成地址,该地址对应的ROM数据就是所有的控制信号。
    • 这种设计比较简单,易于修改和调试( reprogrammable );但速度较慢(需要地址解码),且花费面积可能较大。
  • 组合逻辑(硬连线)实现:使用传统的数字设计方法(真值表 -> 卡诺图 -> 逻辑表达式 -> 逻辑门)来为每个控制信号生成一个优化的组合电路。
    • 这种设计速度更快,是高性能处理器的标准做法。相对的,设计更复杂,而且一旦制造完成就无法修改。

而性能衡量这方面,正如前文提及,时钟周期必须满足最慢指令的需求。而对于单周期设计,最慢的指令是lw

我们来看一下 lw 的路径:

PC -> IMEM -> RegFile读 -> Imm Gen -> ALU -> DMEM -> WBSel Mux -> RegFile写

对应的时钟周期为:

 $$ t_{cost} \geq t_{clk-to-q} + t_{IMEM} + t_{RegRead} + t_{Mux} + t_{ALU} + t_{DMEM} + t_{Mux} + t_{RegSetup}$$

对于所有其他更快的指令(如addbeq),它们都在这个漫长的周期内完成了工作,但必须等待整个周期结束。所以单周期 CPU 的硬件利用率极低

我们可以说单周期设计是理解CPU工作原理的完美教学模型,但它因其极低的性能而没有任何实际应用价值。它的主要价值在于引出了下一个伟大主题:流水线(Pipelining)

先提一嘴,流水线技术的核心思想就是指令级并行:将指令执行划分成多个阶段(IF, ID, EX, MEM, WB),并让不同的指令同时处于不同的阶段,就像工厂的装配线。这样可以极大地提高硬件利用率和系统吞吐量,尽管单条指令的延迟(Latency)可能反而会增加。不过这是后面讲座的内容了。

讲座还稍微提及了一下控制与状态寄存器 (CSRs),这是一类不同于通用寄存器(x0-x31)的特殊寄存器,用于控制和监控处理器状态(如中断使能、周期计数器、异常原因等),系统中断便与此相关。

写过 PA 3 的都知道 CSRs 的含金量…( -= . -)ノ|

总而言之,这一部分完成了计算机架构的“从零到一”的构建。 我们不仅看到了一个完整的CPU数据通路,更重要的是理解了其设计思路、控制方法以及性能权衡。这为接下来学习流水线——这个几乎所有现代处理器都在使用的核心技术——扫清了最后的障碍,并让我们能够深刻理解流水线所要解决的问题及其带来的巨大优势。

以上

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注