从零开始写riscv处理器(二)简单数据通路

RV32I指令类型较多,要实现所有指令的数据通路比较复杂,为便于读者更快了解数据通路,本文先以一个简单的数据通路作为引例,该数据通路只支持执行R型、BEQ指令、Load/Store型指令。在[[从零开始写riscv处理器(三)全部指令数据通路|后一篇文章]]中,再介绍如何构建实现RV32指令的数据通路以及不同之处。

1. 设计思想

分析每类 RISC-V 指令需要哪些主要执行单元。再分析每条指令需要哪些数据通路单元, 然后逐渐降低抽象的层次。

2. 建立简单数据通路

2.1 PC取指模块

在 RISC-V 架构中,PC(程序计数器)取指模块的作用是负责从指令内存中读取指令,并将其提供给下一个阶段的指令解码和执行。
PC 取指模块的主要功能如下:

  1. 从指令存储器中读取指令:根据 PC 寄存器中指定的地址,从指令存储器中读取下一条指令。
  2. 增加 PC:每次成功取指后,PC 需要增加以指向下一条指令的地址。增加的值通常是指令的长度,以便顺序执行下一条指令。
    PC取指模块还有其他功能如跳转、异常中断等,这里暂不介绍,为便于riscv架构初学者理解,只介绍最基本的功能。

根据上面的功能,总结PC取指模块需要三个基本单元构成:

  1. 指令存储器
  2. 程序计数器
  3. 加法器

指令存储器用于存储程序的指令,并根据给定地址提供指令。因为数据通路不会写入指令,所以指令存储器只提供读访问,因此将其视为组合逻辑:任何时刻的输出都反映了输入地址内容的变化,而不需要读控制信号。
程序计数器用于保存当前指令的地址。程序计数器是一个寄存器,它在每个时钟周期结束时会被写入,所以不需要写控制信号。
加法器用来增加 PC 的值以获得下一条指令的地址:计算两个 输入的加法,并输出结果。这个加法器是一个组合逻辑电路,可由ALU (运算单元,后面会讲)实现,只需将其中的控制信号设为总是进行加法运算即可。给这样的 ALU 加上 “Add” 标记,以表明它是加法器并且不能执行其他 ALU 操作。

接着,将几个基本单元组合成PC取指模块如下:

执行任意一条指令过程如下:首先从存储器中取出指令。为准备执行下一条指令,必须增加程序计数器的值,使其指向下一条指令,即PC+4,向后移动 4 个字节。
这里为什么是PC+4呢? 因为在32位RISCV架构中,指令是按字节存储的。一条指令为32bit,分成4个字节存储,当前指令地址+4表示增加4个字节,刚好指向下一条指令的起始地址。

2.2 增加R型指令数据通路

现在考虑 R 型指令。这类指令读两个寄存器,对它们的内容执行 ALU 操作,再将结果写回寄存器。这些指令被称为 R 型指令或算术逻辑指令(因为它们执行算术或逻辑运算)。如 add、sub、and 和 or 指令。
根据上面的功能,总结R型指令数据通路还需要2个基本单元构成:

  1. 寄存器堆
  2. ALU

寄存器堆是寄存器的集合,其中的寄存器可以通过指定相应的寄存器号来进行读写。寄存器堆包含了计算机的寄存器状态。寄存器堆中包含处理器的多个通用寄存器。
ALU对从寄存器读出的值进行运算。

将各个基本单元组成R型数据通路如下:

R 型指令有三个寄存器操作数,每条指令需要从寄存器堆中读出两个数据字,再写入一个数据字。

  • 读出一个数据字,需要一个输入指定要读的寄存器号(rs1或者rs2),以及一个从寄存器堆读出的输出(rs1 data或者rs2 data)。
  • 写入一个数据字,寄存器堆需要两个输入:一个输入指定要写的寄存器号(rd),另一个提供要写入寄存器的数据(rd data)。

寄存器堆根据输入的寄存器号输出相应寄存器的内容。而写操作由写控制信号(RegWrite)控制。
因此,寄存器堆总共需要四个输入(三个5bit寄存器编号和一个32bit数据)和两个输出(两个32bit数据)。
注:因为有32个通用寄存器,输入寄存器号5bit用于唯一确定某个寄存器,$2^5=32$。

2.3 增加BEQ指令数据通路

