diff --git a/debug/Makefile b/debug/Makefile index 38a6cc13feca9a3760e7a39e97e3739d4abf7cea..f6c16e8a48a38075638a4361eb3ddb81406a5f69 100644 --- a/debug/Makefile +++ b/debug/Makefile @@ -3,7 +3,7 @@ NANOS_HOME ?= $(AM_HOME)/../nanos-lite SINGLETEST = ALL=min3 B ?= 0 -E ?= 0 +E ?= -1 V ?= ALL #V ?= OFF EMU_ARGS = B=$(B) E=$(E) V=$(V) @@ -13,14 +13,14 @@ EMU_ARGS = B=$(B) E=$(E) V=$(V) # ------------------------------------------------------------------ cpu: - $(MAKE) -C $(AM_HOME)/tests/cputest $(ARCH) ALL=dummy $(EMU_ARGS) run + $(MAKE) -C $(AM_HOME)/tests/cputest $(ARCH) ALL=shift $(EMU_ARGS) run 2>&1 | tee > ras_shift.log # ------------------------------------------------------------------ # run different test sets # ------------------------------------------------------------------ cputest: - $(MAKE) -C $(AM_HOME)/tests/cputest $(ARCH) $(EMU_ARGS) run + $(MAKE) -C $(AM_HOME)/tests/cputest $(ARCH) $(EMU_ARGS) run 2>&1 | tee > cpu.log #2 > cpu.log cat cpu.log | grep different cat cpu.log | grep IPC diff --git a/src/main/scala/xiangshan/Bundle.scala b/src/main/scala/xiangshan/Bundle.scala index afd6150263453f1166a4ea96cdcb5d0ea26b3df4..cc1d3f52e4dd9cb0a5688a3310843c482e1fd803 100644 --- a/src/main/scala/xiangshan/Bundle.scala +++ b/src/main/scala/xiangshan/Bundle.scala @@ -58,6 +58,7 @@ class BranchInfo extends XSBundle { val tageMeta = new TageMeta val rasSp = UInt(log2Up(RasSize).W) val rasTopCtr = UInt(8.W) + val rasToqAddr = UInt(VAddrBits.W) def apply(histPtr: UInt, tageMeta: TageMeta, rasSp: UInt, rasTopCtr: UInt) = { this.histPtr := histPtr diff --git a/src/main/scala/xiangshan/frontend/BPU.scala b/src/main/scala/xiangshan/frontend/BPU.scala index dbf641d7f7b3e4d025c04450e42358aabb8b8bbf..774bd7f271f47fd927a8b18f589967c928b4ca1d 100644 --- a/src/main/scala/xiangshan/frontend/BPU.scala +++ b/src/main/scala/xiangshan/frontend/BPU.scala @@ -106,6 +106,8 @@ abstract class BPUStage extends XSModule { val pred = Decoupled(new BranchPrediction) val out = Decoupled(new BPUStageIO) val predecode = Flipped(ValidIO(new Predecode)) + val recover = Flipped(ValidIO(new BranchUpdateInfo)) + } val io = IO(new DefaultIO) @@ -235,8 +237,8 @@ class BPUStage2 extends BPUStage { class BPUStage3 extends BPUStage { - io.out.valid := predValid && io.predecode.valid && !io.flush + io.out.valid := predValid && io.predecode.valid && !io.flush // TAGE has its own pipelines and the // response comes directly from s3, // so we do not use those from inLatch @@ -252,11 +254,12 @@ class BPUStage3 extends BPUStage { val brs = pdMask & Reverse(Cat(pds.map(_.isBr))) val jals = pdMask & Reverse(Cat(pds.map(_.isJal))) val jalrs = pdMask & Reverse(Cat(pds.map(_.isJalr))) - // val calls = pdMask & Reverse(Cat(pds.map(_.isCall))) - // val rets = pdMask & Reverse(Cat(pds.map(_.isRet))) + val calls = pdMask & Reverse(Cat(pds.map(_.isCall))) + val rets = pdMask & Reverse(Cat(pds.map(_.isRet))) + val RVCs = pdMask & Reverse(Cat(pds.map(_.isRVC))) - // val callIdx = PriorityEncoder(calls) - // val retIdx = PriorityEncoder(rets) + val callIdx = PriorityEncoder(calls) + val retIdx = PriorityEncoder(rets) val brTakens = if (EnableBPD) { @@ -274,6 +277,27 @@ class BPUStage3 extends BPUStage { else { VecInit((0 until PredictWidth).map(i => brs(i) && !bimTakens(i)))}) targetSrc := inLatch.resp.btb.targets + //RAS + if(EnableRAS){ + val ras = Module(new RAS) + ras.io <> DontCare + ras.io.pc.bits := inLatch.pc + ras.io.pc.valid := io.out.fire()//predValid + ras.io.is_ret := rets.orR && (retIdx === jmpIdx) && io.predecode.valid + ras.io.callIdx.valid := calls.orR && (callIdx === jmpIdx) && io.predecode.valid + ras.io.callIdx.bits := callIdx + ras.io.isRVC := (calls & RVCs).orR //TODO: this is ugly + ras.io.recover := io.recover + + for(i <- 0 until PredictWidth){ + io.out.bits.brInfo(i).rasSp := ras.io.branchInfo.rasSp + io.out.bits.brInfo(i).rasTopCtr := ras.io.branchInfo.rasTopCtr + io.out.bits.brInfo(i).rasToqAddr := ras.io.branchInfo.rasToqAddr + } + takens := VecInit((0 until PredictWidth).map(i => (brTakens(i) || jalrs(i)) && btbHits(i) || jals(i)|| rets(i))) + when(ras.io.is_ret && ras.io.out.valid){targetSrc(retIdx) := ras.io.out.bits.target} + } + lastIsRVC := pds(lastValidPos).isRVC when (lastValidPos === 1.U) { lastHit := pdMask(1) | @@ -378,6 +402,11 @@ abstract class BaseBPU extends XSModule with BranchPredictorComponents{ io.branchInfo.bits := s3.io.out.bits.brInfo s3.io.out.ready := io.branchInfo.ready + s1.io.recover <> DontCare + s2.io.recover <> DontCare + s3.io.recover.valid <> io.inOrderBrInfo.valid + s3.io.recover.bits <> io.inOrderBrInfo.bits.ui + XSDebug(io.branchInfo.fire(), "branchInfo sent!\n") for (i <- 0 until PredictWidth) { val b = io.branchInfo.bits(i) @@ -488,3 +517,16 @@ class BPU extends BaseBPU { } } + +object BPU{ + def apply(enableBPU: Boolean = true) = { + if(enableBPU) { + val BPU = Module(new BPU) + BPU + } + else { + val FakeBPU = Module(new FakeBPU) + FakeBPU + } + } +} \ No newline at end of file diff --git a/src/main/scala/xiangshan/frontend/IFU.scala b/src/main/scala/xiangshan/frontend/IFU.scala index 480925caae352764ea69e8d96a72e8b80cb1a70d..b31b2a12febb5478583bb1761efbbeef5faf65ad 100644 --- a/src/main/scala/xiangshan/frontend/IFU.scala +++ b/src/main/scala/xiangshan/frontend/IFU.scala @@ -30,7 +30,7 @@ class IFUIO extends XSBundle class IFU extends XSModule with HasIFUConst { val io = IO(new IFUIO) - val bpu = if (EnableBPU) Module(new BPU) else Module(new FakeBPU) + val bpu = BPU(EnableBPU) val pd = Module(new PreDecode) val if2_redirect, if3_redirect, if4_redirect = WireInit(false.B) diff --git a/src/main/scala/xiangshan/frontend/RAS.scala b/src/main/scala/xiangshan/frontend/RAS.scala new file mode 100644 index 0000000000000000000000000000000000000000..5fee01244599d888620ac5c274744aaef03d8eac --- /dev/null +++ b/src/main/scala/xiangshan/frontend/RAS.scala @@ -0,0 +1,197 @@ +package xiangshan.frontend + +import chisel3._ +import chisel3.util._ +import xiangshan._ +import xiangshan.backend.ALUOpType +import utils._ + +class RAS extends BasePredictor +{ + class RASResp extends Resp + { + val target =UInt(VAddrBits.W) + } + + class RASBranchInfo extends Meta + { + val rasSp = UInt(log2Up(RasSize).W) + val rasTopCtr = UInt(8.W) + val rasToqAddr = UInt(VAddrBits.W) + } + + class RASIO extends DefaultBasePredictorIO + { + val is_ret = Input(Bool()) + val callIdx = Flipped(ValidIO(UInt(log2Ceil(PredictWidth).W))) + val isRVC = Input(Bool()) + val recover = Flipped(ValidIO(new BranchUpdateInfo)) + val out = ValidIO(new RASResp) + val branchInfo = Output(new RASBranchInfo) + } + + def rasEntry() = new Bundle { + val retAddr = UInt(VAddrBits.W) + val ctr = UInt(8.W) // layer of nested call functions + } + override val io = IO(new RASIO) + + // val ras_0 = Reg(Vec(RasSize, rasEntry())) //RegInit(0.U)asTypeOf(Vec(RasSize,rasEntry)) cause comb loop + // val ras_1 = Reg(Vec(RasSize, rasEntry())) + // val sp_0 = RegInit(0.U(log2Up(RasSize).W)) + // val sp_1 = RegInit(0.U(log2Up(RasSize).W)) + // val choose_bit = RegInit(false.B) //start with 0 + // val spec_ras = Mux(choose_bit, ras_1, ras_0) + // val spec_sp = Mux(choose_bit,sp_1,sp_0) + // val commit_ras = Mux(choose_bit, ras_0, ras_1) + // val commit_sp = Mux(choose_bit,sp_0,sp_1) + + val spec_ras = Reg(Vec(RasSize, rasEntry())) + val spec_sp = RegInit(0.U(log2Up(RasSize).W)) + val commit_ras = Reg(Vec(RasSize, rasEntry())) + val commit_sp = RegInit(0.U(log2Up(RasSize).W)) + + + val spec_is_empty = spec_sp === 0.U + val spec_is_full = spec_sp === (RasSize - 1).U + + val spec_ras_top_entry = spec_ras(spec_sp-1.U) + val spec_ras_top_addr = spec_ras_top_entry.retAddr + val spec_ras_top_ctr = spec_ras_top_entry.ctr + //no need to pass the ras branchInfo + io.branchInfo.rasSp := DontCare + io.branchInfo.rasTopCtr := DontCare + io.branchInfo.rasToqAddr := DontCare + + io.out.valid := !spec_is_empty && io.is_ret + XSDebug("----------------RAS(spec)----------------\n") + XSDebug(" index addr ctr \n") + for(i <- 0 until RasSize){ + XSDebug(" (%d) 0x%x %d",i.U,spec_ras(i).retAddr,spec_ras(i).ctr) + when(i.U === spec_sp){XSDebug(false,true.B," <----sp")} + XSDebug(false,true.B,"\n") + } + XSDebug("----------------RAS(commit)----------------\n") + XSDebug(" index addr ctr \n") + for(i <- 0 until RasSize){ + XSDebug(" (%d) 0x%x %d",i.U,commit_ras(i).retAddr,commit_ras(i).ctr) + when(i.U === commit_sp){XSDebug(false,true.B," <----sp")} + XSDebug(false,true.B,"\n") + } + // update spec RAS + // speculative update RAS + val spec_push = !spec_is_full && io.callIdx.valid && io.pc.valid + val spec_pop = !spec_is_empty && io.is_ret && io.pc.valid + val spec_new_addr = io.pc.bits + (io.callIdx.bits << 1.U) + Mux(io.isRVC,2.U,4.U) + val spec_ras_write = WireInit(0.U.asTypeOf(rasEntry())) + val sepc_alloc_new = spec_new_addr =/= spec_ras_top_addr + when (spec_push) { + //push + spec_ras_write.ctr := 1.U + spec_ras_write.retAddr := spec_new_addr + when(sepc_alloc_new){ + spec_sp := spec_sp + 1.U + spec_ras(spec_sp) := spec_ras_write + }.otherwise{ + spec_ras_top_ctr := spec_ras_top_ctr + 1.U + } + XSDebug("(spec_ras)push inAddr: 0x%x inCtr: %d | allocNewEntry:%d | sp:%d \n",spec_ras_write.retAddr,spec_ras_write.ctr,sepc_alloc_new,spec_sp.asUInt) + } + + when (spec_pop) { + //pop + when (spec_ras_top_ctr === 1.U) { + spec_sp := Mux(spec_sp === 0.U, 0.U, spec_sp - 1.U) + }.otherwise { + spec_ras_top_ctr := spec_ras_top_ctr - 1.U + } + XSDebug("(spec_ras)pop outValid:%d outAddr: 0x%x \n",io.out.valid,io.out.bits.target) + } + io.out.bits.target := spec_ras_top_addr + // TODO: back-up stack for ras + // use checkpoint to recover RAS + + val commit_is_empty = commit_sp === 0.U + val commit_is_full = commit_sp === (RasSize - 1).U + val commit_ras_top_entry = commit_ras(commit_sp-1.U) + val commit_ras_top_addr = commit_ras_top_entry.retAddr + val commit_ras_top_ctr = commit_ras_top_entry.ctr + //update commit ras + val commit_push = !commit_is_full && io.recover.valid && io.recover.bits.pd.isCall + val commit_pop = !commit_is_empty && io.recover.valid && io.recover.bits.pd.isRet + val commit_new_addr = io.recover.bits.pc + 4.U //TODO: consider RVC + val commit_ras_write = WireInit(0.U.asTypeOf(rasEntry())) + val commit_alloc_new = commit_new_addr =/= commit_ras_top_addr + when (commit_push) { + //push + commit_ras_write.ctr := 1.U + commit_ras_write.retAddr := commit_new_addr + when(commit_alloc_new){ + commit_sp := commit_sp + 1.U + commit_ras(commit_sp) := commit_ras_write + }.otherwise{ + commit_ras_top_ctr := commit_ras_top_ctr + 1.U + } + XSDebug("(commit_ras)push inAddr: 0x%x inCtr: %d | allocNewEntry:%d | sp:%d \n",commit_ras_write.retAddr,commit_ras_write.ctr,sepc_alloc_new,commit_sp.asUInt) + } + + when (commit_pop) { + //pop + when (commit_ras_top_ctr === 1.U) { + commit_sp := Mux(commit_sp === 0.U, 0.U, commit_sp - 1.U) + }.otherwise { + commit_ras_top_ctr := commit_ras_top_ctr - 1.U + } + XSDebug("(commit_ras)pop outValid:%d outAddr: 0x%x \n",io.out.valid,io.out.bits.target) + } + + val copy_valid = io.recover.valid && io.recover.bits.isMisPred + val copy_next = RegNext(copy_valid) + XSDebug("copyValid:%d copyNext:%d \n",copy_valid,copy_next) + when(copy_next) + { + for(i <- 0 until RasSize) + { + spec_ras(i) := commit_ras(i) + spec_sp := commit_sp + } + } + + // val recoverSp = io.recover.bits.brInfo.rasSp + // val recoverCtr = io.recover.bits.brInfo.rasTopCtr + // val recoverAddr = io.recover.bits.brInfo.rasToqAddr + // val recover_top = ras(recoverSp - 1.U) + // when (recover_valid) { + // sp := recoverSp + // recover_top.ctr := recoverCtr + // recover_top.retAddr := recoverAddr + // XSDebug("RAS update: SP:%d , Ctr:%d \n",recoverSp,recoverCtr) + // } + // val recover_and_push = recover_valid && push + // val recover_and_pop = recover_valid && pop + // val recover_alloc_new = new_addr =/= recoverAddr + // when(recover_and_push) + // { + // when(recover_alloc_new){ + // sp := recoverSp + 1.U + // ras(recoverSp).retAddr := new_addr + // ras(recoverSp).ctr := 1.U + // recover_top.retAddr := recoverAddr + // recover_top.ctr := recoverCtr + // } .otherwise{ + // sp := recoverSp + // recover_top.ctr := recoverCtr + 1.U + // recover_top.retAddr := recoverAddr + // } + // } .elsewhen(recover_and_pop) + // { + // io.out.bits.target := recoverAddr + // when ( recover_top.ctr === 1.U) { + // sp := recoverSp - 1.U + // }.otherwise { + // sp := recoverSp + // recover_top.ctr := recoverCtr - 1.U + // } + // } + +} \ No newline at end of file diff --git a/src/test/scala/xiangshan/frontend/RASTest.scala b/src/test/scala/xiangshan/frontend/RASTest.scala new file mode 100644 index 0000000000000000000000000000000000000000..8a3720a94675296315a003d51dbfcf572fe46862 --- /dev/null +++ b/src/test/scala/xiangshan/frontend/RASTest.scala @@ -0,0 +1,88 @@ +package xiangshan.frontend + +import chisel3._ +import chiseltest._ +import org.scalatest._ +import xiangshan.testutils._ + + +class RASTest extends FlatSpec +with ChiselScalatestTester +with Matchers +with ParallelTestExecution +with HasPartialDecoupledDriver { + it should "test RASTest" in { + test(new RAS) { c => + def spec_push(pc: Long,callIdx: Int){ + c.io.callIdx.valid.poke(true.B) + c.io.callIdx.bits.poke(callIdx.U) + c.io.pc.valid.poke(true.B) + c.io.pc.bits.poke(pc.U) + c.clock.step() + c.io.callIdx.valid.poke(false.B) + } + + def spec_pop(){ + c.io.is_ret.poke(true.B) + //c.io.out.bits.target.expect(rigth_target.U) + c.clock.step() + c.io.is_ret.poke(false.B) + } + + def commit_push(pc: Long){ + c.io.recover.valid.poke(true.B) + c.io.recover.bits.isMisPred.poke(false.B) + c.io.recover.bits.pd.isCall.poke(true.B) + c. io.recover.bits.pc.poke(pc.U) + c.clock.step() + c.io.recover.valid.poke(false.B) + c.io.recover.bits.pd.isCall.poke(false.B) + } + + def commit_pop(){ + c.io.recover.valid.poke(true.B) + c.io.recover.bits.pd.isRet.poke(true.B) + c.clock.step() + c.io.recover.valid.poke(false.B) + c.io.recover.bits.pd.isRet.poke(false.B) + } + + + // def update_pop(sp:Int,ctr:Int,addr:Long){ + // c.io.recover.valid.poke(true.B) + // c.io.recover.bits.isMisPred.poke(true.B) + // c.io.recover.bits.brInfo.rasToqAddr.poke(addr.U) + // c.io.recover.bits.brInfo.rasTopCtr.poke(ctr.U) + // c.io.recover.bits.brInfo.rasSp.poke(sp.U) + // pop(right_target=addr) + // c.io.out.bits.target.expect(addr.U) + // } + + // def update_push(sp:Int,ctr:Int,addr:Long,pc: Long,callIdx: Int){ + // c.io.recover.valid.poke(true.B) + // c.io.recover.bits.isMisPred.poke(true.B) + // c.io.recover.bits.brInfo.rasToqAddr.poke(addr.U) + // c.io.recover.bits.brInfo.rasTopCtr.poke(ctr.U) + // c.io.recover.bits.brInfo.rasSp.poke(sp.U) + // push(pc,callIdx) + // } + + //update_pop(sp=2,ctr=1,addr=0x60002020+5*2+4) + spec_push(pc=0x60000010,callIdx=2) + spec_push(pc=0x60000014,callIdx=3) + commit_push(pc=0x60000028) + commit_push(pc=0x60000030) + spec_pop() + spec_pop() + c.io.recover.valid.poke(true.B) + c.io.recover.bits.isMisPred.poke(true.B) + commit_pop() + commit_pop() + + + // pop(rigth_target=0x60002000+8*2+4) + // pop(rigth_target=0x60002000+8*2+4) + // pop(rigth_target=0x60002000+8*2+4) + } + } +} \ No newline at end of file