下面是对translator模块的具体实现进行说明。

2、Translator
类TranslatorExample,需要输入opcodes参数,里面使用new创建TranslatorExampleModuleImp的对象,且输入额外参数nPTWPorts,值为1。

class  TranslatorExample(opcodes: OpcodeSet)(implicit p: Parameters) extends LazyRoCC(opcodes, nPTWPorts = 1) {
  override lazy val module = new TranslatorExampleModuleImp(this)
}

TranslatorExampleModuleImp类中混入LazyRoCCModuleImp和HasCoreParameters。

  • 声明req_addr的值为寄存器,宽度为coreMaxAddrBits,这里coreMaxAddrBits=32。
  • 声明req_rd的值为寄存器,位宽与io.resp.bits.rd的位宽一样。
  • 声明req_offset的值为req_addr(11,0),取req_addr的低12位,pgIdxBits在tile/BaseTile.scala中定义,def pgIdxBits: Int = 12。
  • 声明req_vpn为req_addr(coreMaxAddrBits - 1, pgIdxBits)即req_addr(31, 12)。
    这里将req_addr分为了req_offset和req_vpn,一个是偏移值,一个是虚拟页面编号值。由此可知一个虚拟页面有64B数据。
  • 声明pte的值为寄存器,类型为PTE类,类PTE是一个多信号的组合,类PTE可以查看rocket/PTW.scala。
    这里 Enum() 生成四个Bits类型的字面常量,分别为s_idle、s_ptw_req、s_ptw_resp和s_resp
    ,作为有限状态机的状态,同时声明state为寄存器,用于存储状态机的状态值,初始值为s_idle。
    当state为s_idle时,io.cmd.ready为1,也就是Translator模块处于空闲状态。
  • 当io.cmd.fire()为1时,也就是io.cmd.ready为1 && io.cmd.valid为1,也就是Translator模块处于空闲,且core那边有有效指令传输过来,那就将io.cmd.bits.inst.rd赋给req_rd,将 io.cmd.bits.rs1赋给req_addr,state更新状态为s_resp,上述操作均为时序操作。
  • 定义私有成员ptw,仅在此类中可见。ptw的值为io.ptw端口中的第0号ptw信号组,存在多组ptw端口,部分用于pmp的用途,在TranslatorExampleModuleImp类中,后面的ptw信号都是这个私有信号。
  • 当ptw.req.ready为1和ptw.req.valid为1时,状态机转变为s_ptw_resp。
  • 当状态机为s_ptw_resp ,且ptw.resp.valid为1时,将ptw端口返回的pte信号组赋给类中的pte寄存器。同时将状态改为s_resp。
  • 当io.resp.ready为1和io.resp.valid为1时,状态机跳转回s_idle态。
  • 当状态机为s_ptw_req时,ptw.req.valid置1。
  • ptw.req.bits.valid永远为1。
  • 将req_vpn的值赋给ptw.req.bits.bits.addr。
  • 当状态机为s_resp时,io.resp.valid置1。
  • 将req_rd的值赋给io.resp.bits.rd。
  • io.resp.bits.data的值是一个mux输出,选择信号为pte.leaf(),在rocket/PTW.scala中定义。具体为def leaf(dummy: Int = 0) = v && (r || (x && !w)) && a。v、r、x、w和a代表的含义可以查看特权指令集的4.3.1Addressing and Memory Protection,此部分虚拟页面相关。当pte.leaf()为1时,输出pet.ppn和req_offset的拼接;为0时,输出位宽为xLen的有符号数-1,并将这个有符号的-1最终强制转为无符号数,也就是xLen’hFFFF……FFFF。
  • 当状态机不为s_idle时,io.busy均输出为1。
  • 强制接io.interrupt 为0,RTL代码生成时会优化此信号。
  • 强制接io.mem.req.valid为0,RTL代码生成时会优化相关信号。
class TranslatorExampleModuleImp(outer: TranslatorExample)(implicit p: Parameters) extends LazyRoCCModuleImp(outer)
    with HasCoreParameters {
  val req_addr = Reg(UInt(width = coreMaxAddrBits))
  val req_rd = Reg(io.resp.bits.rd)
  val req_offset = req_addr(pgIdxBits - 1, 0)
  val req_vpn = req_addr(coreMaxAddrBits - 1, pgIdxBits)
  val pte = Reg(new PTE)

  val s_idle :: s_ptw_req :: s_ptw_resp :: s_resp :: Nil = Enum(Bits(), 4)
  val state = Reg(init = s_idle)

  io.cmd.ready := (state === s_idle)

  when (io.cmd.fire()) {
    req_rd := io.cmd.bits.inst.rd
    req_addr := io.cmd.bits.rs1
    state := s_ptw_req
  }

  private val ptw = io.ptw(0)

  when (ptw.req.fire()) { state := s_ptw_resp }

  when (state === s_ptw_resp && ptw.resp.valid) {
    pte := ptw.resp.bits.pte
    state := s_resp
  }

  when (io.resp.fire()) { state := s_idle }

  ptw.req.valid := (state === s_ptw_req)
  ptw.req.bits.valid := true.B
  ptw.req.bits.bits.addr := req_vpn

  io.resp.valid := (state === s_resp)
  io.resp.bits.rd := req_rd
  io.resp.bits.data := Mux(pte.leaf(), Cat(pte.ppn, req_offset), SInt(-1, xLen).asUInt)

  io.busy := (state =/= s_idle)
  io.interrupt := Bool(false)
  io.mem.req.valid := Bool(false)
}

translator模块的实际功能是利用rs1[31:0]所存的地址值去读取对应虚拟页面中存储的数据。

由于我现在生成的RTL没有MMU部分,也就是没有虚拟地址,那么也不会有PTW部分,所以关于translator模块的仿真就不做了,以后如果生成带有虚拟地址的RTL时,再补充仿真过程。