B型指令有三个操作数,其中两个寄存器用于比较运算,另一个是 12 位偏移量,用于计算相对于分支指令所在地址的分支目标地址 ( branch target address)。
分支指令的数据通路需要执行两个操作:计算分支目标地址检测分支条件。在计算分支目标地址的同时,必须确定是顺序执行下一条指令,还是执行分支目标地址处的指令。当分支条件为真(例如,两个操作数相等)时,分支目标地址成为新的 PC,我们就说分支发生。如果条件不成立,自增后的 PC 成为新的 PC(就像其他普通指令一样),这时就说分支未发生。
因此,B型指令的数据通路还需要在前面的基础上增加:

  1. 立即数扩展单位
  2. 加法单元
  3. 二选一多选器

将各个模块组合成B型指令的数据通路如下:

分支指令数据通路使用 ALU 检测分支条件是否成立,若成立,ALU输出一个Zero信号。另外使用单独的加法器将 PC 和符号扩展后左移一位的指令中的 12 位(分支偏移量)相加,以得到分支目标地址。在数据通路中,增加一个二选一多选器选择下一条指令地址来自自增还是B型指令跳转结果,控制信号为ALU中的Zero信号。

2.4 增加Load/Store型指令通路

RISC-V 的存取指令一般形式为 Id x1, offset(x2)或 sd x1, offset(x2)。 这类指令通过将基址寄存器 x2 与指令中包含的 12 位有符号偏移量相加,得到存储器地址。对于存储指令,从寄存器x1中读出要存储的数据。如果是载入指令,那么从存储器中读出的数据要写入指定的寄存器x1中。
Load/Store单元同样需要将立即数扩展,立即数扩展单元可复用现数据通路中已有的单元。此外,在现有数据通路上,还需要:

  1. 数据存储单元
  2. 二选一多选器×2

数据存储单元在存储指令时被写入,所以它有读写控制信号、地址输入和写入存储器的数据输入。
Load/Store指令数据通路如下:

其中增加了两个二选一多选器,一个选择写入rd寄存器的数据来自ALU运算结果还是来自data memory,一个选择进入ALU运算的rs2来自寄存器堆还是Load/Store指令中的立即数。

2.5 一个简单的数据通路

现在已经分别讨论了几类指令需要的数据通路单元,可得到一个完整的简单数据通路(暂不考虑控制信号)。

3. 控制模块的实现

现在已经完成了这个简单的数据通路,后续还需要添加控制单元。需要的控制单元主要为ALU单元,根据不同的指令类型,ALU需执行与、或、加、减中的某一种操作,因此需要控制信号进行区分。此外,还有各多选器需要控制信号,根据指令的不同,多选器选择的数据通路也不同。
所以,分别设计两个控制模块:ALU控制模块主控制模块。ALU控制模块控制ALU具体执行的运算,主控制单元控制各多选器的控制信号以及其他的一些信号。
值得注意的是,ALU单元的控制其实也可以通过主控制模块一并控制,但书中没有这样做,而是采用了多级译码的方式:主控制单元生成 ALUOp 位用作 ALU 的输入控制信号,再生成实际信号来控制 ALU。 多级控制可以减小主控制单元的规模,多个小的控制单元可能潜在地减小控制单元的延迟。这样的优化很重要,因为控制单元的延迟是决定时钟周期的关键因素。

3.1 ALU控制模块

根据不同的指令类型,ALU需执行与、或、加、减中的一种操作:

  • 对于load和store指令,ALU做加法计算存储器地址。
  • 对于R型指令,根据指令的7位funct7字段(Instruction[31:25])和3位funct3 字段(Instruction[14:12]),ALU 需执行四种操作(与、或、加、减)中的一种。
  • 对于条件分支指令beq,ALU 将两个操作数做减法并检测结果是否为 0。
    因此,RISC-V中定义了四根输入控制线的组合选择ALU要执行的功能:

  • 控制信号为“0000”时,ALU单元执行AND操作;
  • 控制信号为“0001”时,ALU单元执行OR操作;
  • 控制信号为“0010”时,ALU单元执行Add操作;
  • 控制信号为“0110”时,ALU单元执行Substract操作;

这4 位ALU的输入控制信号可由一个小型控制单元产生,其输入是2位的ALUOp字段以及指令的 funct7 和funct3 字段。ALUOp指明要执行的操作是load和store指令要做的加法, 还是beq指令要做的减法并检测是否为0, 或是由funct7和funct3字段决定(ALUOp信号来自主控制模块)。该控制单元输出一个4位信号,即前面介绍的 4 位组合之一来直接控制 ALU。

增加ALU控制单元后的数据通路如图所示:

