riscv-sodor-rv32_1stage(2)

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)
         }
      }
   }   
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值