diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..002683cc3 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,41 @@ +# This file describes the GitHub Actions workflow for continuous integration of rocket-chip. +# +# See +# https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions +# for API reference documentation on this file format. + +name: Mill Continuous Integration +env: + USER: runner + +on: + push: + branches: + - split + pull_request: + branches: + - split + +jobs: + riscv-test: + name: riscv-tests + runs-on: ubuntu-latest + strategy: + matrix: + config: [rv32, rv64] + steps: + - uses: actions/checkout@v2 + with: + submodules: 'true' + + - uses: cachix/install-nix-action@v19 + with: + install_url: https://releases.nixos.org/nix/nix-2.13.3/install + nix_path: nixpkgs=channel:nixos-unstable + + - name: Coursier Cache + uses: coursier/cache-action@v5 + + - name: run riscv-tests + run: | + nix --experimental-features 'nix-command flakes' develop -c mill -i -j 0 tests.riscvtests.run[${{ matrix.config }}] diff --git a/build.sc b/build.sc index b8b204bcc..b3f1252be 100644 --- a/build.sc +++ b/build.sc @@ -227,6 +227,10 @@ object cosim extends Module { class emulator(xLen: String) extends Module { + val ncores: Int = Runtime.getRuntime.availableProcessors() + + val emulatorCores: Int = if(ncores > 8) 8 else ncores + val topName = "TestBench" def sources = T.sources(millSourcePath) @@ -291,7 +295,7 @@ object cosim extends Module { | TOP_MODULE TestBench | PREFIX VTestBench | OPT_FAST - | THREADS 8 + | THREADS ${emulatorCores} | VERILATOR_ARGS ${verilatorArgs().mkString(" ")} |) |""".stripMargin @@ -468,6 +472,7 @@ object tests extends Module() { PathRef(if (p.exitCode != 0) { os.move(T.dest / s"$name.running.log", T.dest / s"$name.failed.log") System.err.println(s"Test $name failed with exit code ${p.exitCode}") + System.exit(1) T.dest / s"$name.failed.log" } else { os.move(T.dest / s"$name.running.log", T.dest / s"$name.passed.log") diff --git a/cosim/elaborate/src/DUT.scala b/cosim/elaborate/src/DUT.scala index fcb14e7ea..e12796665 100644 --- a/cosim/elaborate/src/DUT.scala +++ b/cosim/elaborate/src/DUT.scala @@ -69,7 +69,7 @@ class DUT(xLen:Int)(p: Parameters) extends Module { val resetVector = InModuleBody { resetVectorNode.makeIO() } - val hartidNode = BundleBridgeSource(() => UInt(4.W)) + val hartidNode = BundleBridgeSource(() => UInt(1.W)) rocketTile.hartIdNode := hartidNode InModuleBody { hartidNode.bundle := 0.U diff --git a/diplomatic/src/rocket/BaseTile.scala b/diplomatic/src/rocket/BaseTile.scala index b7aea6e74..8170a88ac 100644 --- a/diplomatic/src/rocket/BaseTile.scala +++ b/diplomatic/src/rocket/BaseTile.scala @@ -3,16 +3,15 @@ package org.chipsalliance.rockettile import Chisel._ - +import chisel3.util.log2Ceil import org.chipsalliance.cde.config._ import org.chipsalliance.rocket._ import freechips.rocketchip.subsystem._ import freechips.rocketchip.diplomacy._ - import freechips.rocketchip.interrupts._ import freechips.rocketchip.tilelink._ import freechips.rocketchip.util._ -import freechips.rocketchip.prci.{ClockSinkParameters} +import freechips.rocketchip.prci.ClockSinkParameters case object TileVisibilityNodeKey extends Field[TLEphemeralNode] case object TileKey extends Field[TileParams] @@ -70,6 +69,7 @@ trait HasNonDiplomaticTileParameters { require(pgLevels >= res) res } + def matchBits = tileParams.btb.get.nMatchBits max log2Ceil(p(CacheBlockBytes) * tileParams.icache.get.nSets) def asIdBits: Int = p(ASIdBits) def vmIdBits: Int = p(VMIdBits) lazy val maxPAddrBits: Int = { diff --git a/diplomatic/src/rocket/Frontend.scala b/diplomatic/src/rocket/Frontend.scala index 539a01511..567eed569 100644 --- a/diplomatic/src/rocket/Frontend.scala +++ b/diplomatic/src/rocket/Frontend.scala @@ -30,14 +30,7 @@ class FrontendExceptions extends Bundle { } } -class FrontendResp(implicit p: Parameters) extends CoreBundle()(p) { - val btb = new BTBResp - val pc = UInt(vaddrBitsExtended.W) // ID stage PC - val data = UInt((fetchWidth * coreInstBits).W) - val mask = Bits(fetchWidth.W) - val xcpt = new FrontendExceptions - val replay = Bool() -} + class FrontendPerfEvents extends Bundle { val acquire = Bool() @@ -49,11 +42,11 @@ class FrontendIO(implicit p: Parameters) extends CoreBundle()(p) { val clock_enabled = Input(Bool()) val req = Valid(new FrontendReq) val sfence = Valid(new SFenceReq) - val resp = Flipped(Decoupled(new FrontendResp)) + val resp = Flipped(Decoupled(new FrontendResp(tileParams.btb.get, vaddrBits, vaddrBitsExtended, fetchWidth, coreInstBits))) val gpa = Flipped(Valid(UInt(vaddrBitsExtended.W))) - val btb_update = Valid(new BTBUpdate) - val bht_update = Valid(new BHTUpdate) - val ras_update = Valid(new RASUpdate) + val btb_update = Valid(new BTBUpdate(tileParams.btb.get, fetchWidth, vaddrBits)) + val bht_update = Valid(new BHTUpdate(tileParams.btb.get, vaddrBits)) + val ras_update = Valid(new RASUpdate(vaddrBits)) val flush_icache = Output(Bool()) val npc = Input(UInt(vaddrBitsExtended.W)) val perf = Input(new FrontendPerfEvents()) @@ -83,7 +76,7 @@ class FrontendModule(outer: Frontend) extends LazyModuleImp(outer) val icache = outer.icache.module require(fetchWidth*coreInstBytes == outer.icacheParams.fetchBytes) - val fq = withReset(reset.asBool || io.cpu.req.valid) { Module(new ShiftQueue(new FrontendResp, 5, flow = true)) } + val fq = withReset(reset.asBool || io.cpu.req.valid) { Module(new ShiftQueue(new FrontendResp(tileParams.btb.get, vaddrBits, vaddrBitsExtended, fetchWidth, coreInstBits), 5, flow = true))} val clock_en_reg = Reg(Bool()) val clock_en = clock_en_reg || io.cpu.might_request @@ -111,7 +104,7 @@ class FrontendModule(outer: Frontend) extends LazyModuleImp(outer) val s1_speculative = Reg(Bool()) val s2_pc = RegInit(t = UInt(vaddrBitsExtended.W), alignPC(io_reset_vector)) val s2_btb_resp_valid = if (usingBTB) Reg(Bool()) else false.B - val s2_btb_resp_bits = Reg(new BTBResp) + val s2_btb_resp_bits = Reg(new BTBResp(tileParams.btb.get, fetchWidth, vaddrBits)) val s2_btb_taken = s2_btb_resp_valid && s2_btb_resp_bits.taken val s2_tlb_resp = Reg(tlb.io.resp.cloneType) val s2_xcpt = s2_tlb_resp.ae.inst || s2_tlb_resp.pf.inst || s2_tlb_resp.gf.inst @@ -190,7 +183,7 @@ class FrontendModule(outer: Frontend) extends LazyModuleImp(outer) when (icache.io.resp.valid && icache.io.resp.bits.ae) { fq.io.enq.bits.xcpt.ae.inst := true.B } if (usingBTB) { - val btb = Module(new BTB) + val btb = Module(new BTB(tileParams.btb.get, fetchBytes, fetchWidth, vaddrBits, matchBits, coreInstBits)) btb.io.flush := false.B btb.io.req.valid := false.B btb.io.req.bits.addr := s1_pc diff --git a/diplomatic/src/rocket/RocketCore.scala b/diplomatic/src/rocket/RocketCore.scala index 523819d94..9080b1c69 100644 --- a/diplomatic/src/rocket/RocketCore.scala +++ b/diplomatic/src/rocket/RocketCore.scala @@ -213,7 +213,7 @@ class Rocket(tile: RocketTile)(implicit p: Parameters) extends CoreModule()(p) val ex_reg_xcpt_interrupt = Reg(Bool()) val ex_reg_valid = Reg(Bool()) val ex_reg_rvc = Reg(Bool()) - val ex_reg_btb_resp = Reg(new BTBResp) + val ex_reg_btb_resp = Reg(new BTBResp(tileParams.btb.get, fetchWidth, vaddrBits)) val ex_reg_xcpt = Reg(Bool()) val ex_reg_flush_pipe = Reg(Bool()) val ex_reg_load_use = Reg(Bool()) @@ -231,7 +231,7 @@ class Rocket(tile: RocketTile)(implicit p: Parameters) extends CoreModule()(p) val mem_reg_xcpt_interrupt = Reg(Bool()) val mem_reg_valid = Reg(Bool()) val mem_reg_rvc = Reg(Bool()) - val mem_reg_btb_resp = Reg(new BTBResp) + val mem_reg_btb_resp = Reg(new BTBResp(tileParams.btb.get, fetchWidth, vaddrBits)) val mem_reg_xcpt = Reg(Bool()) val mem_reg_replay = Reg(Bool()) val mem_reg_flush_pipe = Reg(Bool()) @@ -275,7 +275,7 @@ class Rocket(tile: RocketTile)(implicit p: Parameters) extends CoreModule()(p) val take_pc = take_pc_mem_wb // decode stage - val ibuf = Module(new IBuf) + val ibuf = Module(new IBuf(coreInstBits, usingCompressed, vaddrBits, vaddrBitsExtended, retireWidth, decodeWidth, fetchWidth, coreInstBits, tileParams.btb.get, p(XLen))) val id_expanded_inst = ibuf.io.inst.map(_.bits.inst) val id_raw_inst = ibuf.io.inst.map(_.bits.raw) val id_inst = id_expanded_inst.map(_.bits) diff --git a/diplomatic/src/rocket/BTB.scala b/rocket/src/BTB.scala similarity index 76% rename from diplomatic/src/rocket/BTB.scala rename to rocket/src/BTB.scala index 8261f0692..d392fbf31 100644 --- a/diplomatic/src/rocket/BTB.scala +++ b/rocket/src/BTB.scala @@ -5,11 +5,8 @@ package org.chipsalliance.rocket import chisel3._ import chisel3.util._ -import chisel3.internal.InstanceId -import org.chipsalliance.cde.config.Parameters -import freechips.rocketchip.subsystem.CacheBlockBytes -import freechips.rocketchip.util._ -import org.chipsalliance.rockettile.HasCoreParameters +import org.chipsalliance.rocket.util._ +import org.chipsalliance.rocket.util.property._ case class BHTParams( nEntries: Int = 512, @@ -25,20 +22,6 @@ case class BTBParams( bhtParams: Option[BHTParams] = Some(BHTParams()), updatesOutOfOrder: Boolean = false) -trait HasBtbParameters extends HasCoreParameters { this: InstanceId => - val btbParams = tileParams.btb.getOrElse(BTBParams(nEntries = 0)) - val matchBits = btbParams.nMatchBits max log2Ceil(p(CacheBlockBytes) * tileParams.icache.get.nSets) - val entries = btbParams.nEntries - val updatesOutOfOrder = btbParams.updatesOutOfOrder - val nPages = (btbParams.nPages + 1) / 2 * 2 // control logic assumes 2 divides pages -} - -abstract class BtbModule(implicit val p: Parameters) extends Module with HasBtbParameters { - Annotated.params(this, btbParams) -} - -abstract class BtbBundle(implicit val p: Parameters) extends Bundle with HasBtbParameters - class RAS(nras: Int) { def push(addr: UInt): Unit = { when (count < nras.U) { count := count + 1.U } @@ -59,9 +42,9 @@ class RAS(nras: Int) { private val stack = Reg(Vec(nras, UInt())) } -class BHTResp(implicit p: Parameters) extends BtbBundle()(p) { - val history = UInt(btbParams.bhtParams.map(_.historyLength).getOrElse(1).W) - val value = UInt(btbParams.bhtParams.map(_.counterLength).getOrElse(1).W) +class BHTResp(bhtParams: Option[BHTParams]) extends Bundle { + val history = UInt(bhtParams.map(_.historyLength).getOrElse(1).W) + val value = UInt(bhtParams.map(_.counterLength).getOrElse(1).W) def taken = value(0) def strongly_taken = value === 1.U } @@ -75,7 +58,7 @@ class BHTResp(implicit p: Parameters) extends BtbBundle()(p) { // - each counter corresponds with the address of the fetch packet ("fetch pc"). // - updated when a branch resolves (and BTB was a hit for that branch). // The updating branch must provide its "fetch pc". -class BHT(params: BHTParams)(implicit val p: Parameters) extends HasCoreParameters { +class BHT(params: BHTParams, fetchBytes: Int) extends Bundle { def index(addr: UInt, history: UInt) = { def hashHistory(hist: UInt) = if (params.historyLength == params.historyBits) hist else { val k = math.sqrt(3)/2 @@ -89,7 +72,7 @@ class BHT(params: BHTParams)(implicit val p: Parameters) extends HasCoreParamete hashAddr(addr) ^ (hashHistory(history) << (log2Up(params.nEntries) - params.historyBits)) } def get(addr: UInt): BHTResp = { - val res = Wire(new BHTResp) + val res = Wire(new BHTResp(Some(params))) res.value := Mux(resetting, 0.U, table(index(addr, history))) res.history := history res @@ -138,8 +121,8 @@ object CFIType { // BTB update occurs during branch resolution (and only on a mispredict). // - "pc" is what future fetch PCs will tag match against. // - "br_pc" is the PC of the branch instruction. -class BTBUpdate(implicit p: Parameters) extends BtbBundle()(p) { - val prediction = new BTBResp +class BTBUpdate(btbParams: BTBParams, fetchWidth: Int, vaddrBits: Int) extends Bundle { + val prediction = new BTBResp(btbParams, fetchWidth, vaddrBits) val pc = UInt(vaddrBits.W) val target = UInt(vaddrBits.W) val taken = Bool() @@ -150,15 +133,15 @@ class BTBUpdate(implicit p: Parameters) extends BtbBundle()(p) { // BHT update occurs during branch resolution on all conditional branches. // - "pc" is what future fetch PCs will tag match against. -class BHTUpdate(implicit p: Parameters) extends BtbBundle()(p) { - val prediction = new BHTResp +class BHTUpdate(btbParams: BTBParams, vaddrBits: Int) extends Bundle { + val prediction = new BHTResp(btbParams.bhtParams) val pc = UInt(vaddrBits.W) val branch = Bool() val taken = Bool() val mispredict = Bool() } -class RASUpdate(implicit p: Parameters) extends BtbBundle()(p) { +class RASUpdate(vaddrBits: Int) extends Bundle { val cfiType = CFIType() val returnAddr = UInt(vaddrBits.W) } @@ -167,47 +150,47 @@ class RASUpdate(implicit p: Parameters) extends BtbBundle()(p) { // shifting off the lowest log(inst_bytes) bits off). // - "mask" provides a mask of valid instructions (instructions are // masked off by the predicted taken branch from the BTB). -class BTBResp(implicit p: Parameters) extends BtbBundle()(p) { +class BTBResp(btbparams: BTBParams, fetchWidth: Int, vaddrBits: Int) extends Bundle { val cfiType = CFIType() val taken = Bool() val mask = Bits(fetchWidth.W) val bridx = Bits(log2Up(fetchWidth).W) val target = UInt(vaddrBits.W) - val entry = UInt(log2Up(entries + 1).W) - val bht = new BHTResp + val entry = UInt(log2Up(btbparams.nEntries + 1).W) + val bht = new BHTResp(btbparams.bhtParams) } -class BTBReq(implicit p: Parameters) extends BtbBundle()(p) { - val addr = UInt(vaddrBits.W) +class BTBReq(vaddrBits: Int) extends Bundle { + val addr = UInt(vaddrBits.W) } // fully-associative branch target buffer // Higher-performance processors may cause BTB updates to occur out-of-order, // which requires an extra CAM port for updates (to ensure no duplicates get // placed in BTB). -class BTB(implicit p: Parameters) extends BtbModule { +class BTB(btbParams: BTBParams, fetchBytes: Int, fetchWidth: Int, vaddrBits: Int, matchBits: Int, coreInstBytes: Int) extends Module { val io = IO(new Bundle { - val req = Flipped(Valid(new BTBReq)) - val resp = Valid(new BTBResp) - val btb_update = Flipped(Valid(new BTBUpdate)) - val bht_update = Flipped(Valid(new BHTUpdate)) - val bht_advance = Flipped(Valid(new BTBResp)) - val ras_update = Flipped(Valid(new RASUpdate)) + val req = Flipped(Valid(new BTBReq(vaddrBits))) + val resp = Valid(new BTBResp(btbParams, fetchWidth, vaddrBits)) + val btb_update = Flipped(Valid(new BTBUpdate(btbParams, fetchWidth, vaddrBits))) + val bht_update = Flipped(Valid(new BHTUpdate(btbParams, vaddrBits))) + val bht_advance = Flipped(Valid(new BTBResp(btbParams, fetchWidth, vaddrBits))) + val ras_update = Flipped(Valid(new RASUpdate(vaddrBits))) val ras_head = Valid(UInt(vaddrBits.W)) val flush = Input(Bool()) }) - val idxs = Reg(Vec(entries, UInt((matchBits - log2Up(coreInstBytes)).W))) - val idxPages = Reg(Vec(entries, UInt(log2Up(nPages).W))) - val tgts = Reg(Vec(entries, UInt((matchBits - log2Up(coreInstBytes)).W))) - val tgtPages = Reg(Vec(entries, UInt(log2Up(nPages).W))) - val pages = Reg(Vec(nPages, UInt((vaddrBits - matchBits).W))) - val pageValid = RegInit(0.U(nPages.W)) + val idxs = Reg(Vec(btbParams.nEntries, UInt((matchBits - log2Up(coreInstBytes)).W))) + val idxPages = Reg(Vec(btbParams.nEntries, UInt(log2Up(btbParams.nPages).W))) + val tgts = Reg(Vec(btbParams.nEntries, UInt((matchBits - log2Up(coreInstBytes)).W))) + val tgtPages = Reg(Vec(btbParams.nEntries, UInt(log2Up(btbParams.nPages).W))) + val pages = Reg(Vec(btbParams.nPages, UInt((vaddrBits - matchBits).W))) + val pageValid = RegInit(0.U(btbParams.nPages.W)) val pagesMasked = (pageValid.asBools zip pages).map { case (v, p) => Mux(v, p, 0.U) } - val isValid = RegInit(0.U(entries.W)) - val cfiType = Reg(Vec(entries, CFIType())) - val brIdx = Reg(Vec(entries, UInt(log2Up(fetchWidth).W))) + val isValid = RegInit(0.U(btbParams.nEntries.W)) + val cfiType = Reg(Vec(btbParams.nEntries, CFIType())) + val brIdx = Reg(Vec(btbParams.nEntries, UInt(log2Up(fetchWidth).W))) private def page(addr: UInt) = addr >> matchBits private def pageMatch(addr: UInt) = { @@ -227,33 +210,33 @@ class BTB(implicit p: Parameters) extends BtbModule { val updatePageHit = pageMatch(r_btb_update.bits.pc) val (updateHit, updateHitAddr) = - if (updatesOutOfOrder) { + if (btbParams.updatesOutOfOrder) { val updateHits = (pageHit << 1)(Mux1H(idxMatch(r_btb_update.bits.pc), idxPages)) (updateHits.orR, OHToUInt(updateHits)) - } else (r_btb_update.bits.prediction.entry < entries.U, r_btb_update.bits.prediction.entry) + } else (r_btb_update.bits.prediction.entry < btbParams.nEntries.U, r_btb_update.bits.prediction.entry) val useUpdatePageHit = updatePageHit.orR val usePageHit = pageHit.orR val doIdxPageRepl = !useUpdatePageHit - val nextPageRepl = RegInit(0.U(log2Ceil(nPages).W)) - val idxPageRepl = Cat(pageHit(nPages-2,0), pageHit(nPages-1)) | Mux(usePageHit, 0.U, UIntToOH(nextPageRepl)) + val nextPageRepl = RegInit(0.U(log2Ceil(btbParams.nPages).W)) + val idxPageRepl = Cat(pageHit(btbParams.nPages-2,0), pageHit(btbParams.nPages-1)) | Mux(usePageHit, 0.U, UIntToOH(nextPageRepl)) val idxPageUpdateOH = Mux(useUpdatePageHit, updatePageHit, idxPageRepl) val idxPageUpdate = OHToUInt(idxPageUpdateOH) val idxPageReplEn = Mux(doIdxPageRepl, idxPageRepl, 0.U) val samePage = page(r_btb_update.bits.pc) === page(update_target) val doTgtPageRepl = !samePage && !usePageHit - val tgtPageRepl = Mux(samePage, idxPageUpdateOH, Cat(idxPageUpdateOH(nPages-2,0), idxPageUpdateOH(nPages-1))) + val tgtPageRepl = Mux(samePage, idxPageUpdateOH, Cat(idxPageUpdateOH(btbParams.nPages-2,0), idxPageUpdateOH(btbParams.nPages-1))) val tgtPageUpdate = OHToUInt(pageHit | Mux(usePageHit, 0.U, tgtPageRepl)) val tgtPageReplEn = Mux(doTgtPageRepl, tgtPageRepl, 0.U) when (r_btb_update.valid && (doIdxPageRepl || doTgtPageRepl)) { val both = doIdxPageRepl && doTgtPageRepl val next = nextPageRepl + Mux[UInt](both, 2.U, 1.U) - nextPageRepl := Mux(next >= nPages.U, next(0), next) + nextPageRepl := Mux(next >= btbParams.nPages.U, next(0), next) } - val repl = new PseudoLRU(entries) + val repl = new PseudoLRU(btbParams.nEntries) val waddr = Mux(updateHit, updateHitAddr, repl.way) val r_resp = Pipe(io.resp) when (r_resp.valid && r_resp.bits.taken || r_btb_update.valid) { @@ -271,11 +254,11 @@ class BTB(implicit p: Parameters) extends BtbModule { if (fetchWidth > 1) brIdx(waddr) := r_btb_update.bits.br_pc >> log2Up(coreInstBytes) - require(nPages % 2 == 0) + require(btbParams.nPages % 2 == 0) val idxWritesEven = !idxPageUpdate(0) def writeBank(i: Int, mod: Int, en: UInt, data: UInt) = - for (i <- i until nPages by mod) + for (i <- i until btbParams.nPages by mod) when (en(i)) { pages(i) := data } writeBank(0, 2, Mux(idxWritesEven, idxPageReplEn, tgtPageReplEn), @@ -302,7 +285,7 @@ class BTB(implicit p: Parameters) extends BtbModule { } if (btbParams.bhtParams.nonEmpty) { - val bht = new BHT(Annotated.params(this, btbParams.bhtParams.get)) + val bht = new BHT(btbParams.bhtParams.get,fetchBytes) val isBranch = (idxHit & cfiType.map(_ === CFIType.branch).asUInt).orR val res = bht.get(io.req.bits.addr) when (io.bht_advance.valid) { diff --git a/diplomatic/src/rocket/IBuf.scala b/rocket/src/IBuf.scala similarity index 78% rename from diplomatic/src/rocket/IBuf.scala rename to rocket/src/IBuf.scala index 78b64cc74..3b2236240 100644 --- a/diplomatic/src/rocket/IBuf.scala +++ b/rocket/src/IBuf.scala @@ -4,11 +4,32 @@ package org.chipsalliance.rocket import chisel3._ import chisel3.util.{Decoupled,log2Ceil,Cat,UIntToOH,Fill} -import org.chipsalliance.cde.config.Parameters -import org.chipsalliance.rockettile._ -import freechips.rocketchip.util._ +import org.chipsalliance.rocket.util._ +import org.chipsalliance.rocket.util.property._ -class Instruction(implicit val p: Parameters) extends ParameterizedBundle with HasCoreParameters { +//todo: move to Fronted +class FrontendExceptions extends Bundle { + val pf = new Bundle { + val inst = Bool() + } + val gf = new Bundle { + val inst = Bool() + } + val ae = new Bundle { + val inst = Bool() + } +} + +class FrontendResp(BTBParams: BTBParams,vaddrBits:Int,vaddrBitsExtended:Int,fetchWidth:Int,coreInstBits:Int) extends Bundle { + val btb = new BTBResp(BTBParams,fetchWidth,vaddrBits) + val pc = UInt(vaddrBitsExtended.W) // ID stage PC + val data = UInt((fetchWidth * coreInstBits).W) + val mask = Bits(fetchWidth.W) + val xcpt = new FrontendExceptions + val replay = Bool() +} + +class Instruction(coreInstBits:Int, usingCompressed:Boolean) extends Bundle { val xcpt0 = new FrontendExceptions // exceptions on first half of instruction val xcpt1 = new FrontendExceptions // exceptions on second half of instruction val replay = Bool() @@ -18,13 +39,14 @@ class Instruction(implicit val p: Parameters) extends ParameterizedBundle with H require(coreInstBits == (if (usingCompressed) 16 else 32)) } -class IBuf(implicit p: Parameters) extends CoreModule { +class IBuf(coreInstBits: Int, usingCompressed: Boolean, vaddrBits: Int, vaddrBitsExtended: Int, retireWidth: Int, decodeWidth: Int, + fetchWidth: Int, coreInstBytes: Int, BTBParams: BTBParams, xlen: Int) extends Module { val io = IO(new Bundle { - val imem = Flipped(Decoupled(new FrontendResp)) + val imem = Flipped(Decoupled(new FrontendResp(BTBParams, vaddrBits, vaddrBitsExtended, fetchWidth, coreInstBits))) val kill = Input(Bool()) val pc = Output(UInt(vaddrBitsExtended.W)) - val btb_resp = Output(new BTBResp()) - val inst = Vec(retireWidth, Decoupled(new Instruction)) + val btb_resp = Output(new BTBResp(BTBParams, fetchWidth, vaddrBits)) + val inst = Vec(retireWidth, Decoupled(new Instruction(coreInstBits, usingCompressed))) }) // This module is meant to be more general, but it's not there yet @@ -33,7 +55,7 @@ class IBuf(implicit p: Parameters) extends CoreModule { val n = fetchWidth - 1 val nBufValid = if (n == 0) 0.U else RegInit(init=0.U(log2Ceil(fetchWidth).W)) val buf = Reg(chiselTypeOf(io.imem.bits)) - val ibufBTBResp = Reg(new BTBResp) + val ibufBTBResp = Reg(new BTBResp(BTBParams, fetchWidth, vaddrBits)) val pcWordMask = (coreInstBytes*fetchWidth-1).U(vaddrBitsExtended.W) val pcWordBits = io.imem.bits.pc.extract(log2Ceil(fetchWidth*coreInstBytes)-1, log2Ceil(coreInstBytes)) @@ -83,7 +105,7 @@ class IBuf(implicit p: Parameters) extends CoreModule { expand(0, 0.U, inst) def expand(i: Int, j: UInt, curInst: UInt): Unit = if (i < retireWidth) { - val exp = Module(new RVCExpander) + val exp = Module(new RVCExpander(usingCompressed = usingCompressed, XLen = xlen)) exp.io.in := curInst io.inst(i).bits.inst := exp.io.out io.inst(i).bits.raw := curInst diff --git a/rocket/src/Misc.scala b/rocket/src/Misc.scala new file mode 100644 index 000000000..d87b17cdd --- /dev/null +++ b/rocket/src/Misc.scala @@ -0,0 +1,45 @@ +// See LICENSE.Berkeley for license details. +// See LICENSE.SiFive for license details. + +package org.chipsalliance.rocket.util + +import Chisel._ +import chisel3.util.random.LFSR +import scala.math._ + +object Random +{ + def apply(mod: Int, random: UInt): UInt = { + if (isPow2(mod)) random.extract(log2Ceil(mod)-1,0) + else PriorityEncoder(partition(apply(1 << log2Up(mod*8), random), mod)) + } + def apply(mod: Int): UInt = apply(mod, randomizer) + def oneHot(mod: Int, random: UInt): UInt = { + if (isPow2(mod)) UIntToOH(random(log2Up(mod)-1,0)) + else PriorityEncoderOH(partition(apply(1 << log2Up(mod*8), random), mod)).asUInt + } + def oneHot(mod: Int): UInt = oneHot(mod, randomizer) + + private def randomizer = LFSR(16) + private def partition(value: UInt, slices: Int) = + Seq.tabulate(slices)(i => value < UInt(((i + 1) << value.getWidth) / slices)) +} + +object PopCountAtLeast { + private def two(x: UInt): (Bool, Bool) = x.getWidth match { + case 1 => (x.asBool, Bool(false)) + case n => + val half = x.getWidth / 2 + val (leftOne, leftTwo) = two(x(half - 1, 0)) + val (rightOne, rightTwo) = two(x(x.getWidth - 1, half)) + (leftOne || rightOne, leftTwo || rightTwo || (leftOne && rightOne)) + } + def apply(x: UInt, n: Int): Bool = n match { + case 0 => Bool(true) + case 1 => x.orR + case 2 => two(x)._2 + case 3 => PopCount(x) >= UInt(n) + } +} + + diff --git a/rocket/src/Property.scala b/rocket/src/Property.scala new file mode 100644 index 000000000..997670bb2 --- /dev/null +++ b/rocket/src/Property.scala @@ -0,0 +1,154 @@ +// See LICENSE.SiFive for license details. + +package org.chipsalliance.rocket.util.property + +import Chisel._ +import chisel3.internal.sourceinfo.SourceInfo +import chisel3.util.{ReadyValidIO} + +sealed abstract class PropertyType(name: String) { + override def toString: String = name +} + +object PropertyType { + object Assert extends PropertyType("Assert") + object Assume extends PropertyType("Assume") + object Cover extends PropertyType("Cover") +} + +trait BasePropertyParameters { + val pType: PropertyType + val cond: Bool + val label: String + val message: String +} + +case class CoverPropertyParameters( + cond: Bool, + label: String = "", + message: String = "") extends BasePropertyParameters { + val pType = PropertyType.Cover +} + +abstract class BasePropertyLibrary { + def generateProperty(prop_param: BasePropertyParameters)(implicit sourceInfo: SourceInfo): Unit +} + +class DefaultPropertyLibrary extends BasePropertyLibrary { + def generateProperty(prop_param: BasePropertyParameters)(implicit sourceInfo: SourceInfo): Unit = { + // default is to do nothing + () + } +} + +abstract class BaseProperty { + def generateProperties(): Seq[BasePropertyParameters] +} + +case class CoverBoolean(cond: Bool, labels: Seq[String]) { +} + +// CrossProperty.generateProperties() will generate Boolean crosses for the cond sequence +// E.g. +// Cond = [ [A1, A2, A3], +// [B2], +// [C1, C2] ] +// It will generate the following properties +// [ A1 && B2 && C1, +// A1 && B2 && C2, +// A2 && B2 && C1, +// A2 && B2 && C2, +// A3 && B2 && C1, +// A3 && B2 && C2 ] +// Each of the boolean expression (A1, A2, C1, etc.) have a label associated with it +// User can exclude a particular cross from being generated by adding it to the exclude list +// e.g. +// exclude = [ ["A1_label", "C2_Label"], +// ["A3_label", "B2_label"] ] +// will exclude all crosses with those labels, so the new cross list will be +// [ A1 && B2 && C1, +// A2 && B2 && C1, +// A2 && B2 && C2 ] + +// Each boolean expression can be associated with more than one label + +class CrossProperty(cond: Seq[Seq[CoverBoolean]], exclude: Seq[Seq[String]], message: String) extends BaseProperty { + def listProperties(c1: CoverBoolean, c2: Seq[CoverBoolean]): Seq[CoverBoolean] = { + if (c2.isEmpty) { + Seq(c1) + } else { + c2.map( (c: CoverBoolean) => { + new CoverBoolean(c1.cond && c.cond, c1.labels ++ c.labels) + }) + } + } + + def crossProperties(cond: Seq[Seq[CoverBoolean]]): Seq[CoverBoolean] = { + if (cond.isEmpty) { + Seq() + } else { + cond.head.map( (c1: CoverBoolean) => { + listProperties(c1, crossProperties(cond.tail)) + }).reduce(_ ++ _) + } + } + def inSequence(search: Seq[String], find: Seq[String]): Boolean = { + if (find.isEmpty) { + true + } else { + find.map( (s:String) => { + search.contains(s) + }).reduce( _ && _ ) + } + } + def SeqsinSequence(search: Seq[String], find: Seq[Seq[String]]): Boolean = { + if (find.isEmpty) { + false + } else { + find.map( (s: Seq[String]) => { + inSequence(search, s) + }).reduce (_ || _) + } + } + + def generateProperties(): Seq[CoverPropertyParameters] = { + crossProperties(cond).filter(c => !SeqsinSequence(c.labels, exclude)).map( (c: CoverBoolean) => { + new CoverPropertyParameters( + cond = c.cond, + label = c.labels.reduce( (s1: String, s2: String) => {s1 + "_" + s2} ), + message = message + " " + c.labels.map("<" + _ + ">").reduce ( (s1: String, s2: String) => { s1 + " X " + s2 })) + }) + } + +} + +// The implementation using a setable global is bad, but removes dependence on Parameters +// This change was made in anticipation of a proper cover library +object cover { + private var propLib: BasePropertyLibrary = new DefaultPropertyLibrary + def setPropLib(lib: BasePropertyLibrary): Unit = this.synchronized { + propLib = lib + } + def apply(cond: Bool)(implicit sourceInfo: SourceInfo): Unit = { + propLib.generateProperty(CoverPropertyParameters(cond)) + } + def apply(cond: Bool, label: String)(implicit sourceInfo: SourceInfo): Unit = { + propLib.generateProperty(CoverPropertyParameters(cond, label)) + } + def apply(cond: Bool, label: String, message: String)(implicit sourceInfo: SourceInfo): Unit = { + propLib.generateProperty(CoverPropertyParameters(cond, label, message)) + } + def apply(prop: BaseProperty)(implicit sourceInfo: SourceInfo): Unit = { + prop.generateProperties().foreach( (pp: BasePropertyParameters) => { + if (pp.pType == PropertyType.Cover) { + propLib.generateProperty(CoverPropertyParameters(pp.cond, pp.label, pp.message)) + } + }) + } + def apply[T <: Data](rv: ReadyValidIO[T], label: String, message: String)(implicit sourceInfo: SourceInfo): Unit = { + apply( rv.valid && rv.ready, label + "_FIRE", message + ": valid and ready") + apply( rv.valid && !rv.ready, label + "_STALL", message + ": valid and not ready") + apply(!rv.valid && rv.ready, label + "_IDLE", message + ": not valid and ready") + apply(!rv.valid && !rv.ready, label + "_FULL", message + ": not valid and not ready") + } +} diff --git a/diplomatic/src/rocket/RVC.scala b/rocket/src/RVC.scala similarity index 94% rename from diplomatic/src/rocket/RVC.scala rename to rocket/src/RVC.scala index e9661fe9e..6b44bcdd0 100644 --- a/diplomatic/src/rocket/RVC.scala +++ b/rocket/src/RVC.scala @@ -4,9 +4,7 @@ package org.chipsalliance.rocket import chisel3._ import chisel3.util._ -import org.chipsalliance.cde.config.Parameters -import org.chipsalliance.rockettile._ -import freechips.rocketchip.util._ +import org.chipsalliance.rocket.util._ class ExpandedInstruction extends Bundle { val bits = UInt(32.W) @@ -155,7 +153,7 @@ class RVCDecoder(x: UInt, xLen: Int, useAddiForMv: Boolean = false) { } } -class RVCExpander(useAddiForMv: Boolean = false)(implicit val p: Parameters) extends Module with HasCoreParameters { +class RVCExpander(useAddiForMv: Boolean = false,usingCompressed:Boolean,XLen:Int)extends Module { val io = IO(new Bundle { val in = Input(UInt(32.W)) val out = Output(new ExpandedInstruction) @@ -164,9 +162,9 @@ class RVCExpander(useAddiForMv: Boolean = false)(implicit val p: Parameters) ext if (usingCompressed) { io.rvc := io.in(1,0) =/= 3.U - io.out := new RVCDecoder(io.in, p(XLen), useAddiForMv).decode + io.out := new RVCDecoder(io.in, XLen, useAddiForMv).decode } else { io.rvc := false.B - io.out := new RVCDecoder(io.in, p(XLen), useAddiForMv).passthrough + io.out := new RVCDecoder(io.in, XLen, useAddiForMv).passthrough } } diff --git a/rocket/src/Replacement.scala b/rocket/src/Replacement.scala new file mode 100644 index 000000000..e145dae17 --- /dev/null +++ b/rocket/src/Replacement.scala @@ -0,0 +1,325 @@ +// See LICENSE.Berkeley for license details. +// See LICENSE.SiFive for license details. + +package org.chipsalliance.rocket.util + +import chisel3._ +import chisel3.util._ +import chisel3.util.random.LFSR + + +abstract class ReplacementPolicy { + def nBits: Int + def perSet: Boolean + def way: UInt + def miss: Unit + def hit: Unit + def access(touch_way: UInt): Unit + def access(touch_ways: Seq[Valid[UInt]]): Unit + def state_read: UInt + def get_next_state(state: UInt, touch_way: UInt): UInt + def get_next_state(state: UInt, touch_ways: Seq[Valid[UInt]]): UInt = { + touch_ways.foldLeft(state)((prev, touch_way) => Mux(touch_way.valid, get_next_state(prev, touch_way.bits), prev)) + } + def get_replace_way(state: UInt): UInt +} + +object ReplacementPolicy { + def fromString(s: String, n_ways: Int): ReplacementPolicy = s.toLowerCase match { + case "random" => new RandomReplacement(n_ways) + case "lru" => new TrueLRU(n_ways) + case "plru" => new PseudoLRU(n_ways) + case t => throw new IllegalArgumentException(s"unknown Replacement Policy type $t") + } +} + +class RandomReplacement(n_ways: Int) extends ReplacementPolicy { + private val replace = Wire(Bool()) + replace := false.B + def nBits = 16 + def perSet = false + private val lfsr = LFSR(nBits, replace) + def state_read = WireDefault(lfsr) + + def way = Random(n_ways, lfsr) + def miss = replace := true.B + def hit = {} + def access(touch_way: UInt) = {} + def access(touch_ways: Seq[Valid[UInt]]) = {} + def get_next_state(state: UInt, touch_way: UInt) = 0.U //DontCare + def get_replace_way(state: UInt) = way +} + +abstract class SeqReplacementPolicy { + def access(set: UInt): Unit + def update(valid: Bool, hit: Bool, set: UInt, way: UInt): Unit + def way: UInt +} + +abstract class SetAssocReplacementPolicy { + def access(set: UInt, touch_way: UInt): Unit + def access(sets: Seq[UInt], touch_ways: Seq[Valid[UInt]]): Unit + def way(set: UInt): UInt +} + +class SeqRandom(n_ways: Int) extends SeqReplacementPolicy { + val logic = new RandomReplacement(n_ways) + def access(set: UInt) = { } + def update(valid: Bool, hit: Bool, set: UInt, way: UInt) = { + when (valid && !hit) { logic.miss } + } + def way = logic.way +} + +class TrueLRU(n_ways: Int) extends ReplacementPolicy { + // True LRU replacement policy, using a triangular matrix to track which sets are more recently used than others. + // The matrix is packed into a single UInt (or Bits). Example 4-way (6-bits): + // [5] - 3 more recent than 2 + // [4] - 3 more recent than 1 + // [3] - 2 more recent than 1 + // [2] - 3 more recent than 0 + // [1] - 2 more recent than 0 + // [0] - 1 more recent than 0 + def nBits = (n_ways * (n_ways-1)) / 2 + def perSet = true + private val state_reg = RegInit(0.U(nBits.W)) + def state_read = WireDefault(state_reg) + + private def extractMRUVec(state: UInt): Seq[UInt] = { + // Extract per-way information about which higher-indexed ways are more recently used + val moreRecentVec = Wire(Vec(n_ways-1, UInt(n_ways.W))) + var lsb = 0 + for (i <- 0 until n_ways-1) { + moreRecentVec(i) := Cat(state(lsb+n_ways-i-2,lsb), 0.U((i+1).W)) + lsb = lsb + (n_ways - i - 1) + } + moreRecentVec + } + + def get_next_state(state: UInt, touch_way: UInt): UInt = { + val nextState = Wire(Vec(n_ways-1, UInt(n_ways.W))) + val moreRecentVec = extractMRUVec(state) // reconstruct lower triangular matrix + val wayDec = UIntToOH(touch_way, n_ways) + + // Compute next value of triangular matrix + // set the touched way as more recent than every other way + nextState.zipWithIndex.map { case (e, i) => + e := Mux(i.U === touch_way, 0.U(n_ways.W), moreRecentVec(i) | wayDec) + } + + nextState.zipWithIndex.tail.foldLeft((nextState.head.apply(n_ways-1,1),0)) { case ((pe,pi),(ce,ci)) => (Cat(ce.apply(n_ways-1,ci+1), pe), ci) }._1 + } + + def access(touch_way: UInt): Unit = { + state_reg := get_next_state(state_reg, touch_way) + } + def access(touch_ways: Seq[Valid[UInt]]): Unit = { + when (touch_ways.map(_.valid).orR) { + state_reg := get_next_state(state_reg, touch_ways) + } + for (i <- 1 until touch_ways.size) { + cover(PopCount(touch_ways.map(_.valid)) === i.U, s"LRU_UpdateCount$i, LRU Update $i simultaneous") + } + } + + def get_replace_way(state: UInt): UInt = { + val moreRecentVec = extractMRUVec(state) // reconstruct lower triangular matrix + // For each way, determine if all other ways are more recent + val mruWayDec = (0 until n_ways).map { i => + val upperMoreRecent = (if (i == n_ways-1) true.B else moreRecentVec(i).apply(n_ways-1,i+1).andR) + val lowerMoreRecent = (if (i == 0) true.B else moreRecentVec.map(e => !e(i)).reduce(_ && _)) + upperMoreRecent && lowerMoreRecent + } + OHToUInt(mruWayDec) + } + + def way = get_replace_way(state_reg) + def miss = access(way) + def hit = {} + @deprecated("replace 'replace' with 'way' from abstract class ReplacementPolicy","Rocket Chip 2020.05") + def replace: UInt = way +} + +class PseudoLRU(n_ways: Int) extends ReplacementPolicy { + // Pseudo-LRU tree algorithm: https://en.wikipedia.org/wiki/Pseudo-LRU#Tree-PLRU + // + // + // - bits storage example for 4-way PLRU binary tree: + // bit[2]: ways 3+2 older than ways 1+0 + // / \ + // bit[1]: way 3 older than way 2 bit[0]: way 1 older than way 0 + // + // + // - bits storage example for 3-way PLRU binary tree: + // bit[1]: way 2 older than ways 1+0 + // \ + // bit[0]: way 1 older than way 0 + // + // + // - bits storage example for 8-way PLRU binary tree: + // bit[6]: ways 7-4 older than ways 3-0 + // / \ + // bit[5]: ways 7+6 > 5+4 bit[2]: ways 3+2 > 1+0 + // / \ / \ + // bit[4]: way 7>6 bit[3]: way 5>4 bit[1]: way 3>2 bit[0]: way 1>0 + + def nBits = n_ways - 1 + def perSet = true + private val state_reg = if (nBits == 0) Reg(UInt(0.W)) else RegInit(0.U(nBits.W)) + def state_read = WireDefault(state_reg) + + def access(touch_way: UInt): Unit = { + state_reg := get_next_state(state_reg, touch_way) + } + def access(touch_ways: Seq[Valid[UInt]]): Unit = { + when (touch_ways.map(_.valid).orR) { + state_reg := get_next_state(state_reg, touch_ways) + } + for (i <- 1 until touch_ways.size) { + cover(PopCount(touch_ways.map(_.valid)) === i.U, s"PLRU_UpdateCount$i, PLRU Update $i simultaneous") + } + } + + + /** @param state state_reg bits for this sub-tree + * @param touch_way touched way encoded value bits for this sub-tree + * @param tree_nways number of ways in this sub-tree + */ + def get_next_state(state: UInt, touch_way: UInt, tree_nways: Int): UInt = { + require(state.getWidth == (tree_nways-1), s"wrong state bits width ${state.getWidth} for $tree_nways ways") + require(touch_way.getWidth == (log2Ceil(tree_nways) max 1), s"wrong encoded way width ${touch_way.getWidth} for $tree_nways ways") + + if (tree_nways > 2) { + // we are at a branching node in the tree, so recurse + val right_nways: Int = 1 << (log2Ceil(tree_nways) - 1) // number of ways in the right sub-tree + val left_nways: Int = tree_nways - right_nways // number of ways in the left sub-tree + val set_left_older = !touch_way(log2Ceil(tree_nways)-1) + val left_subtree_state = state.extract(tree_nways-3, right_nways-1) + val right_subtree_state = state(right_nways-2, 0) + + if (left_nways > 1) { + // we are at a branching node in the tree with both left and right sub-trees, so recurse both sub-trees + Cat(set_left_older, + Mux(set_left_older, + left_subtree_state, // if setting left sub-tree as older, do NOT recurse into left sub-tree + get_next_state(left_subtree_state, touch_way.extract(log2Ceil(left_nways)-1,0), left_nways)), // recurse left if newer + Mux(set_left_older, + get_next_state(right_subtree_state, touch_way(log2Ceil(right_nways)-1,0), right_nways), // recurse right if newer + right_subtree_state)) // if setting right sub-tree as older, do NOT recurse into right sub-tree + } else { + // we are at a branching node in the tree with only a right sub-tree, so recurse only right sub-tree + Cat(set_left_older, + Mux(set_left_older, + get_next_state(right_subtree_state, touch_way(log2Ceil(right_nways)-1,0), right_nways), // recurse right if newer + right_subtree_state)) // if setting right sub-tree as older, do NOT recurse into right sub-tree + } + } else if (tree_nways == 2) { + // we are at a leaf node at the end of the tree, so set the single state bit opposite of the lsb of the touched way encoded value + !touch_way(0) + } else { // tree_nways <= 1 + // we are at an empty node in an empty tree for 1 way, so return single zero bit for Chisel (no zero-width wires) + 0.U(1.W) + } + } + + def get_next_state(state: UInt, touch_way: UInt): UInt = { + val touch_way_sized = if (touch_way.getWidth < log2Ceil(n_ways)) touch_way.padTo (log2Ceil(n_ways)) + else touch_way.extract(log2Ceil(n_ways)-1,0) + get_next_state(state, touch_way_sized, n_ways) + } + + + /** @param state state_reg bits for this sub-tree + * @param tree_nways number of ways in this sub-tree + */ + def get_replace_way(state: UInt, tree_nways: Int): UInt = { + require(state.getWidth == (tree_nways-1), s"wrong state bits width ${state.getWidth} for $tree_nways ways") + + // this algorithm recursively descends the binary tree, filling in the way-to-replace encoded value from msb to lsb + if (tree_nways > 2) { + // we are at a branching node in the tree, so recurse + val right_nways: Int = 1 << (log2Ceil(tree_nways) - 1) // number of ways in the right sub-tree + val left_nways: Int = tree_nways - right_nways // number of ways in the left sub-tree + val left_subtree_older = state(tree_nways-2) + val left_subtree_state = state.extract(tree_nways-3, right_nways-1) + val right_subtree_state = state(right_nways-2, 0) + + if (left_nways > 1) { + // we are at a branching node in the tree with both left and right sub-trees, so recurse both sub-trees + Cat(left_subtree_older, // return the top state bit (current tree node) as msb of the way-to-replace encoded value + Mux(left_subtree_older, // if left sub-tree is older, recurse left, else recurse right + get_replace_way(left_subtree_state, left_nways), // recurse left + get_replace_way(right_subtree_state, right_nways))) // recurse right + } else { + // we are at a branching node in the tree with only a right sub-tree, so recurse only right sub-tree + Cat(left_subtree_older, // return the top state bit (current tree node) as msb of the way-to-replace encoded value + Mux(left_subtree_older, // if left sub-tree is older, return and do not recurse right + 0.U(1.W), + get_replace_way(right_subtree_state, right_nways))) // recurse right + } + } else if (tree_nways == 2) { + // we are at a leaf node at the end of the tree, so just return the single state bit as lsb of the way-to-replace encoded value + state(0) + } else { // tree_nways <= 1 + // we are at an empty node in an unbalanced tree for non-power-of-2 ways, so return single zero bit as lsb of the way-to-replace encoded value + 0.U(1.W) + } + } + + def get_replace_way(state: UInt): UInt = get_replace_way(state, n_ways) + + def way = get_replace_way(state_reg) + def miss = access(way) + def hit = {} +} + +class SeqPLRU(n_sets: Int, n_ways: Int) extends SeqReplacementPolicy { + val logic = new PseudoLRU(n_ways) + val state = SyncReadMem(n_sets, UInt(logic.nBits.W)) + val current_state = Wire(UInt(logic.nBits.W)) + val next_state = Wire(UInt(logic.nBits.W)) + val plru_way = logic.get_replace_way(current_state) + + def access(set: UInt) = { + current_state := state.read(set) + } + + def update(valid: Bool, hit: Bool, set: UInt, way: UInt) = { + val update_way = Mux(hit, way, plru_way) + next_state := logic.get_next_state(current_state, update_way) + when (valid) { state.write(set, next_state) } + } + + def way = plru_way +} + + +class SetAssocLRU(n_sets: Int, n_ways: Int, policy: String) extends SetAssocReplacementPolicy { + val logic = policy.toLowerCase match { + case "plru" => new PseudoLRU(n_ways) + case "lru" => new TrueLRU(n_ways) + case t => throw new IllegalArgumentException(s"unknown Replacement Policy type $t") + } + val state_vec = + if (logic.nBits == 0) Reg(Vec(n_sets, UInt(logic.nBits.W))) // Work around elaboration error on following line + else RegInit(VecInit(Seq.fill(n_sets)(0.U(logic.nBits.W)))) + + def access(set: UInt, touch_way: UInt) = { + state_vec(set) := logic.get_next_state(state_vec(set), touch_way) + } + + def access(sets: Seq[UInt], touch_ways: Seq[Valid[UInt]]) = { + require(sets.size == touch_ways.size, "internal consistency check: should be same number of simultaneous updates for sets and touch_ways") + for (set <- 0 until n_sets) { + val set_touch_ways = (sets zip touch_ways).map { case (touch_set, touch_way) => + Pipe(touch_way.valid && (touch_set === set.U), touch_way.bits, 0)} + when (set_touch_ways.map(_.valid).orR) { + state_vec(set) := logic.get_next_state(state_vec(set), set_touch_ways) + } + } + } + + def way(set: UInt) = logic.get_replace_way(state_vec(set)) + +} + diff --git a/rocket/src/util.scala b/rocket/src/util.scala new file mode 100644 index 000000000..f2f50a799 --- /dev/null +++ b/rocket/src/util.scala @@ -0,0 +1,186 @@ +package org.chipsalliance.rocket + +import chisel3._ +import chisel3.util._ + +//todo: remove util +package object util { + implicit class UIntIsOneOf(private val x: UInt) extends AnyVal { + def isOneOf(s: Seq[UInt]): Bool = s.map(x === _).orR + + def isOneOf(u1: UInt, u2: UInt*): Bool = isOneOf(u1 +: u2.toSeq) + } + + implicit class SeqToAugmentedSeq[T <: Data](private val x: Seq[T]) extends AnyVal { + def apply(idx: UInt): T = { + if (x.size <= 1) { + x.head + } else if (!isPow2(x.size)) { + // For non-power-of-2 seqs, reflect elements to simplify decoder + (x ++ x.takeRight(x.size & -x.size)).toSeq(idx) + } else { + // Ignore MSBs of idx + val truncIdx = + if (idx.isWidthKnown && idx.getWidth <= log2Ceil(x.size)) idx + else (idx | 0.U(log2Ceil(x.size).W))(log2Ceil(x.size)-1, 0) + x.zipWithIndex.tail.foldLeft(x.head) { case (prev, (cur, i)) => Mux(truncIdx === i.U, cur, prev) } + } + } + + def asUInt: UInt = Cat(x.map(_.asUInt).reverse) + + def rotate(n: Int): Seq[T] = x.drop(n) ++ x.take(n) + + def rotate(n: UInt): Seq[T] = { + if (x.size <= 1) { + x + } else { + require(isPow2(x.size)) + val amt = n.padTo(log2Ceil(x.size)) + (0 until log2Ceil(x.size)).foldLeft(x)((r, i) => (r.rotate(1 << i) zip r).map { case (s, a) => Mux(amt(i), s, a) }) + } + } + + def rotateRight(n: Int): Seq[T] = x.takeRight(n) ++ x.dropRight(n) + + def rotateRight(n: UInt): Seq[T] = { + if (x.size <= 1) { + x + } else { + require(isPow2(x.size)) + val amt = n.padTo(log2Ceil(x.size)) + (0 until log2Ceil(x.size)).foldLeft(x)((r, i) => (r.rotateRight(1 << i) zip r).map { case (s, a) => Mux(amt(i), s, a) }) + } + } + } + + implicit class UIntToAugmentedUInt(private val x: UInt) extends AnyVal { + def sextTo(n: Int): UInt = { + require(x.getWidth <= n) + if (x.getWidth == n) x + else Cat(Fill(n - x.getWidth, x(x.getWidth - 1)), x) + } + + def padTo(n: Int): UInt = { + require(x.getWidth <= n) + if (x.getWidth == n) x + else Cat(0.U((n - x.getWidth).W), x) + } + + // shifts left by n if n >= 0, or right by -n if n < 0 + def <<(n: SInt): UInt = { + val w = n.getWidth - 1 + require(w <= 30) + + val shifted = x << n(w - 1, 0) + Mux(n(w), shifted >> (1 << w), shifted) + } + + // shifts right by n if n >= 0, or left by -n if n < 0 + def >>(n: SInt): UInt = { + val w = n.getWidth - 1 + require(w <= 30) + + val shifted = x << (1 << w) >> n(w - 1, 0) + Mux(n(w), shifted, shifted >> (1 << w)) + } + + // Like UInt.apply(hi, lo), but returns 0.U for zero-width extracts + def extract(hi: Int, lo: Int): UInt = { + require(hi >= lo - 1) + if (hi == lo - 1) 0.U + else x(hi, lo) + } + + // Like Some(UInt.apply(hi, lo)), but returns None for zero-width extracts + def extractOption(hi: Int, lo: Int): Option[UInt] = { + require(hi >= lo - 1) + if (hi == lo - 1) None + else Some(x(hi, lo)) + } + + // like x & ~y, but first truncate or zero-extend y to x's width + def andNot(y: UInt): UInt = x & ~(y | (x & 0.U)) + + def rotateRight(n: Int): UInt = if (n == 0) x else Cat(x(n - 1, 0), x >> n) + + def rotateRight(n: UInt): UInt = { + if (x.getWidth <= 1) { + x + } else { + val amt = n.padTo(log2Ceil(x.getWidth)) + (0 until log2Ceil(x.getWidth)).foldLeft(x)((r, i) => Mux(amt(i), r.rotateRight(1 << i), r)) + } + } + + def rotateLeft(n: Int): UInt = if (n == 0) x else Cat(x(x.getWidth - 1 - n, 0), x(x.getWidth - 1, x.getWidth - n)) + + def rotateLeft(n: UInt): UInt = { + if (x.getWidth <= 1) { + x + } else { + val amt = n.padTo(log2Ceil(x.getWidth)) + (0 until log2Ceil(x.getWidth)).foldLeft(x)((r, i) => Mux(amt(i), r.rotateLeft(1 << i), r)) + } + } + + // compute (this + y) % n, given (this < n) and (y < n) + def addWrap(y: UInt, n: Int): UInt = { + val z = x +& y + if (isPow2(n)) z(n.log2 - 1, 0) else Mux(z >= n.U, z - n.U, z)(log2Ceil(n) - 1, 0) + } + + // compute (this - y) % n, given (this < n) and (y < n) + def subWrap(y: UInt, n: Int): UInt = { + val z = x -& y + if (isPow2(n)) z(n.log2 - 1, 0) else Mux(z(z.getWidth - 1), z + n.U, z)(log2Ceil(n) - 1, 0) + } + + def grouped(width: Int): Seq[UInt] = + (0 until x.getWidth by width).map(base => x(base + width - 1, base)) + + def inRange(base: UInt, bounds: UInt) = x >= base && x < bounds + + def ##(y: Option[UInt]): UInt = y.map(x ## _).getOrElse(x) + + // Like >=, but prevents x-prop for ('x >= 0) + def >==(y: UInt): Bool = x >= y || y === 0.U + } + + implicit class IntToAugmentedInt(private val x: Int) extends AnyVal { + // exact log2 + def log2: Int = { + require(isPow2(x)) + log2Ceil(x) + } + } + + implicit class SeqBoolBitwiseOps(private val x: Seq[Bool]) extends AnyVal { + def &(y: Seq[Bool]): Seq[Bool] = (x zip y).map { case (a, b) => a && b } + + def |(y: Seq[Bool]): Seq[Bool] = padZip(x, y).map { case (a, b) => a || b } + + def ^(y: Seq[Bool]): Seq[Bool] = padZip(x, y).map { case (a, b) => a ^ b } + + def <<(n: Int): Seq[Bool] = Seq.fill(n)(false.B) ++ x + + def >>(n: Int): Seq[Bool] = x drop n + + def unary_~(): Seq[Bool] = x.map(!_) + + def andR: Bool = if (x.isEmpty) true.B else x.reduce(_ && _) + + def orR: Bool = if (x.isEmpty) false.B else x.reduce(_ || _) + + def xorR: Bool = if (x.isEmpty) false.B else x.reduce(_ ^ _) + + private def padZip(y: Seq[Bool], z: Seq[Bool]): Seq[(Bool, Bool)] = y.padTo(z.size, false.B) zip z.padTo(y.size, false.B) + } + + implicit class OptionUIntToAugmentedOptionUInt(private val x: Option[UInt]) extends AnyVal { + def ##(y: UInt): UInt = x.map(_ ## y).getOrElse(y) + + def ##(y: Option[UInt]): Option[UInt] = x.map(_ ## y) + } + +} \ No newline at end of file