一、RegFile模块
在记录一中,我们列出了RV32I的32个寄存器。下面分析寄存器模块的定义。
package mini
import chisel3._
import freechips.rocketchip.config.Parameters
class RegFileIO(implicit p: Parameters) extends CoreBundle()(p) {
val raddr1 = Input(UInt(5.W))
val raddr2 = Input(UInt(5.W))
val rdata1 = Output(UInt(xlen.W))
val rdata2 = Output(UInt(xlen.W))
val wen = Input(Bool())
val waddr = Input(UInt(5.W))
val wdata = Input(UInt(xlen.W))
}
class RegFile(implicit val p: Parameters) extends Module with CoreParams {
val io = IO(new RegFileIO)
val regs = Mem(32, UInt(xlen.W))
io.rdata1 := Mux(io.raddr1.orR, regs(io.raddr1), 0.U)
io.rdata2 := Mux(io.raddr2.orR, regs(io.raddr2), 0.U)
when(io.wen & io.waddr.orR) {
regs(io.waddr) := io.wdata
}
}
从上面的代码中可以看到,寄存器模块分为接口定义和寄存器定义两个部分。
接口分为读寄存器和写寄存器,由于指令通常需要一次读取2个源寄存器RS1和RS2,写回的时候,通常只需要对一个目的寄存器RD进行读写,因此接口定义为2个读接口和一个写接口,通常读接口在译码阶段就需要读出,写接口在写回阶段会写入。寄存器的地址来自与指令中rs1 rs2 和rd 部分。
regs用Mem 硬件模型定义,第一个参数是数量,第二个参数是位宽,位宽xlen来自与参数中预定义,这里是32.
io.rdata1 用:=进行赋值,因为之前用val定义的,在scala里不能用=赋值的,这里chisel 定义了新的:= 操作方法用于赋值。regs(io.raddr1) 调用Mem定义的apply方法,参数是寄存器地址。读出寄存器值。应为需要一直读取寄存器,因此没有使能定义。
写寄存器有一个io.wen使能信号,when chisel定义的触发条件,条件成立,写入寄存器rd。
二、immgen模块
/ See LICENSE for license details.
package mini
import chisel3._
import chisel3.util._
import freechips.rocketchip.config.Parameters
import Control._
class ImmGenIO(implicit p: Parameters) extends CoreBundle()(p) {
val inst = Input(UInt(xlen.W))
val sel = Input(UInt(3.W))
val out = Output(UInt(xlen.W))
}
abstract class ImmGen(implicit p: Parameters) extends Module {
val io = IO(new ImmGenIO)
}
class ImmGenWire(implicit p: Parameters) extends ImmGen()(p) {
val Iimm = io.inst(31, 20).asSInt
val Simm = Cat(io.inst(31, 25), io.inst(11,7)).asSInt
val Bimm = Cat(io.inst(31), io.inst(7), io.inst(30, 25), io.inst(11, 8), 0.U(1.W)).asSInt
val Uimm = Cat(io.inst(31, 12), 0.U(12.W)).asSInt
val Jimm = Cat(io.inst(31), io.inst(19, 12), io.inst(20), io.inst(30, 25), io.inst(24, 21), 0.U(1.W)).asSInt
val Zimm = io.inst(19, 15).zext
io.out := MuxLookup(io.sel, Iimm & -2.S,
Seq(IMM_I -> Iimm, IMM_S -> Simm, IMM_B -> Bimm, IMM_U -> Uimm, IMM_J -> Jimm, IMM_Z -> Zimm)).asUInt
}
class ImmGenMux(implicit p: Parameters) extends ImmGen()(p) {
val sign = Mux(io.sel === IMM_Z, 0.S, io.inst(31).asSInt)
val b30_20 = Mux(io.sel === IMM_U, io.inst(30,20).asSInt, sign)
val b19_12 = Mux(io.sel =/= IMM_U && io.sel =/= IMM_J, sign, io.inst(19,12).asSInt)
val b11 = Mux(io.sel === IMM_U || io.sel === IMM_Z, 0.S,
Mux(io.sel === IMM_J, io.inst(20).asSInt,
Mux(io.sel === IMM_B, io.inst(7).asSInt, sign)))
val b10_5 = Mux(io.sel === IMM_U || io.sel === IMM_Z, 0.U, io.inst(30,25))
val b4_1 = Mux(io.sel === IMM_U, 0.U,
Mux(io.sel === IMM_S || io.sel === IMM_B, io.inst(11,8),
Mux(io.sel === IMM_Z, io.inst(19,16), io.inst(24,21))))
val b0 = Mux(io.sel === IMM_S, io.inst(7),
Mux(io.sel === IMM_I, io.inst(20),
Mux(io.sel === IMM_Z, io.inst(15), 0.U)))
io.out := Cat(sign, b30_20, b19_12, b11, b10_5, b4_1, b0).asSInt.asUInt
}
immgen模块单独拿出来用于对指令中的imm 立即数生成 作为ALU模块的B操作数输入到ALU模块进行运算,在这里asInt扩展成32位有符号数。
immgen模块输入 为取值阶段输入的指令 和control模块输入的imm_sel,输出为产生的立即数。
ImmGenWire简单粗暴,直接根据imm_sel,把 每种指令类型产生的imm直接输出。而ImmGenMux是按照32位中的每一部分,分别根据涉及到的指令类型 进行数据产生。最后用Cat组合成32位立即数赋值给io.out.