riscv-sodor-rv32_2stage(2)

riscv-sodor-rv32_2stage(2)

在这里插入图片描述
接着分析的是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 <exe_ rs2_data.toUInt),则br_ltu为1
   // datapath to controlpath outputs
   io.dat.inst   := exe_reg_inst
   io.dat.br_eq  := (exe_rs1_data === exe_rs2_data)
   io.dat.br_lt  := (exe_rs1_data.toSInt < exe_rs2_data.toSInt) 
   io.dat.br_ltu := (exe_rs1_data.toUInt < exe_rs2_data.toUInt)
   
   //dpath模块请求写data到memory中,请求地址为exe_alu_out,写的数据为无符号的exe_rs2_data
   // datapath to data memory outputs
   io.dmem.req.bits.addr := exe_alu_out
   io.dmem.req.bits.data := exe_rs2_data.toUInt 
         

   //时间戳计数器和失效指令计数器
   //以系统时钟为触发上升沿,记录运行的时钟数
   // Time Stamp Counter & Retired Instruction Counter 
   val tsc_reg = Reg(init=0.asUInt(conf.xprlen.W))
   tsc_reg := tsc_reg + 1.U

   //记录失效指令的数量,当流水线暂停,或者cpath发出kill信号时,irt_reg在时钟上升沿来临时加1
   val irt_reg = Reg(init=0.asUInt(conf.xprlen.W))
   when (!io.ctl.stall && !io.ctl.if_kill) { irt_reg := irt_reg + 1.U }
        
   //这部分是打印的内容,与dpath的主体逻辑无关,用于在spike跑仿真时,能打印流水线的相关内容,
   //所以这部分我不做详细说明了

   // Printout
   printf("Cyc= %d Op1=[0x%x] Op2=[0x%x] W[%c,%d= 0x%x] PC= (0x%x,0x%x) [%x,%x] %c%c%c Exe: DASM(%x)\n"
      , tsc_reg(31,0)
      , exe_alu_op1
      , exe_alu_op2
      , Mux(io.ctl.rf_wen, Str("W"), Str("_"))
      , exe_wbaddr
      , exe_wbdata
      , if_reg_pc
      , exe_reg_pc
      , if_inst(6,0)
      , exe_reg_inst(6,0)
      , Mux(io.ctl.stall, Str("S"), Str(" ")) //stall -> 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 的基本一致,所以就不再作过多的介绍。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值