那么现在问题是如何将2 位 ALUOp 字段和 funct 字段映射到四位 ALU 输入控制信号?
下表说明如何根据指令中的 2 位 ALUOp 控制字段、funct7 和 funct3 字段设置 ALU的输入控制信号。

第一列是指令,它决定了 ALUOp 位。根据不同的 ALUOp 位判断ALU的操作:

  • ALU=002或者ALU=012,ALU 操作不依赖于 funct7 或 funct3 字段,“不关心”操作码的值;
  • ALUOp=102 时,根据 funct7 和 funct3 字段区分不同指令,设置ALU的输入控制信号。
  • ALUOp 不使用112编码

对于R型指令的区分,ALUOp均为102 ,需要通过funct3和funct7进一步区分。大部分R型指令通过funct3就能区分,但add指令与sub指令的funct3字段均为000,需要再通过funct7进一步区分,确切来说,需要通过funct7[5]来区分。因此,为区分四类R型指令,只需要将funct3字段、funct7第6位作为 ALU 控制的输入,而不是 funct 字段的全部 10 位。

3.2 主控制模块

3.2.1 定义控制信号如何工作

将之前的简单数据通路图添加上控制信号如下:

控制信号包括6 根 1 位控制线和 2 位 ALUOp 控制信号:

  1. RegWrite信号控制寄存器堆是否写回;
  2. ALUSrc控制第二个ALU操作数来自第二个寄存器堆的输出还是指令的低12位符号扩展;
  3. PCSrc控制PC值是PC+4还是adder的输出,即分支目标;
  4. MemRead控制DataMemery读有效;
  5. MemWrite控制DataMemery写有效;
  6. MemtoReg控制写回数据来自ALU运算结果还是DataMemery输出;

注意,除 PCSrc 控制信号外,所有的控制信号可由主控制单元仅根据指令的操作码和 funct 字段设置。PCSrc 控制线是例外,它要依赖前面提到的B型指令的Zero信号。若指令是B型指令且还需判断ALU 的零输出Zero信号有效,那么PCSrc 控制信号有效。为生成 PCSrc 信号,需要将来自控制单元(称为“Branch”)的信号与来自 ALU 的零输出信号相“与”。

3.2.2 如何设置控制信号

现在来设计主控制模块,控制模块的输入是指令的7位操作码字段,输出包含:

  • 控制多路选择器的1位信号 ×2; (ALUSrc 和 MemtoReg)
  • 控制寄存器堆的1位信号 ×1; (RegWrite)
  • 控制数据存储器读写的信号 ×2; (MemRead 和 MemWrite)
  • 确定是否分支的 1 位信号 ×1; (Branch)
  • ALU 的 2 位控制信号 ×1; (ALUOp)

注意,分支控制信号(Branch)与ALU 的零输出信号(Zero)一起送入一个与门,其输出控制下一个 PC 的选择。PCSrc是衍生信号,不是直接来自控制单元。

控制线的设置完全取决于指令的操作码字段,如下图所示。

从表中控制信号可知:

  • 对于R型指令:进入ALU的rs2 data来自寄存器堆;写入rd寄存器的数据来自ALU运算结果;寄存器堆写使能有效;data memory读写使能均无效;分支使能Branch无效;ALUOp为10;
  • 对于ld型指令:进入ALU的rs2 data来自立即数扩展;写入rd寄存器的数据来自data memory;寄存器堆写使能有效;data memory读使能有效写使能无效;分支使能Branch无效;ALUOp为00;
  • 对于sd型指令:进入ALU的rs2 data来自立即数扩展;不会写入rd寄存器,寄存器堆写使能无效;data memory写使能有效读使能无效;分支使能Branch无效;ALUOp为00;
  • 对于beq型指令:进入ALU的rs2 data来自寄存器堆;不会写入rd寄存器,寄存器堆写使能无效;data memory读写使能均无效;分支使能Branch有效;ALUOp为01;

加上主控制模块的数据通路如下图:

3.3 数据通路操作

下图是各类型指令的数据通路(不考虑控制单元的信号)
R型指令数据通路示意图:

I型指令数据通路示意图:

BEQ指令数据通路示意图:

Load型指令数据通路示意图:

Store型指令数据通路示意图:

来自广东

评论

  1. cc
    Windows Chrome
    8 月前
    2024-3-31 21:37:58

    写太好了哥

    来自湖北
    • 博主
      cc
      Windows Edge
      8 月前
      2024-3-31 21:47:04

      真假的hh

      来自广东

发送评论 编辑评论


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