下面是对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时,再补充仿真过程。