提交 daaf393a 编写于 作者: W William Wang

feat(BPU, datapath): add branch predict support for RVC

上级 a207cd54
......@@ -57,4 +57,11 @@ REMUW |BitPat("b0100000_?????_?????_101_?????_0111011")|1
其他要调整的指令
* LX/SX
* ALUI
\ No newline at end of file
* ALUI
# RVC指令集及流水线变动
检查下列指令的nemu实现
```
8010074a: 80fd srli s1,s1,0x1f
```
\ No newline at end of file
......@@ -11,7 +11,7 @@ class TableAddr(idxBits: Int) extends NOOPBundle {
val tag = UInt(tagBits.W)
val idx = UInt(idxBits.W)
val pad = UInt(2.W)
val pad = UInt(1.W)
def fromUInt(x: UInt) = x.asTypeOf(UInt(AddrBits.W)).asTypeOf(this)
def getTag(x: UInt) = fromUInt(x).tag
......@@ -37,6 +37,7 @@ class BPUUpdateReq extends NOOPBundle {
val actualTaken = Output(Bool()) // for branch
val fuOpType = Output(FuOpType())
val btbType = Output(BTBtype())
val isRVC = Output(Bool()) // for ras, save PC+2 to stack if is RVC
}
class BPU1 extends NOOPModule {
......@@ -55,6 +56,7 @@ class BPU1 extends NOOPModule {
val tag = UInt(btbAddr.tagBits.W)
val _type = UInt(2.W)
val target = UInt(AddrBits.W)
val brIdx = UInt(2.W)
}
val btb = Module(new SRAMTemplate(btbEntry(), set = NRbtb, shouldReset = true, holdRead = true, singlePort = true))
......@@ -67,6 +69,7 @@ class BPU1 extends NOOPModule {
// we should latch the input pc for one cycle
val pcLatch = RegEnable(io.in.pc.bits, io.in.pc.valid)
val btbHit = btbRead.tag === btbAddr.getTag(pcLatch) && !flush && RegNext(btb.io.r.req.ready, init = false.B)
// btbHit will ignore pc(1,0). pc(1,0) is used to build brIdx
// PHT
val pht = Mem(NRbtb, UInt(2.W))
......@@ -76,8 +79,10 @@ class BPU1 extends NOOPModule {
val NRras = 16
val ras = Mem(NRras, UInt(AddrBits.W))
val raBrIdxs = Mem(NRras, UInt(2.W))
val sp = Counter(NRras)
val rasTarget = RegEnable(ras.read(sp.value), io.in.pc.valid)
val rasBrIdx = RegEnable(raBrIdxs.read(sp.value), io.in.pc.valid)
// update
val req = WireInit(0.U.asTypeOf(new BPUUpdateReq))
......@@ -87,6 +92,7 @@ class BPU1 extends NOOPModule {
btbWrite.tag := btbAddr.getTag(req.pc)
btbWrite.target := req.actualTarget
btbWrite._type := req.btbType
btbWrite.brIdx := Cat(req.pc(1), ~req.pc(1))
// NOTE: We only update BTB at a miss prediction.
// If a miss prediction is found, the pipeline will be flushed
// in the next cycle. Therefore it is safe to use single-port
......@@ -109,8 +115,9 @@ class BPU1 extends NOOPModule {
}
}
when (req.valid) {
when (req.fuOpType === ALUOpType.call) {
ras.write(sp.value + 1.U, req.pc + 4.U)
when (req.fuOpType === ALUOpType.call) {
ras.write(sp.value + 1.U, Mux(req.isRVC, req.pc + 2.U, req.pc + 4.U))
raBrIdxs.write(sp.value + 1.U, Mux(req.pc(1), 2.U, 1.U))
sp.value := sp.value + 1.U
}
.elsewhen (req.fuOpType === ALUOpType.ret) {
......@@ -119,6 +126,7 @@ class BPU1 extends NOOPModule {
}
io.out.target := Mux(btbRead._type === BTBtype.R, rasTarget, btbRead.target)
io.out.brIdx := Mux(btbRead._type === BTBtype.R, rasBrIdx, btbRead.brIdx)
io.out.valid := btbHit && Mux(btbRead._type === BTBtype.B, phtTaken, true.B)
}
......
......@@ -26,6 +26,7 @@ class DataSrcIO extends NOOPBundle {
class RedirectIO extends NOOPBundle {
val target = Output(UInt(AddrBits.W))
val brIdx = Output(UInt(2.W)) // for RVC
val valid = Output(Bool())
}
......@@ -33,6 +34,7 @@ class IRIDCtrlFlowIO extends NOOPBundle {
val instr = Output(UInt(64.W))
val pc = Output(UInt(AddrBits.W))
val pnpc = Output(UInt(AddrBits.W))
val brIdx = Output(UInt(2.W))
val redirect = new RedirectIO
}
......@@ -40,6 +42,7 @@ class CtrlFlowIO extends NOOPBundle {
val instr = Output(UInt(32.W))
val pc = Output(UInt(AddrBits.W))
val pnpc = Output(UInt(AddrBits.W))
val brIdx = Output(UInt(2.W))
val redirect = new RedirectIO
}
......
......@@ -127,15 +127,19 @@ class IDU extends NOOPModule with HasInstrType {
// val nextState = WireInit(0.U(2.W))
val canGo = WireInit(false.B)
val canIn = WireInit(false.B)
val rvcFinish = pcOffset === 0.U && !isRVC || pcOffset === 4.U && !isRVC || pcOffset === 2.U && isRVC || pcOffset === 6.U && isRVC
// val brIdx = io.in.bits.brIdx
val brIdx = 0.U
val rvcFinish = pcOffset === 0.U && (!isRVC || brIdx(0)) || pcOffset === 4.U && (!isRVC || brIdx(0)) || pcOffset === 2.U && isRVC || pcOffset === 6.U && isRVC
// if brIdx(0) (branch taken at inst with offest 0), ignore the rest part of this instline
// just get next pc and instline from IFU
val rvcNext = pcOffset === 0.U && isRVC || pcOffset === 4.U && isRVC || pcOffset === 2.U && !isRVC
val rvcSpecial = pcOffset === 6.U && !isRVC
val flushIFU = (state === s_idle || state === s_extra) && rvcSpecial && io.in.valid
val pcOut = WireInit(0.U(AddrBits.W))
val pnpcOut = WireInit(0.U(AddrBits.W))
val specialPCR = Reg(UInt(AddrBits.W))
val specialPCR = Reg(UInt(AddrBits.W)) // reg for full inst taht cross 2 inst line
val specialInstR = Reg(UInt(16.W))
val redirectPC = Cat(io.in.bits.pc(31,3), 0.U(3.W))+"b1010".U
val redirectPC = Cat(io.in.bits.pc(31,3), 0.U(3.W))+"b1010".U // IDU can got get full inst from a single inst line
val rvcForceLoadNext = pcOffset === 2.U && !isRVC && io.in.bits.pnpc(2,0) === 4.U
//------------------------------------------------------
// rvcForceLoadNext is used to deal with:
......@@ -145,6 +149,10 @@ class IDU extends NOOPModule with HasInstrType {
// For icache req inst in seq, if there is no rvcForceLoadNext,
// after 8010004e there will be 8010004c instead of 80100050
//------------------------------------------------------
// if there is a j inst in current inst line, a redirect req will be sent by ALU before invalid inst exception being committed
// only for test, add this to pipeline when do real implementation
// val predictBranch = io.in.valid && Mux(io.in.bits.pc(1), io.in.bits.pc + 2.U === io.in.bits.pnpc, io.in.bits.pc + 4.U === io.in.bits.pnpc)
// val flush = rvcSpecial
instr := Mux(state === s_waitnext, Cat(instIn(15,0), specialInstR), LookupTree(pcOffset, List(
"b000".U -> instIn(31,0),
......@@ -155,6 +163,7 @@ class IDU extends NOOPModule with HasInstrType {
io.redirect.target := redirectPC
io.redirect.valid := flushIFU
io.redirect.brIdx := DontCare
when(!io.flush){
switch(state){
......@@ -197,7 +206,7 @@ class IDU extends NOOPModule with HasInstrType {
is(s_waitnext){//require next 64bits, for this inst has size 32 and offset 6
//ignore bp result, use pc+4 instead
pcOut := specialPCR
pnpcOut := specialPCR+4.U
pnpcOut := specialPCR + 4.U
// pnpcOut := Mux(rvcFinish, io.in.bits.pnpc, Mux(isRVC, pcOut+2.U, pcOut+4.U))
canGo := io.in.valid
canIn := false.B
......@@ -226,6 +235,11 @@ class IDU extends NOOPModule with HasInstrType {
io.out.valid := io.in.valid && canGo
io.in.ready := !io.in.valid || (io.out.fire() && canIn)
// NOTE:
// we did not do special opt for cross-line jump inst, hopefully there will not be too much such inst
// for perf counter:
// val crossLineJump = state === s_waitnext && fuType === FuType.alu && fuOpType.isBru()
Debug(){
when(io.out.fire()){
printf("[IDU] pc %x pcin: %x instr %x instrin %x state %x instrType: %x fuType: %x fuOpType: %x\n", pcOut, io.in.bits.pc, instr, io.in.bits.instr, state, instrType, fuType, fuOpType)
......
......@@ -13,7 +13,7 @@ trait HasResetVector {
class IFU extends NOOPModule with HasResetVector {
val io = IO(new Bundle {
val imem = new SimpleBusUC(userBits = AddrBits)
val imem = new SimpleBusUC(userBits = AddrBits + 2)
val pc = Input(UInt(AddrBits.W))
val out = Decoupled(new IRIDCtrlFlowIO)
val redirect = Flipped(new RedirectIO)
......@@ -30,8 +30,14 @@ class IFU extends NOOPModule with HasResetVector {
val bp1 = Module(new BPU1)
// predicted next pc
val pnpc = bp1.io.out.target
val pbrIdx = bp1.io.out.brIdx
// val npc = Mux(io.redirect.valid, io.redirect.target, Mux(io.redirectRVC.valid, io.redirectRVC.target, Mux(bp1.io.out.valid, pnpc, snpc)))
val npc = Mux(io.redirect.valid, io.redirect.target, Mux(io.redirectRVC.valid, io.redirectRVC.target, snpc))
val brIdx = Wire(UInt(2.W))
// brIdx(0) -> branch at pc offset 0 (mod 4)
// brIdx(1) -> branch at pc offset 2 (mod 4)
brIdx := Mux(io.redirect.valid, 0.U, Mux(io.redirectRVC.valid, 0.U, pbrIdx))
//TODO: BP will be disabled shortly after a redirect request
bp1.io.in.pc.valid := io.imem.req.fire() // only predict when Icache accepts a request
bp1.io.in.pc.bits := npc // predict one cycle early
......@@ -55,14 +61,15 @@ class IFU extends NOOPModule with HasResetVector {
io.imem.req.bits.addr := Cat(pc(AddrBits-1,1),0.U(1.W))//cache will treat it as Cat(pc(63,3),0.U(3.W))
io.imem.req.bits.size := "b11".U
io.imem.req.bits.cmd := SimpleBusCmd.read
io.imem.req.bits.user := npc
io.imem.req.bits.user := Cat(brIdx, npc)
io.imem.resp.ready := io.out.ready || io.flushVec(0)
io.out.bits := DontCare
io.out.bits.pc := io.pc
//inst path only uses 32bit inst, get the right inst according to pc(2)
io.out.bits.instr := io.imem.resp.bits.rdata
io.out.bits.pnpc := io.imem.resp.bits.user
io.out.bits.pnpc := io.imem.resp.bits.user(AddrBits-1,0)
io.out.bits.brIdx := io.imem.resp.bits.user(AddrBits+1,AddrBits)
io.out.valid := io.imem.resp.valid && !io.flushVec(0)
Debug(){
......
......@@ -84,7 +84,7 @@ class NOOP(implicit val p: NOOPConfig) extends NOOPModule {
isu.io.forward <> exu.io.forward
io.imem <> (if (HasIcache) {
val icache = Module(new Cache(ro = true, name = "icache", userBits = AddrBits))
val icache = Module(new Cache(ro = true, name = "icache", userBits = AddrBits + 2)) // userBits = AddrBits + BrIdxBits
icache.io.in <> ifu.io.imem
icache.io.flush := Fill(2, ifu.io.flushVec(0) | ifu.io.bpFlush)
ifu.io.pc := icache.io.addr
......
......@@ -100,13 +100,15 @@ class ALU extends NOOPModule {
// val pcPlus2 = ALUOpType.pcPlus2(func)
val taken = LookupTree(ALUOpType.getBranchType(func), branchOpTable) ^ ALUOpType.isBranchInvert(func)
val target = Mux(isBranch, io.cfIn.pc + io.offset, adderRes)(AddrBits-1,0)
val predictWrong = true.B//(io.redirect.target =/= io.cfIn.pnpc)
io.redirect.target := Mux(!taken && isBranch, Mux(io.cfIn.instr(1,0)=/="b11".U, io.cfIn.pc + 2.U, io.cfIn.pc + 4.U), target)
val predictWrong = (io.redirect.target =/= io.cfIn.pnpc)
val isRVC = (io.cfIn.instr(1,0) =/= "b11".U)
io.redirect.target := Mux(!taken && isBranch, Mux(isRVC, io.cfIn.pc + 2.U, io.cfIn.pc + 4.U), target)
// with branch predictor, this is actually to fix the wrong prediction
io.redirect.valid := valid && isBru && predictWrong
// may be can move to ISU to calculate pc + 4
// this is actually for jal and jalr to write pc + 4 to rd
io.out.bits := Mux(isBru, Mux(io.cfIn.instr(1,0)==="b11".U, io.cfIn.pc + 4.U, io.cfIn.pc + 2.U), aluRes)
// may be can be moved to ISU to calculate pc + 4
// this is actually for jal and jalr to write pc + 4/2 to rd
io.redirect.brIdx := DontCare
io.out.bits := Mux(isBru, Mux(!isRVC, io.cfIn.pc + 4.U, io.cfIn.pc + 2.U), aluRes)
// when(pcPlus2 && isBru){
// printf("CJALR %x %x \n ", io.cfIn.instr, io.cfIn.pc)
// }
......@@ -134,6 +136,7 @@ class ALU extends NOOPModule {
bpuUpdateReq.actualTaken := taken
bpuUpdateReq.fuOpType := func
bpuUpdateReq.btbType := LookupTree(func, RV32I_BRUInstr.bruFuncTobtbTypeTable)
bpuUpdateReq.isRVC := isRVC
BoringUtils.addSource(RegNext(bpuUpdateReq), "bpuUpdateReq")
......
......@@ -104,6 +104,7 @@ class CSR(implicit val p: NOOPConfig) extends NOOPModule with HasCSRConst {
io.redirect.valid := (valid && func === CSROpType.jmp) || isException
io.redirect.target := Mux(isMret, mepc, mtvec)
io.redirect.brIdx := DontCare
when (io.redirect.valid && !isMret) {
mepc := io.cfIn.pc
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册