// See LICENSE.SiFive for license details. // See LICENSE.Berkeley for license details. package freechips.rocketchip.rocket import Chisel._ import chisel3.experimental.dontTouch import freechips.rocketchip.config.{Parameters, Field} import freechips.rocketchip.subsystem._ import freechips.rocketchip.diplomacy._ import freechips.rocketchip.tile._ import freechips.rocketchip.tilelink._ import freechips.rocketchip.util._ import scala.collection.mutable.ListBuffer import scala.math.max case class DCacheParams( nSets: Int = 64, nWays: Int = 4, rowBits: Int = 64, nTLBEntries: Int = 32, tagECC: Code = new IdentityCode, dataECC: Code = new IdentityCode, dataECCBytes: Int = 1, nMSHRs: Int = 1, nSDQ: Int = 17, nRPQ: Int = 16, nMMIOs: Int = 1, blockBytes: Int = 64, acquireBeforeRelease: Boolean = false, pipelineWayMux: Boolean = false, scratch: Option[BigInt] = None) extends L1CacheParams { def dataScratchpadBytes: Int = scratch.map(_ => nSets*blockBytes).getOrElse(0) def replacement = new RandomReplacement(nWays) require((!scratch.isDefined || nWays == 1), "Scratchpad only allowed in direct-mapped cache.") require((!scratch.isDefined || nMSHRs == 0), "Scratchpad only allowed in blocking cache.") require(isPow2(nSets), s"nSets($nSets) must be pow2") } trait HasL1HellaCacheParameters extends HasL1CacheParameters with HasCoreParameters { val cacheParams = tileParams.dcache.get val cfg = cacheParams def wordBits = coreDataBits def wordBytes = coreDataBytes def wordOffBits = log2Up(wordBytes) def beatBytes = cacheBlockBytes / cacheDataBeats def beatWords = beatBytes / wordBytes def beatOffBits = log2Up(beatBytes) def idxMSB = untagBits-1 def idxLSB = blockOffBits def offsetmsb = idxLSB-1 def offsetlsb = wordOffBits def rowWords = rowBits/wordBits def doNarrowRead = coreDataBits * nWays % rowBits == 0 def encDataBits = cacheParams.dataECC.width(coreDataBits) def encRowBits = encDataBits*rowWords def lrscCycles = 32 // ISA requires 16-insn LRSC sequences to succeed def lrscBackoff = 3 // disallow LRSC reacquisition briefly def blockProbeAfterGrantCycles = 8 // give the processor some time to issue a request after a grant def nIOMSHRs = cacheParams.nMMIOs def maxUncachedInFlight = cacheParams.nMMIOs def dataScratchpadSize = cacheParams.dataScratchpadBytes require(rowBits >= coreDataBits, s"rowBits($rowBits) < coreDataBits($coreDataBits)") // TODO should rowBits even be seperably specifiable? require(rowBits == cacheDataBits, s"rowBits($rowBits) != cacheDataBits($cacheDataBits)") // would need offset addr for puts if data width < xlen require(xLen <= cacheDataBits, s"xLen($xLen) > cacheDataBits($cacheDataBits)") require(!usingVM || untagBits <= pgIdxBits, s"untagBits($untagBits) > pgIdxBits($pgIdxBits)") } abstract class L1HellaCacheModule(implicit val p: Parameters) extends Module with HasL1HellaCacheParameters abstract class L1HellaCacheBundle(implicit val p: Parameters) extends ParameterizedBundle()(p) with HasL1HellaCacheParameters /** Bundle definitions for HellaCache interfaces */ trait HasCoreMemOp extends HasCoreParameters { val addr = UInt(width = coreMaxAddrBits) val tag = Bits(width = dcacheReqTagBits) val cmd = Bits(width = M_SZ) val typ = Bits(width = MT_SZ) } trait HasCoreData extends HasCoreParameters { val data = Bits(width = coreDataBits) } class HellaCacheReqInternal(implicit p: Parameters) extends CoreBundle()(p) with HasCoreMemOp { val phys = Bool() } class HellaCacheReq(implicit p: Parameters) extends HellaCacheReqInternal()(p) with HasCoreData class HellaCacheResp(implicit p: Parameters) extends CoreBundle()(p) with HasCoreMemOp with HasCoreData { val replay = Bool() val has_data = Bool() val data_word_bypass = Bits(width = coreDataBits) val data_raw = Bits(width = coreDataBits) val store_data = Bits(width = coreDataBits) } class AlignmentExceptions extends Bundle { val ld = Bool() val st = Bool() } class HellaCacheExceptions extends Bundle { val ma = new AlignmentExceptions val pf = new AlignmentExceptions val ae = new AlignmentExceptions } class HellaCacheWriteData(implicit p: Parameters) extends CoreBundle()(p) { val data = UInt(width = coreDataBits) val mask = UInt(width = coreDataBytes) } class HellaCachePerfEvents extends Bundle { val acquire = Bool() val release = Bool() val tlbMiss = Bool() } // interface between D$ and processor/DTLB class HellaCacheIO(implicit p: Parameters) extends CoreBundle()(p) { val req = Decoupled(new HellaCacheReq) val s1_kill = Bool(OUTPUT) // kill previous cycle's req val s1_data = new HellaCacheWriteData().asOutput // data for previous cycle's req val s2_nack = Bool(INPUT) // req from two cycles ago is rejected val resp = Valid(new HellaCacheResp).flip val replay_next = Bool(INPUT) val s2_xcpt = (new HellaCacheExceptions).asInput val invalidate_lr = Bool(OUTPUT) val ordered = Bool(INPUT) val perf = new HellaCachePerfEvents().asInput } /** Base classes for Diplomatic TL2 HellaCaches */ abstract class HellaCache(hartid: Int)(implicit p: Parameters) extends LazyModule { private val cfg = p(TileKey).dcache.get val firstMMIO = max(1, cfg.nMSHRs) val node = TLClientNode(Seq(TLClientPortParameters( clients = cfg.scratch.map { _ => Seq( TLClientParameters( name = s"Core ${hartid} DCache MMIO", sourceId = IdRange(0, cfg.nMMIOs), requestFifo = true)) } getOrElse { Seq( TLClientParameters( name = s"Core ${hartid} DCache", sourceId = IdRange(0, firstMMIO), supportsProbe = TransferSizes(cfg.blockBytes, cfg.blockBytes)), TLClientParameters( name = s"Core ${hartid} DCache MMIO", sourceId = IdRange(firstMMIO, firstMMIO+cfg.nMMIOs), requestFifo = true)) }, minLatency = 1))) val module: HellaCacheModule } class HellaCacheBundle(val outer: HellaCache)(implicit p: Parameters) extends CoreBundle()(p) { val hartid = UInt(INPUT, hartIdLen) val cpu = (new HellaCacheIO).flip val ptw = new TLBPTWIO() val errors = new DCacheErrors } class HellaCacheModule(outer: HellaCache) extends LazyModuleImp(outer) with HasL1HellaCacheParameters { implicit val edge = outer.node.edges.out(0) val (tl_out, _) = outer.node.out(0) val io = IO(new HellaCacheBundle(outer)) dontTouch(io.cpu.resp) // Users like to monitor these fields even if the core ignores some signals dontTouch(io.cpu.s1_data) private val fifoManagers = edge.manager.managers.filter(TLFIFOFixer.allUncacheable) fifoManagers.foreach { m => require (m.fifoId == fifoManagers.head.fifoId, s"IOMSHRs must be FIFO for all regions with effects, but HellaCache sees ${m.nodePath.map(_.name)}") } } /** Mix-ins for constructing tiles that have a HellaCache */ trait HasHellaCache { this: BaseTile => val module: HasHellaCacheModule implicit val p: Parameters def findScratchpadFromICache: Option[AddressSet] var nDCachePorts = 0 val dcache: HellaCache = LazyModule( if(tileParams.dcache.get.nMSHRs == 0) { new DCache(hartId, findScratchpadFromICache _, p(RocketCrossingKey).head.knownRatio) } else { new NonBlockingDCache(hartId) }) tlMasterXbar.node := dcache.node } trait HasHellaCacheModule { val outer: HasHellaCache val dcachePorts = ListBuffer[HellaCacheIO]() val dcacheArb = Module(new HellaCacheArbiter(outer.nDCachePorts)(outer.p)) outer.dcache.module.io.cpu <> dcacheArb.io.mem } /** Metadata array used for all HellaCaches */ class L1Metadata(implicit p: Parameters) extends L1HellaCacheBundle()(p) { val coh = new ClientMetadata val tag = UInt(width = tagBits) } object L1Metadata { def apply(tag: Bits, coh: ClientMetadata)(implicit p: Parameters) = { val meta = Wire(new L1Metadata) meta.tag := tag meta.coh := coh meta } } class L1MetaReadReq(implicit p: Parameters) extends L1HellaCacheBundle()(p) { val idx = UInt(width = idxBits) val way_en = UInt(width = nWays) val tag = UInt(width = tagBits) } class L1MetaWriteReq(implicit p: Parameters) extends L1MetaReadReq()(p) { val data = new L1Metadata } class L1MetadataArray[T <: L1Metadata](onReset: () => T)(implicit p: Parameters) extends L1HellaCacheModule()(p) { val rstVal = onReset() val io = new Bundle { val read = Decoupled(new L1MetaReadReq).flip val write = Decoupled(new L1MetaWriteReq).flip val resp = Vec(nWays, rstVal.cloneType).asOutput } val rst_cnt = Reg(init=UInt(0, log2Up(nSets+1))) val rst = rst_cnt < UInt(nSets) val waddr = Mux(rst, rst_cnt, io.write.bits.idx) val wdata = Mux(rst, rstVal, io.write.bits.data).asUInt val wmask = Mux(rst || Bool(nWays == 1), SInt(-1), io.write.bits.way_en.asSInt).toBools val rmask = Mux(rst || Bool(nWays == 1), SInt(-1), io.read.bits.way_en.asSInt).toBools when (rst) { rst_cnt := rst_cnt+UInt(1) } val metabits = rstVal.getWidth val tag_array = SeqMem(nSets, Vec(nWays, UInt(width = metabits))) val wen = rst || io.write.valid when (wen) { tag_array.write(waddr, Vec.fill(nWays)(wdata), wmask) } io.resp := tag_array.read(io.read.bits.idx, io.read.fire()).map(rstVal.fromBits(_)) io.read.ready := !wen // so really this could be a 6T RAM io.write.ready := !rst }