riscv-sodor-rv32_1stage(2)
接riscv-sodor-rv32_1stage(1)的内容,先放rv32_1stage的结构图:
下面进行说明的是dpath.scala:
package Sodor
{
import chisel3._
import chisel3.util._
import Constants._
import Common._
import Common.Constants._
//定义cpath到dpath的一些控制信号
//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中定义,后面再说明吧,在这里知道它是声明了一片sram就可以了
//dmem是cpath出来数据的memory,MemPortIo()在/commo/memory.scala中定义,后面再说明吧,在这里知道它是声明了一片sram就可以了
//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
//定义几个32位、wire类型的信号
//pc_next:下一个pc的值
//pc_plus4:当前pc+4
//br_target:分支判断的目标
//jmp_target:直接跳转的目标
//jump_reg_target:以寄存器的值为地址的跳转
// exception_target:异常的跳转
// Instruction Fetch
val pc_next = Wire(UInt(32.W))
val pc_plus4 = Wire(UInt(32.W))
val br_target = Wire(UInt(32.W))
val jmp_target = Wire(UInt(32.W))
val jump_reg_target = Wire(UInt(32.W))
val exception_target = Wire(UInt(32.W))
//pc_next的变化情况,根据io.ctl.pc_sel的值来选择
// PC Register
pc_next := MuxCase(pc_plus4, Array(
(io.ctl.pc_sel === PC_4) -> pc_plus4,
(io.ctl.pc_sel === PC_BR) -> br_target,
(io.ctl.pc_sel === PC_J ) -> jmp_target,
(io.ctl.pc_sel === PC_JR) -> jump_reg_target,
(io.ctl.pc_sel === PC_EXC) -> exception_target
))
//定义一个pc register,初始值为START_ADDR
val pc_reg = Reg(init = START_ADDR)
//当流水线不暂停时,每个clk上升沿来时,将pc_next的值赋给pc_reg
when (!io.ctl.stall)
{
pc_reg := pc_next
}
//pc_plus4的值为pc_reg+4,即当前pc+4,在不支持压缩指令的情况下,pc的运行以word对齐
pc_plus4 := (pc_reg + 4.asUInt(conf.xprlen.W))
//请求memory中instruction的数据,请求地址为pc的值,请求信号为true,当io.imem.resp.valid为假//时,inst输出BUBBLE,当然这里永远都是真的
io.imem.req.bits.addr := pc_reg
io.imem.req.valid := true.B
val inst = Mux(io.imem.resp.valid, io.imem.resp.bits.data, BUBBLE)
//提前指令中rs1,rs2,wb的地址,提前rs1,rs2,wb通用寄存器的编号,这里需要对RISCV指令比较熟悉
// Decode
val rs1_addr = inst(RS1_MSB, RS1_LSB)
val rs2_addr = inst(RS2_MSB, RS2_LSB)
val wb_addr = inst(RD_MSB, RD_LSB)
//声明wb_data信号,位宽为conf.xprlen的长度
val wb_data = Wire(UInt(conf.xprlen.W))
//声明通用寄存器,32个通用寄存器,通用寄存器的位宽为conf.xprlen
// Register File
val regfile = Mem(UInt(conf.xprlen.W), 32)
//当写寄存器使能了,且写的寄存器编号不为0,同时没有异常生成时,将wb_data赋给相应的寄存器
//这步操作是在时钟上升沿来时完成,类似于always @(posedge clk)
when (io.ctl.rf_wen && (wb_addr != 0.U) && !io.ctl.exception)
{
regfile(wb_addr) := wb_data
}
//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
}
///
//根据inst中提取rs1,rs2的寄存器编号,利用这个编号提取寄存器中的值,若rs1和rs2为0时,则
//rs1_data,rs2_data输出0值
val rs1_data = Mux((rs1_addr != 0.U), regfile(rs1_addr), 0.asUInt(conf.xprlen.W))
val rs2_data = Mux((rs2_addr != 0.U), regfile(rs2_addr), 0.asUInt(conf.xprlen.W))
//如果inst中包含的不是寄存器的编号,即不是rs1,rs2和wb的编号,而是立即数的话,则通过下面
//的步骤将立即数重组,不同指令有不同立即数类型,这点需要看RISCV指令的用户使用册2.2
// immediates
val imm_i = inst(31, 20)
val imm_s = Cat(inst(31, 25), inst(11,7))
val imm_b = Cat(inst(31), inst(7), inst(30,25), inst(11,8))
val imm_u = inst(31, 12)
val imm_j = Cat(inst(31), inst(19,12), inst(20), inst(30,21))
val imm_z = Cat(Fill(27,0.U), 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是rs1_data(寄存器的值),还是U类立即数imm_u_sext,
//还是Z类的立即数imm_z,或者是0
val alu_op1 = MuxCase(0.U, Array(
(io.ctl.op1_sel === OP1_RS1) -> 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是rs2_data(寄存器的值),还是pc寄存器的值,还是I类立即数
//imm_u_sext,还是S类的立即数imm_z,或者是0
val alu_op2 = MuxCase(0.U, Array(
(io.ctl.op2_sel === OP2_RS2) -> rs2_data,
(io.ctl.op2_sel === OP2_PC) -> pc_reg,
(io.ctl.op2_sel === OP2_IMI) -> imm_i_sext,
(io.ctl.op2_sel === OP2_IMS) -> imm_s_sext
)).toUInt
//定义一个alu_out信号,位宽为conf.xprlen
// ALU
val alu_out = Wire(UInt(conf.xprlen.W))
//定义一个alu_shamt信号,取alu_op2数据的低5位
val alu_shamt = alu_op2(4,0).toUInt
//进行ALU运算,根据ctl.alu_fun信号来确定运算的类型,->为运算的逻辑,最后将运算值赋给alu_out
alu_out := MuxCase(0.U, Array(
(io.ctl.alu_fun === ALU_ADD) -> (alu_op1 + alu_op2).toUInt,
(io.ctl.alu_fun === ALU_SUB) -> (alu_op1 - alu_op2).toUInt,
(io.ctl.alu_fun === ALU_AND) -> (alu_op1 & alu_op2).toUInt,
(io.ctl.alu_fun === ALU_OR) -> (alu_op1 | alu_op2).toUInt,
(io.ctl.alu_fun === ALU_XOR) -> (alu_op1 ^ alu_op2).toUInt,
(io.ctl.alu_fun === ALU_SLT) -> (alu_op1.toSInt < alu_op2.toSInt).toUInt,
(io.ctl.alu_fun === ALU_SLTU) -> (alu_op1 < alu_op2).toUInt,
(io.ctl.alu_fun === ALU_SLL) -> ((alu_op1 << alu_shamt)(conf.xprlen-1, 0)).toUInt,
(io.ctl.alu_fun === ALU_SRA) -> (alu_op1.toSInt >> alu_shamt).toUInt,
(io.ctl.alu_fun === ALU_SRL) -> (alu_op1 >> alu_shamt).toUInt,
(io.ctl.alu_fun === ALU_COPY1)-> alu_op1
))
//跳转目标的处理
//br_target:当前pc加br的立即数
//jmp_target:当前pc+跳转的立即数
// jump_reg_target:rs1寄存器值+立即数的跳转
// Branch/Jump Target Calculation
br_target := pc_reg + imm_b_sext
jmp_target := pc_reg + imm_j_sext
jump_reg_target := (rs1_data.toUInt + imm_i_sext.toUInt)
// Control Status Registers
//什么一个csr的模块,调用CSRFile()
val csr = Module(new CSRFile())
csr.io := DontCare
//根据inst数据中的(CSR_ADDR_MSB,CSR_ADDR_LSB)来确定是哪个csr寄存器需要被操作
csr.io.decode.csr := inst(CSR_ADDR_MSB,CSR_ADDR_LSB)
//根据ctl.csr_cmd的信号来说明csr的具体操作是什么,如写/读/清除/置位
csr.io.rw.cmd := io.ctl.csr_cmd
//操作csr寄存器的数据
csr.io.rw.wdata := alu_out
//csr寄存器中有一个特别的csr寄存器为instret,用于记录某时刻开始后系统执行了多少条指令
//这里csr模块中的retire就是用于记录执行了多少条指令,当流水线不暂停时,每个
//时钟来临,retiret都会为1,然后instret就自动+1
csr.io.retire := !io.ctl.stall
//csr寄存器记录系统是否有异常
csr.io.exception := io.ctl.exception
//csr模块中的pc信号记录当前pc寄存器的值
csr.io.pc := pc_reg
//若发生异常时,异常跳转的目标由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的运算结果alu_out
//还是mem数据的读取,还是当前pc+4后的值,还是csr寄存器读取的值
// WB Mux
wb_data := MuxCase(alu_out, Array(
(io.ctl.wb_sel === WB_ALU) -> alu_out,
(io.ctl.wb_sel === WB_MEM) -> io.dmem.resp.bits.data,
(io.ctl.wb_sel === WB_PC4) -> pc_plus4,
(io.ctl.wb_sel === WB_CSR) -> csr.io.rw.rdata
))
//dpath模块给cpath模块的数据
//inst为指令的数据,若(rs1_data === rs2_data),则br_eq为1,若有符号数(rs1_data.toSInt < rs2_data.toSInt)
//则br_lt为1,若无符号数(rs1_data.toUInt < rs2_data.toUInt),则br_ltu为1
// datapath to controlpath outputs
io.dat.inst := inst
io.dat.br_eq := (rs1_data === rs2_data)
io.dat.br_lt := (rs1_data.toSInt < rs2_data.toSInt)
io.dat.br_ltu := (rs1_data.toUInt < rs2_data.toUInt)
//dpath模块请求写data到memory中,请求地址为alu_out,写的数据为无符号的rs2_data
// datapath to data memory outputs
io.dmem.req.bits.addr := alu_out
io.dmem.req.bits.data := rs2_data.toUInt
//这部分是打印的内容,与dpath的主体逻辑无关,用于在spike跑仿真时,能打印流水线的相关内容,
//所以这部分我不做详细说明了
// Printout
// pass output through the spike-dasm binary (found in riscv-tools) to turn
// the DASM(%x) into a disassembly string.
printf("Cyc= %d Op1=[0x%x] Op2=[0x%x] W[%c,%d= 0x%x] %c Mem[%d: R:0x%x W:0x%x] PC= 0x%x %c%c DASM(%x)\n"
, csr.io.time(31,0)
, alu_op1
, alu_op2
, Mux(io.ctl.rf_wen, Str("W"), Str("_"))
, wb_addr
, wb_data
, Mux(csr.io.exception, Str("E"), Str(" ")) // EXC -> E
, io.ctl.wb_sel
, io.dmem.resp.bits.data
, io.dmem.req.bits.data
, pc_reg
, Mux(io.ctl.stall, Str("s"), Str(" "))
, Mux(io.ctl.pc_sel === 1.U, Str("B"),
Mux(io.ctl.pc_sel === 2.U, Str("J"),
Mux(io.ctl.pc_sel === 3.U, Str("K"),// JR -> K
Mux(io.ctl.pc_sel === 4.U, Str("X"),// EX -> X
Mux(io.ctl.pc_sel === 0.U, Str(" "), Str("?"))))))
, inst
)
if (PRINT_COMMIT_LOG)
{
when (!io.ctl.stall)
{
// use "sed" to parse out "@@@" from the other printf code above.
val rd = inst(RD_MSB,RD_LSB)
when (io.ctl.rf_wen && rd != 0.U)
{
printf("@@@ 0x%x (0x%x) x%d 0x%x\n", pc_reg, inst, rd, Cat(Fill(32,wb_data(31)),wb_data))
}
.otherwise
{
printf("@@@ 0x%x (0x%x)\n", pc_reg, inst)
}
}
}
}
}