接着分析的是dpath.scala。riscv-sodor-rv32_2stage dpath.scala与riscv-sodor-rv32_1stage dpath.scala的差异在于取指后加了一级执行的寄存器。当在执行的时候,取指寄存器可以预先取指,这样能提高系统的效率。如果没有执行阶段的寄存器(riscv-sodor-rv32_1stage),在执行非单步指令时(multi-cycle的指令),cpu流水线要暂停取指,等当前指令完成执行后才能进行下一步的取指操作,这样会大大降低系统的效率。
package Sodor
{
import chisel3._
import chisel3.util._
import Constants._
import Common._
import Common.Constants._
//定义dpath到cpath的一些控制信号
//inst:从memory中读取的instruction
//br_eq:分支判断条件,当判断为相等时
//br_lt:分支判断条件,当有符号数,判断为小于时
//br_ltu:分支判断条件,当无符号数,判断为小于时
//csr_eret:当当前的执行指令为csr的call/break/ret时,该位置1
class DatToCtlIo(implicit conf: SodorConfiguration) extends Bundle()
{
val inst = Output(UInt(32.W))
val br_eq = Output(Bool())
val br_lt = Output(Bool())
val br_ltu= Output(Bool())
val csr_eret = Output(Bool())
override def cloneType = { new DatToCtlIo().asInstanceOf[this.type] }
}
//定义dpath的端口部分
//Flipped是反转的作用,将DebugCPath()中定义的端口反过来,并在cpath模块中命名为dcpath,即input在这边边ouput
//imem是cpath取指令的memory,MemPortIo()在/commo/memory.scala中定义 ,是memory端口的定义
//dmem是dpath取数据的memory,MemPortIo()在/commo/memory.scala中定义,是memory端口的定义
//dat是在DatToCtlIo()的调用,DatToCtlIo()在上面已经做了说明
//ctl是CtlToDatIo()的调用,利用Flipped将端口的方向翻转
class DpathIo(implicit conf: SodorConfiguration) extends Bundle()
{
val ddpath = Flipped(new DebugDPath())
val imem = new MemPortIo(conf.xprlen)
val dmem = new MemPortIo(conf.xprlen)
val ctl = Flipped(new CtlToDatIo())
val dat = new DatToCtlIo()
}
//主体部分,DatPath的主要逻辑
class DatPath(implicit conf: SodorConfiguration) extends Module
{
//先声明一个io的端口,调用上面的DpathIo()
val io = IO(new DpathIo())
io := DontCare
//**********************************
// Pipeline State Registers
//声明一个if_reg_pc的寄存器,初始值为START_ADDR,用于取指阶段
val if_reg_pc = Reg(init = START_ADDR)
//声明一个exe_reg_pc的寄存器,初始值为0,位宽为conf.xprlen.W,用于执行阶段
val exe_reg_pc = Reg(init=0.asUInt(conf.xprlen.W))
//声明一个exe_reg_pc_plus4的寄存器,初始值为0,位宽为conf.xprlen.W,用于执行阶段,是图中PC+4的位置
val exe_reg_pc_plus4 = Reg(init=0.asUInt(conf.xprlen.W))
//声明一个exe_reg_inst的寄存器,初始值为BUBBLE,用于执行阶段,是图中IR的位置
val exe_reg_inst = Reg(init=BUBBLE)
//定义几个32位、wire类型的信号
//if_pc_next:下一个pc的值
//exe_br_target:分支判断的目标
//exe_jmp_target:直接跳转的目标
//exe_jump_reg_target:以寄存器的值为地址的跳转
//exception_target:异常的跳转
//**********************************
// Instruction Fetch Stage
val if_pc_next = Wire(UInt(32.W))
val exe_br_target = Wire(UInt(32.W))
val exe_jmp_target = Wire(UInt(32.W))
val exe_jump_reg_target = Wire(UInt(32.W))
val exception_target = Wire(UInt(32.W))
//只要流水线不暂停,则每个时钟上升沿时将if_pc_next赋给if_reg_pc
when (!io.ctl.stall)
{
if_reg_pc := if_pc_next
}
//声明一个if_pc_plus4信号,等于当前if_reg_pc的值加4
val if_pc_plus4 = (if_reg_pc + 4.asUInt(conf.xprlen.W))
//下一个pc的值,if_pc_next的变化情况,根据io.ctl.pc_sel的值来选择
if_pc_next := MuxCase(if_pc_plus4, Array(
(io.ctl.pc_sel === PC_4) -> if_pc_plus4,
(io.ctl.pc_sel === PC_BR) -> exe_br_target,
(io.ctl.pc_sel === PC_J ) -> exe_jmp_target,
(io.ctl.pc_sel === PC_JR) -> exe_jump_reg_target,
(io.ctl.pc_sel === PC_EXC)-> exception_target
))
//指令memory,取指令数据,将if_reg_pc的值作为取值地址,将得到的指令赋给if_inst信号
//Instruction Memory
io.imem.req.bits.addr := if_reg_pc
val if_inst = io.imem.resp.bits.data
//如果流水线暂停,则保持当前运行的pc和当前运行的指令数据
//如果流水线没有暂停,但cpath模块给出了kill信号(则下一个pc不是pc+4,可能是跳转指令),则执行pc赋值为0,指令指令赋值为BUBBLE
//如果流水线没有暂停,且没有kill信号,则每个时钟上升沿时,将if_inst赋给exe_reg_inst,将if_reg_pc赋给exe_reg_pc
when(io.ctl.stall)
{
exe_reg_inst := exe_reg_inst
exe_reg_pc := exe_reg_pc
}
.elsewhen(io.ctl.if_kill)
{
exe_reg_inst := BUBBLE
exe_reg_pc := 0.U
}
.otherwise
{
exe_reg_inst := if_inst
exe_reg_pc := if_reg_pc
}
//执行阶段的pc加4,用于将pc+4回写到寄存器的操作,当前执行地址是pc值,而此值为pc+4
exe_reg_pc_plus4 := if_pc_plus4
//执行阶段
//根据exe_reg_inst的数据,进行exe_rs1_addr,exe_rs2_addr和exe_wbaddr编号的提取
//**********************************
// Execute Stage
val exe_rs1_addr = exe_reg_inst(RS1_MSB, RS1_LSB)
val exe_rs2_addr = exe_reg_inst(RS2_MSB, RS2_LSB)
val exe_wbaddr = exe_reg_inst(RD_MSB, RD_LSB)
//声明一个exe_wbdata的信号,位宽为conf.xprlen.W
val exe_wbdata = Wire(UInt(conf.xprlen.W))
// Register File
//声明通用寄存器,32个通用寄存器,通用寄存器的位宽为conf.xprlen
val regfile = Mem(UInt(conf.xprlen.W),32)
//debug用的接口,当在debug模式时,给出了ddpath.addr,即将相应寄存器的值赋给ddpath.rdata
//类似的,当ddpath.validreq有效时,将debug写的ddpath.wdata赋给相应的寄存器中(ddpath.addr)
//// DebugModule
io.ddpath.rdata := regfile(io.ddpath.addr)
when(io.ddpath.validreq){
regfile(io.ddpath.addr) := io.ddpath.wdata
}
///
//当写寄存器使能了,且写的寄存器编号不为0,同时没有异常生成时,将wb_data赋给exe_wbdata
//这步操作是在时钟上升沿来时完成,类似于always @(posedge clk)
when (io.ctl.rf_wen && (exe_wbaddr != 0.U) && !io.ctl.exception)
{
regfile(exe_wbaddr) := exe_wbdata
}
//根据inst中提取rs1,rs2的寄存器编号,利用这个编号提取寄存器中的值,若rs1和rs2为0时,则
//rs1_data,rs2_data输出0值
val exe_rs1_data = Mux((exe_rs1_addr != 0.U), regfile(exe_rs1_addr), 0.U)
val exe_rs2_data = Mux((exe_rs2_addr != 0.U), regfile(exe_rs2_addr), 0.U)
//如果inst中包含的不是寄存器的编号,即不是rs1,rs2和wb的编号,而是立即数的话,则通过下面
//的步骤将立即数重组,不同指令有不同立即数类型,这点需要看RISCV指令的用户使用册2.2
// immediates
val imm_i = exe_reg_inst(31, 20)
val imm_s = Cat(exe_reg_inst(31, 25), exe_reg_inst(11,7))
val imm_b = Cat(exe_reg_inst(31), exe_reg_inst(7), exe_reg_inst(30,25), exe_reg_inst(11,8))
val imm_u = exe_reg_inst(31, 12)
val imm_j = Cat(exe_reg_inst(31), exe_reg_inst(19,12), exe_reg_inst(20), exe_reg_inst(30,21))
val imm_z = Cat(Fill(27,0.U), exe_reg_inst(19,15))
//下面是对符号位进行扩展,将符号位进行高位填充
// sign-extend immediates
val imm_i_sext = Cat(Fill(20,imm_i(11)), imm_i)
val imm_s_sext = Cat(Fill(20,imm_s(11)), imm_s)
val imm_b_sext = Cat(Fill(19,imm_b(11)), imm_b, 0.U)
val imm_u_sext = Cat(imm_u, Fill(12,0.U))
val imm_j_sext = Cat(Fill(11,imm_j(19)), imm_j, 0.U)
//根据ctl.op1_sel信号来选择alu_op1是exe_rs1_data(寄存器的值),还是U类立即数imm_u_sext,
//还是Z类的立即数imm_z,或者是0
val exe_alu_op1 = MuxCase(0.U, Array(
(io.ctl.op1_sel === OP1_RS1) -> exe_rs1_data,
(io.ctl.op1_sel === OP1_IMU) -> imm_u_sext,
(io.ctl.op1_sel === OP1_IMZ) -> imm_z
)).toUInt
//根据ctl.op2_sel信号来选择alu_op2是exe_rs2_data(寄存器的值),还是pc寄存器的值,还是I类立即数imm_u_sext,还是S类的立即数imm_z,或者是0
val exe_alu_op2 = MuxCase(0.U, Array(
(io.ctl.op2_sel === OP2_RS2) -> exe_rs2_data,
(io.ctl.op2_sel === OP2_PC) -> exe_reg_pc,
(io.ctl.op2_sel === OP2_IMI) -> imm_i_sext,
(io.ctl.op2_sel === OP2_IMS) -> imm_s_sext
)).toUInt
//定义一个exe_alu_out信号,位宽为conf.xprlen
// ALU
val exe_alu_out = Wire(UInt(conf.xprlen.W))
//定义一个alu_shamt信号,取exe_alu_op2数据的低5位
val alu_shamt = exe_alu_op2(4,0).toUInt
//进行ALU运算,根据ctl.alu_fun信号来确定运算的类型,->为运算的逻辑,最后将运算值赋给exe_alu_out
exe_alu_out := MuxCase(0.U, Array(
(io.ctl.alu_fun === ALU_ADD) -> (exe_alu_op1 + exe_alu_op2).toUInt,
(io.ctl.alu_fun === ALU_SUB) -> (exe_alu_op1 - exe_alu_op2).toUInt,
(io.ctl.alu_fun === ALU_AND) -> (exe_alu_op1 & exe_alu_op2).toUInt,
(io.ctl.alu_fun === ALU_OR) -> (exe_alu_op1 | exe_alu_op2).toUInt,
(io.ctl.alu_fun === ALU_XOR) -> (exe_alu_op1 ^ exe_alu_op2).toUInt,
(io.ctl.alu_fun === ALU_SLT) -> (exe_alu_op1.toSInt < exe_alu_op2.toSInt).toUInt,
(io.ctl.alu_fun === ALU_SLTU) -> (exe_alu_op1 < exe_alu_op2).toUInt,
(io.ctl.alu_fun === ALU_SLL) -> ((exe_alu_op1 << alu_shamt)(conf.xprlen-1, 0)).toUInt,
(io.ctl.alu_fun === ALU_SRA) -> (exe_alu_op1.toSInt >> alu_shamt).toUInt,
(io.ctl.alu_fun === ALU_SRL) -> (exe_alu_op1 >> alu_shamt).toUInt,
(io.ctl.alu_fun === ALU_COPY1)-> exe_alu_op1
))
//跳转目标的处理
//exe_br_target:当前pc加br的立即数
//exe_jmp_target:当前pc+跳转的立即数
// exe_jump_reg_target:rs1寄存器值+立即数的跳转
// Branch/Jump Target Calculation
exe_br_target := exe_reg_pc + imm_b_sext
exe_jmp_target := exe_reg_pc + imm_j_sext
exe_jump_reg_target := (exe_rs1_data.toUInt + imm_i_sext.toUInt)
//声明一个csr的模块,调用CSRFile()
// Control Status Registers
val csr = Module(new CSRFile())
csr.io := DontCare
//根据inst数据中的(CSR_ADDR_MSB,CSR_ADDR_LSB)来确定是哪个csr寄存器需要被操作
csr.io.decode.csr := exe_reg_inst(CSR_ADDR_MSB,CSR_ADDR_LSB)
//根据ctl.csr_cmd的信号来说明csr的具体操作是什么,如写/读/清除/置位
csr.io.rw.cmd := io.ctl.csr_cmd
//操作csr寄存器的数据
csr.io.rw.wdata := exe_alu_out
//将从csr寄存器中读出的值赋给csr_out信号
val csr_out = csr.io.rw.rdata
//csr寄存器中有一个特别的csr寄存器为instret,用于记录某时刻开始后系统执行了多少条指令
//这里csr模块中的retire就是用于记录执行了多少条指令,当流水线不暂停时,每个
//时钟来临,retiret都会为1,然后instret就自动+1
csr.io.retire := !io.ctl.stall // TODO verify this works properly
//csr寄存器记录系统是否有异常
csr.io.exception := io.ctl.exception
//csr模块中的pc信号记录当前pc寄存器的值
csr.io.pc := exe_reg_pc
//若发生异常时,异常跳转的目标由csr模块中的csr.io.evec(这是一个csr寄存器)给出
exception_target := csr.io.evec
//将csr的eret信号传输给cpath模块,用于下一个pc的选择
io.dat.csr_eret := csr.io.eret
// Add your own uarch counters here!
//增加自定义的计数器,非指令集规定的计数器
csr.io.counters.foreach(_.inc := false.B)
//writeback数据的选择,通过cpath模块传过来WB_ALU信号进行选择,是ALU的运算结果exe_alu_out
//还是mem数据的读取,还是当前pc+4后的值,还是csr寄存器读取的值
// WB Mux
exe_wbdata := MuxCase(exe_alu_out, Array(
(io.ctl.wb_sel === WB_ALU) -> exe_alu_out,
(io.ctl.wb_sel === WB_MEM) -> io.dmem.resp.bits.data,
(io.ctl.wb_sel === WB_PC4) -> exe_reg_pc_plus4,
(io.ctl.wb_sel === WB_CSR) -> csr_out
))
//dpath模块给cpath模块的数据
//inst为指令的数据,若(exe_rs1_data === exe——rs2_data),则br_eq为1,
//若有符号数(exe_rs1_data.toSInt < exe_rs2_data.toSInt)则br_lt为1,
//若无符号数(exe_rs1_data.toUInt S
, Mux(io.ctl.if_kill, Str("K"), Str(" ")) // Kill -> K
, Mux(io.ctl.pc_sel === UInt(1), Str("B"), // BR -> B
Mux(io.ctl.pc_sel === UInt(2), Str("J"),
Mux(io.ctl.pc_sel === UInt(3), Str("R"), // JR -> R
Mux(io.ctl.pc_sel === UInt(4), Str("E"), // EX -> E
Mux(io.ctl.pc_sel === UInt(0), Str(" "), Str("?"))))))
, exe_reg_inst
)
}
}
riscv-sodor-rv32_2stage的core.scala,consts.scala,tile.scala,package.scala,top.scala与riscv-sodor-rv32_1stage 的基本一致,所以就不再作过多的介绍。