From 1e05fc052510c2a5daea779564996d3986970082 Mon Sep 17 00:00:00 2001 From: Henry Cook Date: Wed, 29 Apr 2015 13:18:26 -0700 Subject: [PATCH 1/2] First pages commit --- uncore/.gitignore | 2 + uncore/LICENSE | 24 + uncore/README.md | 11 + uncore/build.sbt | 13 + uncore/chisel-dependent.sbt | 8 + uncore/doc/TileLink0.3.1Specification.pdf | Bin 0 -> 172795 bytes uncore/index.html | 1 + uncore/src/main/scala/bigmem.scala | 80 ++ uncore/src/main/scala/broadcast.scala | 387 +++++++ uncore/src/main/scala/cache.scala | 1078 ++++++++++++++++++ uncore/src/main/scala/coherence.scala | 688 ++++++++++++ uncore/src/main/scala/consts.scala | 48 + uncore/src/main/scala/directory.scala | 43 + uncore/src/main/scala/ecc.scala | 146 +++ uncore/src/main/scala/htif.scala | 255 +++++ uncore/src/main/scala/memserdes.scala | 584 ++++++++++ uncore/src/main/scala/metadata.scala | 315 ++++++ uncore/src/main/scala/network.scala | 104 ++ uncore/src/main/scala/package.scala | 6 + uncore/src/main/scala/slowio.scala | 70 ++ uncore/src/main/scala/tilelink.scala | 1221 +++++++++++++++++++++ uncore/src/main/scala/uncore.scala | 129 +++ uncore/src/main/scala/util.scala | 106 ++ 23 files changed, 5319 insertions(+) create mode 100644 uncore/.gitignore create mode 100644 uncore/LICENSE create mode 100644 uncore/README.md create mode 100644 uncore/build.sbt create mode 100644 uncore/chisel-dependent.sbt create mode 100644 uncore/doc/TileLink0.3.1Specification.pdf create mode 100644 uncore/index.html create mode 100644 uncore/src/main/scala/bigmem.scala create mode 100644 uncore/src/main/scala/broadcast.scala create mode 100644 uncore/src/main/scala/cache.scala create mode 100644 uncore/src/main/scala/coherence.scala create mode 100644 uncore/src/main/scala/consts.scala create mode 100644 uncore/src/main/scala/directory.scala create mode 100644 uncore/src/main/scala/ecc.scala create mode 100644 uncore/src/main/scala/htif.scala create mode 100644 uncore/src/main/scala/memserdes.scala create mode 100644 uncore/src/main/scala/metadata.scala create mode 100644 uncore/src/main/scala/network.scala create mode 100644 uncore/src/main/scala/package.scala create mode 100644 uncore/src/main/scala/slowio.scala create mode 100644 uncore/src/main/scala/tilelink.scala create mode 100644 uncore/src/main/scala/uncore.scala create mode 100644 uncore/src/main/scala/util.scala diff --git a/uncore/.gitignore b/uncore/.gitignore new file mode 100644 index 00000000..555feb41 --- /dev/null +++ b/uncore/.gitignore @@ -0,0 +1,2 @@ +target/ +project/target/ diff --git a/uncore/LICENSE b/uncore/LICENSE new file mode 100644 index 00000000..7cff15e4 --- /dev/null +++ b/uncore/LICENSE @@ -0,0 +1,24 @@ +Copyright (c) 2012-2014, The Regents of the University of California +(Regents). All Rights Reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. Neither the name of the Regents nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, +SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING +OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF REGENTS HAS +BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED +HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE +MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. diff --git a/uncore/README.md b/uncore/README.md new file mode 100644 index 00000000..003f9caa --- /dev/null +++ b/uncore/README.md @@ -0,0 +1,11 @@ +Uncore Library +============== + +This is the repository for uncore components assosciated with Rocket chip +project. To uses these modules, include this repo as a git submodule within +the your chip repository and add it as Project in your chip's build.scala. +These components are only dependent on Chisel, i.e. + + lazy val uncore = Project("uncore", file("uncore"), settings = buildSettings) dependsOn(chisel) + +Documentation about the uncore library will come in the near future. diff --git a/uncore/build.sbt b/uncore/build.sbt new file mode 100644 index 00000000..d78d02ca --- /dev/null +++ b/uncore/build.sbt @@ -0,0 +1,13 @@ +organization := "edu.berkeley.cs" + +version := "2.0" + +name := "uncore" + +scalaVersion := "2.10.2" + +site.settings + +ghpages.settings + +git.remoteRepo := "git@github.com:ucb-bar/uncore.git" diff --git a/uncore/chisel-dependent.sbt b/uncore/chisel-dependent.sbt new file mode 100644 index 00000000..88eb615c --- /dev/null +++ b/uncore/chisel-dependent.sbt @@ -0,0 +1,8 @@ +// Provide a managed dependency on chisel if -DchiselVersion="" is +// supplied on the command line. + +val chiselVersion_u = System.getProperty("chiselVersion", "None") + +// _u a temporary fix until sbt 13.6 https://github.com/sbt/sbt/issues/1465 + +libraryDependencies ++= ( if (chiselVersion_u != "None" ) ("edu.berkeley.cs" %% "chisel" % chiselVersion_u) :: Nil; else Nil) diff --git a/uncore/doc/TileLink0.3.1Specification.pdf b/uncore/doc/TileLink0.3.1Specification.pdf new file mode 100644 index 0000000000000000000000000000000000000000..23666814d9e8738924e59e5f9f8872e7fa58d725 GIT binary patch literal 172795 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%0D)V|>HMz50 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𝔪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Ǒ=H=6uGM>Xvl&Q}RM7rRbY0n>~^JJJV%Y##*X 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
60T{U6Tlg?C=PMUPDdFTobF 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 literal 0 HcmV?d00001 diff --git a/uncore/index.html b/uncore/index.html new file mode 100644 index 00000000..03f98016 --- /dev/null +++ b/uncore/index.html @@ -0,0 +1 @@ +My GitHub Page diff --git a/uncore/src/main/scala/bigmem.scala b/uncore/src/main/scala/bigmem.scala new file mode 100644 index 00000000..fbbc4c07 --- /dev/null +++ b/uncore/src/main/scala/bigmem.scala @@ -0,0 +1,80 @@ +// See LICENSE for license details. + +package uncore +import Chisel._ + +class BigMem[T <: Data](n: Int, preLatency: Int, postLatency: Int, leaf: Mem[UInt], noMask: Boolean = false)(gen: => T) extends Module +{ + class Inputs extends Bundle { + val addr = UInt(INPUT, log2Up(n)) + val rw = Bool(INPUT) + val wdata = gen.asInput + val wmask = gen.asInput + override def clone = new Inputs().asInstanceOf[this.type] + } + val io = new Bundle { + val in = Valid(new Inputs).flip + val rdata = gen.asOutput + } + val data = gen + val colMux = if (2*data.getWidth <= leaf.data.getWidth && n > leaf.n) 1 << math.floor(math.log(leaf.data.getWidth/data.getWidth)/math.log(2)).toInt else 1 + val nWide = if (data.getWidth > leaf.data.getWidth) 1+(data.getWidth-1)/leaf.data.getWidth else 1 + val nDeep = if (n > colMux*leaf.n) 1+(n-1)/(colMux*leaf.n) else 1 + if (nDeep > 1 || colMux > 1) + require(isPow2(n) && isPow2(leaf.n)) + + val rdataDeep = Vec.fill(nDeep){Bits()} + val rdataSel = Vec.fill(nDeep){Bool()} + for (i <- 0 until nDeep) { + val in = Pipe(io.in.valid && (if (nDeep == 1) Bool(true) else UInt(i) === io.in.bits.addr(log2Up(n)-1, log2Up(n/nDeep))), io.in.bits, preLatency) + val idx = in.bits.addr(log2Up(n/nDeep/colMux)-1, 0) + val wdata = in.bits.wdata.toBits + val wmask = in.bits.wmask.toBits + val ren = in.valid && !in.bits.rw + val reg_ren = Reg(next=ren) + val rdata = Vec.fill(nWide){Bits()} + + val r = Pipe(ren, in.bits.addr, postLatency) + + for (j <- 0 until nWide) { + val mem = leaf.clone + var dout: Bits = null + val ridx = if (postLatency > 0) Reg(Bits()) else null + + var wmask0 = Fill(colMux, wmask(math.min(wmask.getWidth, leaf.data.getWidth*(j+1))-1, leaf.data.getWidth*j)) + if (colMux > 1) + wmask0 = wmask0 & FillInterleaved(gen.getWidth, UIntToOH(in.bits.addr(log2Up(n/nDeep)-1, log2Up(n/nDeep/colMux)), log2Up(colMux))) + val wdata0 = Fill(colMux, wdata(math.min(wdata.getWidth, leaf.data.getWidth*(j+1))-1, leaf.data.getWidth*j)) + when (in.valid) { + when (in.bits.rw) { + if (noMask) + mem.write(idx, wdata0) + else + mem.write(idx, wdata0, wmask0) + } + .otherwise { if (postLatency > 0) ridx := idx } + } + + if (postLatency == 0) { + dout = mem(idx) + } else if (postLatency == 1) { + dout = mem(ridx) + } else + dout = Pipe(reg_ren, mem(ridx), postLatency-1).bits + + rdata(j) := dout + } + val rdataWide = rdata.reduceLeft((x, y) => Cat(y, x)) + + var colMuxOut = rdataWide + if (colMux > 1) { + val colMuxIn = Vec((0 until colMux).map(k => rdataWide(gen.getWidth*(k+1)-1, gen.getWidth*k))) + colMuxOut = colMuxIn(r.bits(log2Up(n/nDeep)-1, log2Up(n/nDeep/colMux))) + } + + rdataDeep(i) := colMuxOut + rdataSel(i) := r.valid + } + + io.rdata := Mux1H(rdataSel, rdataDeep) +} diff --git a/uncore/src/main/scala/broadcast.scala b/uncore/src/main/scala/broadcast.scala new file mode 100644 index 00000000..b554b231 --- /dev/null +++ b/uncore/src/main/scala/broadcast.scala @@ -0,0 +1,387 @@ +// See LICENSE for license details. + +package uncore +import Chisel._ + +case object L2StoreDataQueueDepth extends Field[Int] + +trait BroadcastHubParameters extends CoherenceAgentParameters { + val sdqDepth = params(L2StoreDataQueueDepth)*innerDataBeats + val dqIdxBits = math.max(log2Up(nReleaseTransactors) + 1, log2Up(sdqDepth)) + val nDataQueueLocations = 3 //Stores, VoluntaryWBs, Releases +} + +class DataQueueLocation extends Bundle with BroadcastHubParameters { + val idx = UInt(width = dqIdxBits) + val loc = UInt(width = log2Ceil(nDataQueueLocations)) +} + +object DataQueueLocation { + def apply(idx: UInt, loc: UInt) = { + val d = new DataQueueLocation + d.idx := idx + d.loc := loc + d + } +} + +class L2BroadcastHub extends ManagerCoherenceAgent + with BroadcastHubParameters { + val internalDataBits = new DataQueueLocation().getWidth + val inStoreQueue :: inVolWBQueue :: inClientReleaseQueue :: Nil = Enum(UInt(), nDataQueueLocations) + + // Create SHRs for outstanding transactions + val trackerList = (0 until nReleaseTransactors).map(id => + Module(new BroadcastVoluntaryReleaseTracker(id), {case TLDataBits => internalDataBits})) ++ + (nReleaseTransactors until nTransactors).map(id => + Module(new BroadcastAcquireTracker(id), {case TLDataBits => internalDataBits})) + + // Propagate incoherence flags + trackerList.map(_.io.incoherent := io.incoherent.toBits) + + // Queue to store impending Put data + val sdq = Vec.fill(sdqDepth){ Reg(io.iacq().data) } + val sdq_val = Reg(init=Bits(0, sdqDepth)) + val sdq_alloc_id = PriorityEncoder(~sdq_val) + val sdq_rdy = !sdq_val.andR + val sdq_enq = io.inner.acquire.fire() && io.iacq().hasData() + when (sdq_enq) { sdq(sdq_alloc_id) := io.iacq().data } + + // Handle acquire transaction initiation + val trackerAcquireIOs = trackerList.map(_.io.inner.acquire) + val acquireConflicts = Vec(trackerList.map(_.io.has_acquire_conflict)).toBits + val acquireMatches = Vec(trackerList.map(_.io.has_acquire_match)).toBits + val acquireReadys = Vec(trackerAcquireIOs.map(_.ready)).toBits + val acquire_idx = Mux(acquireMatches.orR, + PriorityEncoder(acquireMatches), + PriorityEncoder(acquireReadys)) + + val block_acquires = acquireConflicts.orR || !sdq_rdy + io.inner.acquire.ready := acquireReadys.orR && !block_acquires + trackerAcquireIOs.zipWithIndex.foreach { + case(tracker, i) => + tracker.bits := io.inner.acquire.bits + tracker.bits.data := DataQueueLocation(sdq_alloc_id, inStoreQueue).toBits + tracker.valid := io.inner.acquire.valid && !block_acquires && (acquire_idx === UInt(i)) + } + + // Queue to store impending Voluntary Release data + val voluntary = io.irel().isVoluntary() + val vwbdq_enq = io.inner.release.fire() && voluntary && io.irel().hasData() + val (rel_data_cnt, rel_data_done) = Counter(vwbdq_enq, innerDataBeats) //TODO Zero width + val vwbdq = Vec.fill(innerDataBeats){ Reg(io.irel().data) } //TODO Assumes nReleaseTransactors == 1 + when(vwbdq_enq) { vwbdq(rel_data_cnt) := io.irel().data } + + // Handle releases, which might be voluntary and might have data + val trackerReleaseIOs = trackerList.map(_.io.inner.release) + val releaseReadys = Vec(trackerReleaseIOs.map(_.ready)).toBits + val releaseMatches = Vec(trackerList.map(_.io.has_release_match)).toBits + val release_idx = PriorityEncoder(releaseMatches) + io.inner.release.ready := releaseReadys(release_idx) + trackerReleaseIOs.zipWithIndex.foreach { + case(tracker, i) => + tracker.valid := io.inner.release.valid && (release_idx === UInt(i)) + tracker.bits := io.inner.release.bits + tracker.bits.data := DataQueueLocation(rel_data_cnt, + (if(i < nReleaseTransactors) inVolWBQueue + else inClientReleaseQueue)).toBits + } + assert(!(io.inner.release.valid && !releaseMatches.orR), + "Non-voluntary release should always have a Tracker waiting for it.") + + // Wire probe requests and grant reply to clients, finish acks from clients + // Note that we bypass the Grant data subbundles + io.inner.grant.bits.data := io.outer.grant.bits.data + io.inner.grant.bits.addr_beat := io.outer.grant.bits.addr_beat + doOutputArbitration(io.inner.grant, trackerList.map(_.io.inner.grant)) + doOutputArbitration(io.inner.probe, trackerList.map(_.io.inner.probe)) + doInputRouting(io.inner.finish, trackerList.map(_.io.inner.finish)) + + // Create an arbiter for the one memory port + val outer_arb = Module(new ClientUncachedTileLinkIOArbiter(trackerList.size), + { case TLId => params(OuterTLId) + case TLDataBits => internalDataBits }) + outer_arb.io.in <> trackerList.map(_.io.outer) + // Get the pending data out of the store data queue + val outer_data_ptr = new DataQueueLocation().fromBits(outer_arb.io.out.acquire.bits.data) + val is_in_sdq = outer_data_ptr.loc === inStoreQueue + val free_sdq = io.outer.acquire.fire() && + io.outer.acquire.bits.hasData() && + outer_data_ptr.loc === inStoreQueue + io.outer.acquire.bits.data := MuxLookup(outer_data_ptr.loc, io.irel().data, Array( + inStoreQueue -> sdq(outer_data_ptr.idx), + inVolWBQueue -> vwbdq(outer_data_ptr.idx))) + io.outer <> outer_arb.io.out + + // Update SDQ valid bits + when (io.outer.acquire.valid || sdq_enq) { + sdq_val := sdq_val & ~(UIntToOH(outer_data_ptr.idx) & Fill(sdqDepth, free_sdq)) | + PriorityEncoderOH(~sdq_val(sdqDepth-1,0)) & Fill(sdqDepth, sdq_enq) + } +} + +class BroadcastXactTracker extends XactTracker { + val io = new ManagerXactTrackerIO +} + +class BroadcastVoluntaryReleaseTracker(trackerId: Int) extends BroadcastXactTracker { + val s_idle :: s_outer :: s_grant :: s_ack :: Nil = Enum(UInt(), 4) + val state = Reg(init=s_idle) + + val xact = Reg(Bundle(new ReleaseFromSrc, { case TLId => params(InnerTLId); case TLDataBits => 0 })) + val data_buffer = Vec.fill(innerDataBeats){ Reg(io.irel().data.clone) } + val coh = ManagerMetadata.onReset + + val collect_irel_data = Reg(init=Bool(false)) + val irel_data_valid = Reg(init=Bits(0, width = innerDataBeats)) + val irel_data_done = connectIncomingDataBeatCounter(io.inner.release) + val (oacq_data_cnt, oacq_data_done) = connectOutgoingDataBeatCounter(io.outer.acquire) + + io.has_acquire_conflict := Bool(false) + io.has_release_match := io.irel().isVoluntary() + io.has_acquire_match := Bool(false) + + io.outer.acquire.valid := Bool(false) + io.outer.grant.ready := Bool(false) + io.inner.acquire.ready := Bool(false) + io.inner.probe.valid := Bool(false) + io.inner.release.ready := Bool(false) + io.inner.grant.valid := Bool(false) + io.inner.finish.ready := Bool(false) + + io.inner.grant.bits := coh.makeGrant(xact, UInt(trackerId)) + + //TODO: Use io.outer.release instead? + io.outer.acquire.bits := Bundle( + PutBlock( + client_xact_id = UInt(trackerId), + addr_block = xact.addr_block, + addr_beat = oacq_data_cnt, + data = data_buffer(oacq_data_cnt)))(outerTLParams) + + when(collect_irel_data) { + io.inner.release.ready := Bool(true) + when(io.inner.release.valid) { + data_buffer(io.irel().addr_beat) := io.irel().data + irel_data_valid(io.irel().addr_beat) := Bool(true) + } + when(irel_data_done) { collect_irel_data := Bool(false) } + } + + switch (state) { + is(s_idle) { + io.inner.release.ready := Bool(true) + when( io.inner.release.valid ) { + xact := io.irel() + data_buffer(UInt(0)) := io.irel().data + collect_irel_data := io.irel().hasMultibeatData() + irel_data_valid := io.irel().hasData() << io.irel().addr_beat + state := Mux(io.irel().hasData(), s_outer, + Mux(io.irel().requiresAck(), s_ack, s_idle)) + } + } + is(s_outer) { + io.outer.acquire.valid := !collect_irel_data || irel_data_valid(oacq_data_cnt) + when(oacq_data_done) { + state := s_grant // converted irel to oacq, so expect grant TODO: Mux(xact.requiresAck(), s_grant, s_idle) ? + } + } + is(s_grant) { // Forward the Grant.voluntaryAck + io.outer.grant.ready := io.inner.grant.ready + io.inner.grant.valid := io.outer.grant.valid + when(io.inner.grant.fire()) { + state := Mux(io.ignt().requiresAck(), s_ack, s_idle) + } + } + is(s_ack) { + // TODO: This state is unnecessary if no client will ever issue the + // pending Acquire that caused this writeback until it receives the + // Grant.voluntaryAck for this writeback + io.inner.finish.ready := Bool(true) + when(io.inner.finish.valid) { state := s_idle } + } + } +} + +class BroadcastAcquireTracker(trackerId: Int) extends BroadcastXactTracker { + val s_idle :: s_probe :: s_mem_read :: s_mem_write :: s_make_grant :: s_mem_resp :: s_ack :: Nil = Enum(UInt(), 7) + val state = Reg(init=s_idle) + + val xact = Reg(Bundle(new AcquireFromSrc, { case TLId => params(InnerTLId); case TLDataBits => 0 })) + val data_buffer = Vec.fill(innerDataBeats){ Reg(io.iacq().data.clone) } + val coh = ManagerMetadata.onReset + + assert(!(state != s_idle && xact.isBuiltInType() && + Vec(Acquire.getType, Acquire.putType, Acquire.putAtomicType, + Acquire.prefetchType).contains(xact.a_type)), + "Broadcast Hub does not support PutAtomics, subblock Gets/Puts, or prefetches") // TODO + + val release_count = Reg(init=UInt(0, width = log2Up(io.inner.tlNCachingClients+1))) + val pending_probes = Reg(init=Bits(0, width = io.inner.tlNCachingClients)) + val curr_p_id = PriorityEncoder(pending_probes) + val full_sharers = coh.full() + val probe_self = io.inner.acquire.bits.requiresSelfProbe() + val mask_self_true = UInt(UInt(1) << io.inner.acquire.bits.client_id, width = io.inner.tlNCachingClients) + val mask_self_false = ~UInt(UInt(1) << io.inner.acquire.bits.client_id, width = io.inner.tlNCachingClients) + val mask_self = Mux(probe_self, full_sharers | mask_self_true, full_sharers & mask_self_false) + val mask_incoherent = mask_self & ~io.incoherent.toBits + + val collect_iacq_data = Reg(init=Bool(false)) + val iacq_data_valid = Reg(init=Bits(0, width = innerDataBeats)) + val iacq_data_done = connectIncomingDataBeatCounter(io.inner.acquire) + val irel_data_done = connectIncomingDataBeatCounter(io.inner.release) + val (ignt_data_cnt, ignt_data_done) = connectOutgoingDataBeatCounter(io.inner.grant) + val (oacq_data_cnt, oacq_data_done) = connectOutgoingDataBeatCounter(io.outer.acquire) + val ognt_data_done = connectIncomingDataBeatCounter(io.outer.grant) + val pending_ognt_ack = Reg(init=Bool(false)) + val pending_outer_write = xact.hasData() + val pending_outer_write_ = io.iacq().hasData() + val pending_outer_read = io.ignt().hasData() + val pending_outer_read_ = coh.makeGrant(io.iacq(), UInt(trackerId)).hasData() + + io.has_acquire_conflict := xact.conflicts(io.iacq()) && + (state != s_idle) && + !collect_iacq_data + io.has_acquire_match := xact.conflicts(io.iacq()) && + collect_iacq_data + io.has_release_match := xact.conflicts(io.irel()) && + !io.irel().isVoluntary() && + (state === s_probe) + + val outer_write_acq = Bundle(PutBlock( + client_xact_id = UInt(trackerId), + addr_block = xact.addr_block, + addr_beat = oacq_data_cnt, + data = data_buffer(oacq_data_cnt)))(outerTLParams) + val outer_write_rel = Bundle(PutBlock( + client_xact_id = UInt(trackerId), + addr_block = xact.addr_block, + addr_beat = io.irel().addr_beat, + data = io.irel().data))(outerTLParams) + val outer_read = Bundle(GetBlock( + client_xact_id = UInt(trackerId), + addr_block = xact.addr_block))(outerTLParams) + + io.outer.acquire.valid := Bool(false) + io.outer.acquire.bits := outer_read //default + io.outer.grant.ready := Bool(false) + + io.inner.probe.valid := Bool(false) + io.inner.probe.bits := coh.makeProbe(curr_p_id, xact) + + io.inner.grant.valid := Bool(false) + io.inner.grant.bits := coh.makeGrant(xact, UInt(trackerId)) // Data bypassed in parent + + io.inner.acquire.ready := Bool(false) + io.inner.release.ready := Bool(false) + io.inner.finish.ready := Bool(false) + + assert(!(state != s_idle && collect_iacq_data && io.inner.acquire.fire() && + io.iacq().client_id != xact.client_id), + "AcquireTracker accepted data beat from different network source than initial request.") + + assert(!(state != s_idle && collect_iacq_data && io.inner.acquire.fire() && + io.iacq().client_xact_id != xact.client_xact_id), + "AcquireTracker accepted data beat from different client transaction than initial request.") + + assert(!(state === s_idle && io.inner.acquire.fire() && + io.iacq().addr_beat != UInt(0)), + "AcquireTracker initialized with a tail data beat.") + + when(collect_iacq_data) { + io.inner.acquire.ready := Bool(true) + when(io.inner.acquire.valid) { + data_buffer(io.iacq().addr_beat) := io.iacq().data + iacq_data_valid(io.iacq().addr_beat) := Bool(true) + } + when(iacq_data_done) { collect_iacq_data := Bool(false) } + } + + when(pending_ognt_ack) { + io.outer.grant.ready := Bool(true) + when(io.outer.grant.valid) { pending_ognt_ack := Bool(false) } + //TODO add finish queue if this isnt the last level manager + } + + switch (state) { + is(s_idle) { + io.inner.acquire.ready := Bool(true) + when(io.inner.acquire.valid) { + xact := io.iacq() + data_buffer(UInt(0)) := io.iacq().data + collect_iacq_data := io.iacq().hasMultibeatData() + iacq_data_valid := io.iacq().hasData() << io.iacq().addr_beat + val needs_probes = mask_incoherent.orR + when(needs_probes) { + pending_probes := mask_incoherent + release_count := PopCount(mask_incoherent) + } + state := Mux(needs_probes, s_probe, + Mux(pending_outer_write_, s_mem_write, + Mux(pending_outer_read_, s_mem_read, s_make_grant))) + } + } + is(s_probe) { + // Generate probes + io.inner.probe.valid := pending_probes.orR + when(io.inner.probe.ready) { + pending_probes := pending_probes & ~UIntToOH(curr_p_id) + } + + // Handle releases, which may have data to be written back + io.inner.release.ready := !io.irel().hasData() || io.outer.acquire.ready + when(io.inner.release.valid) { + when(io.irel().hasData()) { + io.outer.acquire.valid := Bool(true) + io.outer.acquire.bits := outer_write_rel + when(io.outer.acquire.ready) { + when(oacq_data_done) { + pending_ognt_ack := Bool(true) + release_count := release_count - UInt(1) + when(release_count === UInt(1)) { + state := Mux(pending_outer_write, s_mem_write, + Mux(pending_outer_read, s_mem_read, s_make_grant)) + } + } + } + } .otherwise { + release_count := release_count - UInt(1) + when(release_count === UInt(1)) { + state := Mux(pending_outer_write, s_mem_write, + Mux(pending_outer_read, s_mem_read, s_make_grant)) + } + } + } + } + is(s_mem_write) { // Write data to outer memory + io.outer.acquire.valid := !pending_ognt_ack || !collect_iacq_data || iacq_data_valid(oacq_data_cnt) + io.outer.acquire.bits := outer_write_acq + when(oacq_data_done) { + pending_ognt_ack := Bool(true) + state := Mux(pending_outer_read, s_mem_read, s_mem_resp) + } + } + is(s_mem_read) { // Read data from outer memory (possibly what was just written) + io.outer.acquire.valid := !pending_ognt_ack + io.outer.acquire.bits := outer_read + when(io.outer.acquire.fire()) { state := s_mem_resp } + } + is(s_mem_resp) { // Wait to forward grants from outer memory + io.outer.grant.ready := io.inner.grant.ready + io.inner.grant.valid := io.outer.grant.valid + when(ignt_data_done) { + state := Mux(io.ignt().requiresAck(), s_ack, s_idle) + } + } + is(s_make_grant) { // Manufacture a local grant (some kind of permission upgrade) + io.inner.grant.valid := Bool(true) + when(io.inner.grant.ready) { + state := Mux(io.ignt().requiresAck(), s_ack, s_idle) + } + } + is(s_ack) { // Wait for transaction to complete + io.inner.finish.ready := Bool(true) + when(io.inner.finish.valid) { state := s_idle } + } + } +} diff --git a/uncore/src/main/scala/cache.scala b/uncore/src/main/scala/cache.scala new file mode 100644 index 00000000..7fcd1408 --- /dev/null +++ b/uncore/src/main/scala/cache.scala @@ -0,0 +1,1078 @@ +// See LICENSE for license details. + +package uncore +import Chisel._ +import scala.reflect.ClassTag + +case object CacheName extends Field[String] +case object NSets extends Field[Int] +case object NWays extends Field[Int] +case object RowBits extends Field[Int] +case object Replacer extends Field[() => ReplacementPolicy] +case object AmoAluOperandBits extends Field[Int] +case object L2DirectoryRepresentation extends Field[DirectoryRepresentation] +case object NPrimaryMisses extends Field[Int] +case object NSecondaryMisses extends Field[Int] +case object CacheBlockBytes extends Field[Int] +case object CacheBlockOffsetBits extends Field[Int] +case object ECCCode extends Field[Option[Code]] + +abstract trait CacheParameters extends UsesParameters { + val nSets = params(NSets) + val blockOffBits = params(CacheBlockOffsetBits) + val idxBits = log2Up(nSets) + val untagBits = blockOffBits + idxBits + val tagBits = params(PAddrBits) - untagBits + val nWays = params(NWays) + val wayBits = log2Up(nWays) + val isDM = nWays == 1 + val rowBits = params(RowBits) + val rowBytes = rowBits/8 + val rowOffBits = log2Up(rowBytes) + val code = params(ECCCode).getOrElse(new IdentityCode) +} + +abstract class CacheBundle extends Bundle with CacheParameters +abstract class CacheModule extends Module with CacheParameters + +class StoreGen(typ: Bits, addr: Bits, dat: Bits) { + val byte = typ === MT_B || typ === MT_BU + val half = typ === MT_H || typ === MT_HU + val word = typ === MT_W || typ === MT_WU + def mask = + Mux(byte, Bits( 1) << addr(2,0), + Mux(half, Bits( 3) << Cat(addr(2,1), Bits(0,1)), + Mux(word, Bits( 15) << Cat(addr(2), Bits(0,2)), + Bits(255)))) + def data = + Mux(byte, Fill(8, dat( 7,0)), + Mux(half, Fill(4, dat(15,0)), + wordData)) + lazy val wordData = + Mux(word, Fill(2, dat(31,0)), + dat) +} + +class LoadGen(typ: Bits, addr: Bits, dat: Bits, zero: Bool) { + val t = new StoreGen(typ, addr, dat) + val sign = typ === MT_B || typ === MT_H || typ === MT_W || typ === MT_D + + val wordShift = Mux(addr(2), dat(63,32), dat(31,0)) + val word = Cat(Mux(t.word, Fill(32, sign && wordShift(31)), dat(63,32)), wordShift) + val halfShift = Mux(addr(1), word(31,16), word(15,0)) + val half = Cat(Mux(t.half, Fill(48, sign && halfShift(15)), word(63,16)), halfShift) + val byteShift = Mux(zero, UInt(0), Mux(addr(0), half(15,8), half(7,0))) + val byte = Cat(Mux(zero || t.byte, Fill(56, sign && byteShift(7)), half(63,8)), byteShift) +} + +class AMOALU extends CacheModule { + val operandBits = params(AmoAluOperandBits) + require(operandBits == 64) + val io = new Bundle { + val addr = Bits(INPUT, blockOffBits) + val cmd = Bits(INPUT, M_SZ) + val typ = Bits(INPUT, MT_SZ) + val lhs = Bits(INPUT, operandBits) + val rhs = Bits(INPUT, operandBits) + val out = Bits(OUTPUT, operandBits) + } + + val storegen = new StoreGen(io.typ, io.addr, io.rhs) + val rhs = storegen.wordData + + val sgned = io.cmd === M_XA_MIN || io.cmd === M_XA_MAX + val max = io.cmd === M_XA_MAX || io.cmd === M_XA_MAXU + val min = io.cmd === M_XA_MIN || io.cmd === M_XA_MINU + val word = io.typ === MT_W || io.typ === MT_WU || // Logic minimization: + io.typ === MT_B || io.typ === MT_BU + + val mask = SInt(-1,64) ^ (io.addr(2) << UInt(31)) + val adder_out = (io.lhs & mask).toUInt + (rhs & mask) + + val cmp_lhs = Mux(word && !io.addr(2), io.lhs(31), io.lhs(63)) + val cmp_rhs = Mux(word && !io.addr(2), rhs(31), rhs(63)) + val lt_lo = io.lhs(31,0) < rhs(31,0) + val lt_hi = io.lhs(63,32) < rhs(63,32) + val eq_hi = io.lhs(63,32) === rhs(63,32) + val lt = Mux(word, Mux(io.addr(2), lt_hi, lt_lo), lt_hi || eq_hi && lt_lo) + val less = Mux(cmp_lhs === cmp_rhs, lt, Mux(sgned, cmp_lhs, cmp_rhs)) + + val out = Mux(io.cmd === M_XA_ADD, adder_out, + Mux(io.cmd === M_XA_AND, io.lhs & rhs, + Mux(io.cmd === M_XA_OR, io.lhs | rhs, + Mux(io.cmd === M_XA_XOR, io.lhs ^ rhs, + Mux(Mux(less, min, max), io.lhs, + storegen.data))))) + + val wmask = FillInterleaved(8, storegen.mask) + io.out := wmask & out | ~wmask & io.lhs +} + +abstract class ReplacementPolicy { + def way: UInt + def miss: Unit + def hit: Unit +} + +class RandomReplacement(ways: Int) extends ReplacementPolicy { + private val replace = Bool() + replace := Bool(false) + val lfsr = LFSR16(replace) + + def way = if(ways == 1) UInt(0) else lfsr(log2Up(ways)-1,0) + def miss = replace := Bool(true) + def hit = {} +} + +abstract class Metadata extends CacheBundle { + val tag = Bits(width = tagBits) + val coh: CoherenceMetadata +} + +class MetaReadReq extends CacheBundle { + val idx = Bits(width = idxBits) +} + +class MetaWriteReq[T <: Metadata](gen: T) extends MetaReadReq { + val way_en = Bits(width = nWays) + val data = gen.clone + override def clone = new MetaWriteReq(gen).asInstanceOf[this.type] +} + +class MetadataArray[T <: Metadata](makeRstVal: () => T) extends CacheModule { + val rstVal = makeRstVal() + val io = new Bundle { + val read = Decoupled(new MetaReadReq).flip + val write = Decoupled(new MetaWriteReq(rstVal.clone)).flip + val resp = Vec.fill(nWays){rstVal.clone.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).toBits + val wmask = Mux(rst, SInt(-1), io.write.bits.way_en).toUInt + when (rst) { rst_cnt := rst_cnt+UInt(1) } + + val metabits = rstVal.getWidth + val tag_arr = Mem(UInt(width = metabits*nWays), nSets, seqRead = true) + when (rst || io.write.valid) { + tag_arr.write(waddr, Fill(nWays, wdata), FillInterleaved(metabits, wmask)) + } + + val tags = tag_arr(RegEnable(io.read.bits.idx, io.read.valid)) + io.resp := io.resp.fromBits(tags) + io.read.ready := !rst && !io.write.valid // so really this could be a 6T RAM + io.write.ready := !rst +} + +abstract trait L2HellaCacheParameters extends CacheParameters with CoherenceAgentParameters { + val idxMSB = idxBits-1 + val idxLSB = 0 + val blockAddrBits = params(TLBlockAddrBits) + val refillCyclesPerBeat = outerDataBits/rowBits + val refillCycles = refillCyclesPerBeat*outerDataBeats + val internalDataBeats = params(CacheBlockBytes)*8/rowBits + require(refillCyclesPerBeat == 1) + val amoAluOperandBits = params(AmoAluOperandBits) + require(amoAluOperandBits <= innerDataBits) + require(rowBits == innerDataBits) // TODO: relax this by improving s_data_* states + val nSecondaryMisses = params(NSecondaryMisses) + val isLastLevelCache = true + val ignoresWriteMask = !params(ECCCode).isEmpty +} + +abstract class L2HellaCacheBundle extends Bundle with L2HellaCacheParameters +abstract class L2HellaCacheModule extends Module with L2HellaCacheParameters { + def doInternalOutputArbitration[T <: Data : ClassTag]( + out: DecoupledIO[T], + ins: Seq[DecoupledIO[T]]) { + val arb = Module(new RRArbiter(out.bits.clone, ins.size)) + out <> arb.io.out + arb.io.in <> ins + } + + def doInternalInputRouting[T <: HasL2Id](in: ValidIO[T], outs: Seq[ValidIO[T]]) { + outs.map(_.bits := in.bits) + outs.zipWithIndex.map { case (o,i) => o.valid := in.valid && in.bits.id === UInt(i) } + } +} + +trait HasL2Id extends Bundle with CoherenceAgentParameters { + val id = UInt(width = log2Up(nTransactors + 1)) +} + +trait HasL2InternalRequestState extends L2HellaCacheBundle { + val tag_match = Bool() + val meta = new L2Metadata + val way_en = Bits(width = nWays) +} + +trait HasL2BeatAddr extends L2HellaCacheBundle { + val addr_beat = UInt(width = log2Up(refillCycles)) +} + +trait HasL2Data extends L2HellaCacheBundle + with HasL2BeatAddr { + val data = UInt(width = rowBits) + def hasData(dummy: Int = 0) = Bool(true) + def hasMultibeatData(dummy: Int = 0) = Bool(refillCycles > 1) +} + +class L2Metadata extends Metadata with L2HellaCacheParameters { + val coh = new HierarchicalMetadata +} + +object L2Metadata { + def apply(tag: Bits, coh: HierarchicalMetadata) = { + val meta = new L2Metadata + meta.tag := tag + meta.coh := coh + meta + } +} + +class L2MetaReadReq extends MetaReadReq with HasL2Id { + val tag = Bits(width = tagBits) +} + +class L2MetaWriteReq extends MetaWriteReq[L2Metadata](new L2Metadata) + with HasL2Id { + override def clone = new L2MetaWriteReq().asInstanceOf[this.type] +} + +class L2MetaResp extends L2HellaCacheBundle + with HasL2Id + with HasL2InternalRequestState + +trait HasL2MetaReadIO extends L2HellaCacheBundle { + val read = Decoupled(new L2MetaReadReq) + val resp = Valid(new L2MetaResp).flip +} + +trait HasL2MetaWriteIO extends L2HellaCacheBundle { + val write = Decoupled(new L2MetaWriteReq) +} + +class L2MetaRWIO extends L2HellaCacheBundle with HasL2MetaReadIO with HasL2MetaWriteIO + +class L2MetadataArray extends L2HellaCacheModule { + val io = new L2MetaRWIO().flip + + def onReset = L2Metadata(UInt(0), HierarchicalMetadata.onReset) + val meta = Module(new MetadataArray(onReset _)) + meta.io.read <> io.read + meta.io.write <> io.write + + val s1_tag = RegEnable(io.read.bits.tag, io.read.valid) + val s1_id = RegEnable(io.read.bits.id, io.read.valid) + def wayMap[T <: Data](f: Int => T) = Vec((0 until nWays).map(f)) + val s1_clk_en = Reg(next = io.read.fire()) + val s1_tag_eq_way = wayMap((w: Int) => meta.io.resp(w).tag === s1_tag) + val s1_tag_match_way = wayMap((w: Int) => s1_tag_eq_way(w) && meta.io.resp(w).coh.outer.isValid()).toBits + val s2_tag_match_way = RegEnable(s1_tag_match_way, s1_clk_en) + val s2_tag_match = s2_tag_match_way.orR + val s2_hit_coh = Mux1H(s2_tag_match_way, wayMap((w: Int) => RegEnable(meta.io.resp(w).coh, s1_clk_en))) + + val replacer = params(Replacer)() + val s1_replaced_way_en = UIntToOH(replacer.way) + val s2_replaced_way_en = UIntToOH(RegEnable(replacer.way, s1_clk_en)) + val s2_repl_meta = Mux1H(s2_replaced_way_en, wayMap((w: Int) => + RegEnable(meta.io.resp(w), s1_clk_en && s1_replaced_way_en(w))).toSeq) + when(!s2_tag_match) { replacer.miss } + + io.resp.valid := Reg(next = s1_clk_en) + io.resp.bits.id := RegEnable(s1_id, s1_clk_en) + io.resp.bits.tag_match := s2_tag_match + io.resp.bits.meta := Mux(s2_tag_match, + L2Metadata(s2_repl_meta.tag, s2_hit_coh), + s2_repl_meta) + io.resp.bits.way_en := Mux(s2_tag_match, s2_tag_match_way, s2_replaced_way_en) +} + +class L2DataReadReq extends L2HellaCacheBundle + with HasL2BeatAddr + with HasL2Id { + val addr_idx = UInt(width = idxBits) + val way_en = Bits(width = nWays) +} + +class L2DataWriteReq extends L2DataReadReq + with HasL2Data { + val wmask = Bits(width = rowBits/8) +} + +class L2DataResp extends L2HellaCacheBundle with HasL2Id with HasL2Data + +trait HasL2DataReadIO extends L2HellaCacheBundle { + val read = Decoupled(new L2DataReadReq) + val resp = Valid(new L2DataResp).flip +} + +trait HasL2DataWriteIO extends L2HellaCacheBundle { + val write = Decoupled(new L2DataWriteReq) +} + +class L2DataRWIO extends L2HellaCacheBundle with HasL2DataReadIO with HasL2DataWriteIO + +class L2DataArray(delay: Int) extends L2HellaCacheModule { + val io = new L2DataRWIO().flip + + val wmask = FillInterleaved(8, io.write.bits.wmask) + val reg_raddr = Reg(UInt()) + val array = Mem(Bits(width=rowBits), nWays*nSets*refillCycles, seqRead = true) + val waddr = Cat(OHToUInt(io.write.bits.way_en), io.write.bits.addr_idx, io.write.bits.addr_beat) + val raddr = Cat(OHToUInt(io.read.bits.way_en), io.read.bits.addr_idx, io.read.bits.addr_beat) + + when (io.write.bits.way_en.orR && io.write.valid) { + array.write(waddr, io.write.bits.data, wmask) + }.elsewhen (io.read.bits.way_en.orR && io.read.valid) { + reg_raddr := raddr + } + + io.resp.valid := ShiftRegister(io.read.fire(), delay+1) + io.resp.bits.id := ShiftRegister(io.read.bits.id, delay+1) + io.resp.bits.addr_beat := ShiftRegister(io.read.bits.addr_beat, delay+1) + io.resp.bits.data := ShiftRegister(array(reg_raddr), delay) + io.read.ready := !io.write.valid + io.write.ready := Bool(true) +} + +class L2HellaCacheBank extends HierarchicalCoherenceAgent with L2HellaCacheParameters { + require(isPow2(nSets)) + require(isPow2(nWays)) + + val meta = Module(new L2MetadataArray) // TODO: add delay knob + val data = Module(new L2DataArray(1)) + val tshrfile = Module(new TSHRFile) + tshrfile.io.inner <> io.inner + io.outer <> tshrfile.io.outer + io.incoherent <> tshrfile.io.incoherent + tshrfile.io.meta <> meta.io + tshrfile.io.data <> data.io +} + +class TSHRFileIO extends HierarchicalTLIO { + val meta = new L2MetaRWIO + val data = new L2DataRWIO +} + +class TSHRFile extends L2HellaCacheModule with HasCoherenceAgentWiringHelpers { + val io = new TSHRFileIO + + // Create TSHRs for outstanding transactions + val trackerList = (0 until nReleaseTransactors).map(id => Module(new L2VoluntaryReleaseTracker(id))) ++ + (nReleaseTransactors until nTransactors).map(id => Module(new L2AcquireTracker(id))) + + // WritebackUnit evicts data from L2, including invalidating L1s + val wb = Module(new L2WritebackUnit(nTransactors)) + doInternalOutputArbitration(wb.io.wb.req, trackerList.map(_.io.wb.req)) + doInternalInputRouting(wb.io.wb.resp, trackerList.map(_.io.wb.resp)) + + // Propagate incoherence flags + (trackerList.map(_.io.incoherent) :+ wb.io.incoherent) foreach { _ := io.incoherent.toBits } + + // Handle acquire transaction initiation + val trackerAcquireIOs = trackerList.map(_.io.inner.acquire) + val acquireConflicts = Vec(trackerList.map(_.io.has_acquire_conflict)).toBits + val acquireMatches = Vec(trackerList.map(_.io.has_acquire_match)).toBits + val acquireReadys = Vec(trackerAcquireIOs.map(_.ready)).toBits + val acquire_idx = Mux(acquireMatches.orR, + PriorityEncoder(acquireMatches), + PriorityEncoder(acquireReadys)) + val block_acquires = acquireConflicts.orR + io.inner.acquire.ready := acquireReadys.orR && !block_acquires + trackerAcquireIOs.zipWithIndex.foreach { + case(tracker, i) => + tracker.bits := io.inner.acquire.bits + tracker.valid := io.inner.acquire.valid && !block_acquires && (acquire_idx === UInt(i)) + } + + // Wire releases from clients + val trackerReleaseIOs = trackerList.map(_.io.inner.release) :+ wb.io.inner.release + val releaseReadys = Vec(trackerReleaseIOs.map(_.ready)).toBits + val releaseMatches = Vec(trackerList.map(_.io.has_release_match) :+ wb.io.has_release_match).toBits + val release_idx = PriorityEncoder(releaseMatches) + io.inner.release.ready := releaseReadys(release_idx) + trackerReleaseIOs.zipWithIndex.foreach { + case(tracker, i) => + tracker.bits := io.inner.release.bits + tracker.valid := io.inner.release.valid && (release_idx === UInt(i)) + } + assert(!(io.inner.release.valid && !releaseMatches.orR), + "Non-voluntary release should always have a Tracker waiting for it.") + + // Wire probe requests and grant reply to clients, finish acks from clients + doOutputArbitration(io.inner.probe, trackerList.map(_.io.inner.probe) :+ wb.io.inner.probe) + doOutputArbitration(io.inner.grant, trackerList.map(_.io.inner.grant)) + doInputRouting(io.inner.finish, trackerList.map(_.io.inner.finish)) + + // Create an arbiter for the one memory port + val outerList = trackerList.map(_.io.outer) :+ wb.io.outer + val outer_arb = Module(new ClientTileLinkIOArbiter(outerList.size))(outerTLParams) + outer_arb.io.in <> outerList + io.outer <> outer_arb.io.out + + // Wire local memory arrays + doInternalOutputArbitration(io.meta.read, trackerList.map(_.io.meta.read)) + doInternalOutputArbitration(io.meta.write, trackerList.map(_.io.meta.write)) + doInternalOutputArbitration(io.data.read, trackerList.map(_.io.data.read) :+ wb.io.data.read) + doInternalOutputArbitration(io.data.write, trackerList.map(_.io.data.write)) + doInternalInputRouting(io.meta.resp, trackerList.map(_.io.meta.resp)) + doInternalInputRouting(io.data.resp, trackerList.map(_.io.data.resp) :+ wb.io.data.resp) +} + + +class L2XactTrackerIO extends HierarchicalXactTrackerIO { + val data = new L2DataRWIO + val meta = new L2MetaRWIO + val wb = new L2WritebackIO +} + +abstract class L2XactTracker extends XactTracker with L2HellaCacheParameters { + class CacheBlockBuffer { // TODO + val buffer = Reg(Bits(width = params(CacheBlockBytes)*8)) + + def internal = Vec.fill(internalDataBeats){ Bits(width = rowBits) }.fromBits(buffer) + def inner = Vec.fill(innerDataBeats){ Bits(width = innerDataBits) }.fromBits(buffer) + def outer = Vec.fill(outerDataBeats){ Bits(width = outerDataBits) }.fromBits(buffer) + } + + def connectDataBeatCounter[S <: L2HellaCacheBundle](inc: Bool, data: S, beat: UInt, full_block: Bool) = { + if(data.refillCycles > 1) { + val (multi_cnt, multi_done) = Counter(full_block && inc, data.refillCycles) + (Mux(!full_block, beat, multi_cnt), Mux(!full_block, inc, multi_done)) + } else { (UInt(0), inc) } + } + + def connectInternalDataBeatCounter[T <: HasL2BeatAddr]( + in: DecoupledIO[T], + beat: UInt = UInt(0), + full_block: Bool = Bool(true)) = { + connectDataBeatCounter(in.fire(), in.bits, beat, full_block) + } + + def connectInternalDataBeatCounter[T <: HasL2Data]( + in: ValidIO[T], + full_block: Bool = Bool(true)) = { + connectDataBeatCounter(in.valid, in.bits, UInt(0), full_block)._2 + } + + def addPendingBitInternal[T <: HasL2BeatAddr](in: DecoupledIO[T]) = + Fill(in.bits.refillCycles, in.fire()) & UIntToOH(in.bits.addr_beat) + + def addPendingBitInternal[T <: HasL2BeatAddr](in: ValidIO[T]) = + Fill(in.bits.refillCycles, in.valid) & UIntToOH(in.bits.addr_beat) + + def dropPendingBit[T <: HasL2BeatAddr] (in: DecoupledIO[T]) = + ~Fill(in.bits.refillCycles, in.fire()) | ~UIntToOH(in.bits.addr_beat) + + def dropPendingBitInternal[T <: HasL2BeatAddr] (in: ValidIO[T]) = + ~Fill(in.bits.refillCycles, in.valid) | ~UIntToOH(in.bits.addr_beat) + + def addPendingBitWhenBeatHasPartialWritemask(in: DecoupledIO[AcquireFromSrc]): UInt = { + val a = in.bits + val isPartial = a.wmask() != Acquire.fullWriteMask + addPendingBitWhenBeat(in.fire() && isPartial && Bool(ignoresWriteMask), a) + } + + def pinAllReadyValidLow[T <: Data](b: Bundle) { + b.elements.foreach { + _._2 match { + case d: DecoupledIO[T] => + if(d.ready.dir == OUTPUT) d.ready := Bool(false) + else if(d.valid.dir == OUTPUT) d.valid := Bool(false) + case v: ValidIO[T] => if(v.valid.dir == OUTPUT) v.valid := Bool(false) + case b: Bundle => pinAllReadyValidLow(b) + case _ => + } + } + } +} + +class L2VoluntaryReleaseTracker(trackerId: Int) extends L2XactTracker { + val io = new L2XactTrackerIO + pinAllReadyValidLow(io) + + val s_idle :: s_meta_read :: s_meta_resp :: s_busy :: s_meta_write :: Nil = Enum(UInt(), 5) + val state = Reg(init=s_idle) + + val xact = Reg(Bundle(new ReleaseFromSrc, { case TLId => params(InnerTLId); case TLDataBits => 0 })) + val data_buffer = Vec.fill(innerDataBeats){ Reg(init=UInt(0, width = innerDataBits)) } + val xact_way_en = Reg{ Bits(width = nWays) } + val xact_old_meta = Reg{ new L2Metadata } + val coh = xact_old_meta.coh + + val pending_irels = Reg(init=Bits(0, width = io.inner.tlDataBeats)) + val pending_writes = Reg(init=Bits(0, width = io.inner.tlDataBeats)) + val pending_ignt = Reg(init=Bool(false)) + + val all_pending_done = + !(pending_writes.orR || + pending_ignt) + + // Accept a voluntary Release (and any further beats of data) + pending_irels := (pending_irels & dropPendingBitWhenBeatHasData(io.inner.release)) + io.inner.release.ready := state === s_idle || pending_irels.orR + when(io.inner.release.fire()) { data_buffer(io.irel().addr_beat) := io.irel().data } + + // Begin a transaction by getting the current block metadata + io.meta.read.valid := state === s_meta_read + io.meta.read.bits.id := UInt(trackerId) + io.meta.read.bits.idx := xact.addr_block(idxMSB,idxLSB) + io.meta.read.bits.tag := xact.addr_block >> UInt(idxBits) + + // Write the voluntarily written back data to this cache + pending_writes := (pending_writes & dropPendingBit(io.data.write)) | + addPendingBitWhenBeatHasData(io.inner.release) + val curr_write_beat = PriorityEncoder(pending_writes) + io.data.write.valid := state === s_busy && pending_writes.orR + io.data.write.bits.id := UInt(trackerId) + io.data.write.bits.way_en := xact_way_en + io.data.write.bits.addr_idx := xact.addr_block(idxMSB,idxLSB) + io.data.write.bits.addr_beat := curr_write_beat + io.data.write.bits.wmask := SInt(-1) + io.data.write.bits.data := data_buffer(curr_write_beat) + + // Send an acknowledgement + io.inner.grant.valid := state === s_busy && pending_ignt && !pending_irels + io.inner.grant.bits := coh.inner.makeGrant(xact, UInt(trackerId)) + when(io.inner.grant.fire()) { pending_ignt := Bool(false) } + + // End a transaction by updating the block metadata + io.meta.write.valid := state === s_meta_write + io.meta.write.bits.id := UInt(trackerId) + io.meta.write.bits.idx := xact.addr_block(idxMSB,idxLSB) + io.meta.write.bits.way_en := xact_way_en + io.meta.write.bits.data.tag := xact.addr_block >> UInt(idxBits) + io.meta.write.bits.data.coh.inner := xact_old_meta.coh.inner.onRelease(xact) + io.meta.write.bits.data.coh.outer := Mux(xact.hasData(), + xact_old_meta.coh.outer.onHit(M_XWR), + xact_old_meta.coh.outer) + + // State machine updates and transaction handler metadata intialization + when(state === s_idle && io.inner.release.valid) { + xact := io.irel() + when(io.irel().hasMultibeatData()) { + pending_irels := dropPendingBitWhenBeatHasData(io.inner.release) + }. otherwise { + pending_irels := UInt(0) + } + pending_writes := addPendingBitWhenBeatHasData(io.inner.release) + pending_ignt := io.irel().requiresAck() + state := s_meta_read + } + when(state === s_meta_read && io.meta.read.ready) { state := s_meta_resp } + when(state === s_meta_resp && io.meta.resp.valid) { + xact_old_meta := io.meta.resp.bits.meta + xact_way_en := io.meta.resp.bits.way_en + state := s_busy + } + when(state === s_busy && all_pending_done) { state := s_meta_write } + when(state === s_meta_write && io.meta.write.ready) { state := s_idle } + + // These IOs are used for routing in the parent + io.has_release_match := io.irel().isVoluntary() + io.has_acquire_match := Bool(false) + io.has_acquire_conflict := Bool(false) + + // Checks for illegal behavior + assert(!(state === s_meta_resp && io.meta.resp.valid && !io.meta.resp.bits.tag_match), + "VoluntaryReleaseTracker accepted Release for a block not resident in this cache!") + assert(!(state === s_idle && io.inner.release.fire() && !io.irel().isVoluntary()), + "VoluntaryReleaseTracker accepted Release that wasn't voluntary!") +} + + +class L2AcquireTracker(trackerId: Int) extends L2XactTracker { + val io = new L2XactTrackerIO + pinAllReadyValidLow(io) + + val s_idle :: s_meta_read :: s_meta_resp :: s_wb_req :: s_wb_resp :: s_inner_probe :: s_outer_acquire :: s_busy :: s_meta_write :: Nil = Enum(UInt(), 9) + val state = Reg(init=s_idle) + + // State holding transaction metadata + val xact = Reg(Bundle(new AcquireFromSrc, { case TLId => params(InnerTLId) })) + val data_buffer = Vec.fill(innerDataBeats){ Reg(init=UInt(0, width = innerDataBits)) } + val wmask_buffer = Vec.fill(innerDataBeats){ Reg(init=UInt(0,width = innerDataBits/8)) } + val xact_tag_match = Reg{ Bool() } + val xact_way_en = Reg{ Bits(width = nWays) } + val xact_old_meta = Reg{ new L2Metadata } + val pending_coh = Reg{ xact_old_meta.coh.clone } + + // Secondary miss queue + val ignt_q = Module(new Queue(new SecondaryMissInfo, nSecondaryMisses))(innerTLParams) + + // State holding progress made on processing this transaction + val iacq_data_done = connectIncomingDataBeatCounter(io.inner.acquire) + val pending_irels = connectTwoWayBeatCounter( + max = io.inner.tlNCachingClients, + up = io.inner.probe, + down = io.inner.release)._1 + val (pending_ognt, oacq_data_idx, oacq_data_done, ognt_data_idx, ognt_data_done) = + connectTwoWayBeatCounter( + max = 1, + up = io.outer.acquire, + down = io.outer.grant, + beat = xact.addr_beat) + val (ignt_data_idx, ignt_data_done) = connectOutgoingDataBeatCounter(io.inner.grant, ignt_q.io.deq.bits.addr_beat) + val pending_ifins = connectTwoWayBeatCounter( + max = nSecondaryMisses, + up = io.inner.grant, + down = io.inner.finish, + track = (g: Grant) => g.requiresAck())._1 + val pending_puts = Reg(init=Bits(0, width = io.inner.tlDataBeats)) + val pending_iprbs = Reg(init = Bits(0, width = io.inner.tlNCachingClients)) + val pending_reads = Reg(init=Bits(0, width = io.inner.tlDataBeats)) + val pending_writes = Reg(init=Bits(0, width = io.inner.tlDataBeats)) + val pending_resps = Reg(init=Bits(0, width = io.inner.tlDataBeats)) + val pending_ignt_data = Reg(init=Bits(0, width = io.inner.tlDataBeats)) + val pending_meta_write = Reg{ Bool() } + + val all_pending_done = + !(pending_reads.orR || + pending_writes.orR || + pending_resps.orR || + pending_puts.orR || + pending_ognt || + ignt_q.io.count > UInt(0) || + //pending_meta_write || // Has own state: s_meta_write + pending_ifins) + + // Provide a single ALU per tracker to merge Puts and AMOs with data being + // refilled, written back, or extant in the cache + val amoalu = Module(new AMOALU) + amoalu.io.addr := xact.addr() + amoalu.io.cmd := xact.op_code() + amoalu.io.typ := xact.op_size() + amoalu.io.lhs := io.data.resp.bits.data // default, overwritten by calls to mergeData + amoalu.io.rhs := data_buffer.head // default, overwritten by calls to mergeData + val amo_result = xact.data // Reuse xact buffer space to store AMO result + + // Utility functions for updating the data and metadata that will be kept in + // the cache or granted to the original requestor after this transaction: + + def updatePendingCohWhen(flag: Bool, next: HierarchicalMetadata) { + when(flag && pending_coh != next) { + pending_meta_write := Bool(true) + pending_coh := next + } + } + + def mergeData(dataBits: Int)(beat: UInt, incoming: UInt) { + val old_data = incoming // Refilled, written back, or de-cached data + val new_data = data_buffer(beat) // Newly Put data is already in the buffer + amoalu.io.lhs := old_data >> xact.amo_shift_bits() + amoalu.io.rhs := new_data >> xact.amo_shift_bits() + val wmask = FillInterleaved(8, wmask_buffer(beat)) + data_buffer(beat) := ~wmask & old_data | + wmask & Mux(xact.isBuiltInType(Acquire.putAtomicType), + amoalu.io.out << xact.amo_shift_bits(), + new_data) + wmask_buffer(beat) := SInt(-1) + when(xact.is(Acquire.putAtomicType) && xact.addr_beat === beat) { amo_result := old_data } + } + def mergeDataInternal[T <: HasL2Data with HasL2BeatAddr](in: ValidIO[T]) { + when(in.valid) { mergeData(rowBits)(in.bits.addr_beat, in.bits.data) } + } + def mergeDataInner[T <: HasTileLinkData with HasTileLinkBeatId](in: DecoupledIO[T]) { + when(in.fire() && in.bits.hasData()) { + mergeData(innerDataBits)(in.bits.addr_beat, in.bits.data) + } + } + def mergeDataOuter[T <: HasTileLinkData with HasTileLinkBeatId](in: DecoupledIO[T]) { + when(in.fire() && in.bits.hasData()) { + mergeData(outerDataBits)(in.bits.addr_beat, in.bits.data) + } + } + + // Actual transaction processing logic begins here: + // + // First, take care of accpeting new requires or secondary misses + // For now, the only allowed secondary miss types are Gets-under-Get + // and Puts-under-Put from the same client + val can_merge_iacq_get = (xact.isBuiltInType(Acquire.getType) && + io.iacq().isBuiltInType(Acquire.getType)) && + xact.client_id === io.iacq().client_id && //TODO remove + xact.conflicts(io.iacq()) && + state != s_idle && state != s_meta_write && + !all_pending_done && + xact.allocate() && + !io.inner.release.fire() && + !io.outer.grant.fire() && + !io.data.resp.valid && + ignt_q.io.enq.ready + + // This logic also allows the tail beats of a PutBlock to be merged in + val can_merge_iacq_put = ((xact.isBuiltInType(Acquire.putType) && + io.iacq().isBuiltInType(Acquire.putType)) || + (xact.isBuiltInType(Acquire.putBlockType) && + io.iacq().isBuiltInType(Acquire.putBlockType))) && + xact.client_id === io.iacq().client_id && //TODO remove + xact.conflicts(io.iacq()) && + state != s_idle && state != s_meta_write && + !all_pending_done && + xact.allocate() && + !io.inner.release.fire() && + !io.outer.grant.fire() && + !io.data.resp.valid && + ignt_q.io.enq.ready + + io.inner.acquire.ready := state === s_idle || + can_merge_iacq_put || + can_merge_iacq_get + + // Enqueue secondary miss information + ignt_q.io.enq.valid := iacq_data_done + ignt_q.io.enq.bits.client_xact_id := io.iacq().client_xact_id + ignt_q.io.enq.bits.addr_beat := io.iacq().addr_beat + // TODO add ignt.dst <- iacq.src + + // Track whether any beats are missing from a PutBlock + pending_puts := (pending_puts & dropPendingBitWhenBeatHasData(io.inner.acquire)) + + // Begin a transaction by getting the current block metadata + io.meta.read.valid := state === s_meta_read + io.meta.read.bits.id := UInt(trackerId) + io.meta.read.bits.idx := xact.addr_block(idxMSB,idxLSB) + io.meta.read.bits.tag := xact.addr_block >> UInt(idxBits) + + // Issue a request to the writeback unit + io.wb.req.valid := state === s_wb_req + io.wb.req.bits.id := UInt(trackerId) + io.wb.req.bits.idx := xact.addr_block(idxMSB,idxLSB) + io.wb.req.bits.tag := xact_old_meta.tag + io.wb.req.bits.coh := xact_old_meta.coh + io.wb.req.bits.way_en := xact_way_en + + // Track which clients yet need to be probed and make Probe message + pending_iprbs := pending_iprbs & dropPendingBitAtDest(io.inner.probe) + val curr_probe_dst = PriorityEncoder(pending_iprbs) + io.inner.probe.valid := state === s_inner_probe && pending_iprbs.orR + io.inner.probe.bits := pending_coh.inner.makeProbe(curr_probe_dst, xact) + + // Handle incoming releases from clients, which may reduce sharer counts + // and/or write back dirty data + io.inner.release.ready := state === s_inner_probe + val pending_coh_on_irel = HierarchicalMetadata( + pending_coh.inner.onRelease(io.irel()), // Drop sharer + Mux(io.irel().hasData(), // Dirty writeback + pending_coh.outer.onHit(M_XWR), + pending_coh.outer)) + updatePendingCohWhen(io.inner.release.fire(), pending_coh_on_irel) + mergeDataInner(io.inner.release) + + // Handle misses or coherence permission upgrades by initiating a new transaction in the outer memory: + // + // If we're allocating in this cache, we can use the current metadata + // to make an appropriate custom Acquire, otherwise we copy over the + // built-in Acquire from the inner TL to the outer TL + io.outer.acquire.valid := state === s_outer_acquire + io.outer.acquire.bits := Mux( + xact.allocate(), + xact_old_meta.coh.outer.makeAcquire( + client_xact_id = UInt(0), + addr_block = xact.addr_block, + op_code = xact.op_code()), + Bundle(Acquire(xact))(outerTLParams)) + + // Handle the response from outer memory + io.outer.grant.ready := state === s_busy + val pending_coh_on_ognt = HierarchicalMetadata( + ManagerMetadata.onReset, + pending_coh.outer.onGrant(io.outer.grant.bits, xact.op_code())) + updatePendingCohWhen(ognt_data_done, pending_coh_on_ognt) + mergeDataOuter(io.outer.grant) + + // Going back to the original inner transaction, we can issue a Grant as + // soon as the data is released, granted, put, or read from the cache + pending_ignt_data := pending_ignt_data | + addPendingBitWhenBeatHasData(io.inner.release) | + addPendingBitWhenBeatHasData(io.outer.grant) | + addPendingBitInternal(io.data.resp) + ignt_q.io.deq.ready := ignt_data_done + io.inner.grant.valid := state === s_busy && + ignt_q.io.deq.valid && + (!io.ignt().hasData() || pending_ignt_data(ignt_data_idx)) + // Make the Grant message using the data stored in the secondary miss queue + io.inner.grant.bits := pending_coh.inner.makeGrant( + pri = xact, + sec = ignt_q.io.deq.bits, + manager_xact_id = UInt(trackerId), + data = Mux(xact.is(Acquire.putAtomicType), + amo_result, + data_buffer(ignt_data_idx))) + io.inner.grant.bits.addr_beat := ignt_data_idx // override based on outgoing counter + + val pending_coh_on_ignt = HierarchicalMetadata( + pending_coh.inner.onGrant(io.ignt()), + Mux(ognt_data_done, + pending_coh_on_ognt.outer, + pending_coh.outer)) + updatePendingCohWhen(io.inner.grant.fire(), pending_coh_on_ignt) + + // We must wait for as many Finishes as we sent Grants + io.inner.finish.ready := state === s_busy + + // We read from the the cache at this level if data wasn't written back or refilled. + // We may merge Gets, requiring further beats to be read. + // If ECC requires a full writemask, we'll read out data on partial writes as well. + pending_reads := (pending_reads & + dropPendingBit(io.data.read) & + dropPendingBitWhenBeatHasData(io.inner.release) & + dropPendingBitWhenBeatHasData(io.outer.grant)) | + addPendingBitWhenBeatIsGetOrAtomic(io.inner.acquire) | + addPendingBitWhenBeatHasPartialWritemask(io.inner.acquire) + val curr_read_beat = PriorityEncoder(pending_reads) + io.data.read.valid := state === s_busy && pending_reads.orR && !pending_ognt + io.data.read.bits.id := UInt(trackerId) + io.data.read.bits.way_en := xact_way_en + io.data.read.bits.addr_idx := xact.addr_block(idxMSB,idxLSB) + io.data.read.bits.addr_beat := curr_read_beat + + pending_resps := (pending_resps & dropPendingBitInternal(io.data.resp)) | + addPendingBitInternal(io.data.read) + mergeDataInternal(io.data.resp) + + // We write data to the cache at this level if it was Put here with allocate flag, + // written back dirty, or refilled from outer memory. + pending_writes := (pending_writes & dropPendingBit(io.data.write)) | + addPendingBitWhenBeatHasData(io.inner.acquire) | + addPendingBitWhenBeatHasData(io.inner.release) | + addPendingBitWhenBeatHasData(io.outer.grant) + val curr_write_beat = PriorityEncoder(pending_writes) + io.data.write.valid := state === s_busy && + pending_writes.orR && + !pending_ognt && + !pending_reads(curr_write_beat) && + !pending_resps(curr_write_beat) + io.data.write.bits.id := UInt(trackerId) + io.data.write.bits.way_en := xact_way_en + io.data.write.bits.addr_idx := xact.addr_block(idxMSB,idxLSB) + io.data.write.bits.addr_beat := curr_write_beat + io.data.write.bits.wmask := wmask_buffer(curr_write_beat) + io.data.write.bits.data := data_buffer(curr_write_beat) + + // End a transaction by updating the block metadata + io.meta.write.valid := state === s_meta_write + io.meta.write.bits.id := UInt(trackerId) + io.meta.write.bits.idx := xact.addr_block(idxMSB,idxLSB) + io.meta.write.bits.way_en := xact_way_en + io.meta.write.bits.data.tag := xact.addr_block >> UInt(idxBits) + io.meta.write.bits.data.coh := pending_coh + + // Handling of secondary misses (Gets and Puts only for now) + when(io.inner.acquire.fire() && io.iacq().hasData()) { // state <= s_meta_wrtie + val beat = io.iacq().addr_beat + val wmask = io.iacq().wmask() + val full = FillInterleaved(8, wmask) + data_buffer(beat) := (~full & data_buffer(beat)) | (full & io.iacq().data) + wmask_buffer(beat) := wmask | Mux(state === s_idle, Bits(0), wmask_buffer(beat)) + } + + // Defined here because of Chisel default wire demands, used in s_meta_resp + val pending_coh_on_hit = HierarchicalMetadata( + io.meta.resp.bits.meta.coh.inner, + io.meta.resp.bits.meta.coh.outer.onHit(xact.op_code())) + + // State machine updates and transaction handler metadata intialization + when(state === s_idle && io.inner.acquire.valid) { + xact := io.iacq() + xact.data := UInt(0) + pending_puts := Mux( // Make sure to collect all data from a PutBlock + io.iacq().isBuiltInType(Acquire.putBlockType), + dropPendingBitWhenBeatHasData(io.inner.acquire), + UInt(0)) + pending_reads := Mux( // GetBlocks and custom types read all beats + io.iacq().isBuiltInType(Acquire.getBlockType) || !io.iacq().isBuiltInType(), + SInt(-1, width = innerDataBeats), + (addPendingBitWhenBeatIsGetOrAtomic(io.inner.acquire) | + addPendingBitWhenBeatHasPartialWritemask(io.inner.acquire)).toUInt) + pending_writes := addPendingBitWhenBeatHasData(io.inner.acquire) + pending_resps := UInt(0) + pending_ignt_data := UInt(0) + pending_meta_write := UInt(0) + state := s_meta_read + } + when(state === s_meta_read && io.meta.read.ready) { state := s_meta_resp } + when(state === s_meta_resp && io.meta.resp.valid) { + xact_tag_match := io.meta.resp.bits.tag_match + xact_old_meta := io.meta.resp.bits.meta + xact_way_en := io.meta.resp.bits.way_en + val coh = io.meta.resp.bits.meta.coh + val tag_match = io.meta.resp.bits.tag_match + val is_hit = (if(!isLastLevelCache) tag_match && coh.outer.isHit(xact.op_code()) + else xact.isBuiltInType(Acquire.putBlockType) || + tag_match && coh.outer.isValid()) + val needs_writeback = !tag_match && + xact.allocate() && + (coh.outer.requiresVoluntaryWriteback() || + coh.inner.requiresProbesOnVoluntaryWriteback()) + val needs_inner_probes = tag_match && coh.inner.requiresProbes(xact) + when(!tag_match || is_hit && pending_coh_on_hit != coh) { pending_meta_write := Bool(true) } + pending_coh := Mux(is_hit, pending_coh_on_hit, coh) + when(needs_inner_probes) { + val full_sharers = coh.inner.full() + val mask_self = Mux( + xact.requiresSelfProbe(), + coh.inner.full() | UIntToOH(xact.client_id), + coh.inner.full() & ~UIntToOH(xact.client_id)) + val mask_incoherent = mask_self & ~io.incoherent.toBits + pending_iprbs := mask_incoherent + } + state := Mux(needs_writeback, s_wb_req, + Mux(needs_inner_probes, s_inner_probe, + Mux(!is_hit, s_outer_acquire, s_busy))) + } + when(state === s_wb_req && io.wb.req.ready) { state := s_wb_resp } + when(state === s_wb_resp && io.wb.resp.valid) { + // If we're overwriting the whole block in a last level cache we can + // just do it without fetching any data from memory + val skip_outer_acquire = Bool(isLastLevelCache) && xact.isBuiltInType(Acquire.putBlockType) + state := Mux(!skip_outer_acquire, s_outer_acquire, s_busy) + } + when(state === s_inner_probe && !(pending_iprbs.orR || pending_irels)) { + // Tag matches, so if this is the last level cache we can use the data without upgrading permissions + val skip_outer_acquire = + (if(!isLastLevelCache) xact_old_meta.coh.outer.isHit(xact.op_code()) + else xact.isBuiltInType(Acquire.putBlockType) || xact_old_meta.coh.outer.isValid()) + state := Mux(!skip_outer_acquire, s_outer_acquire, s_busy) + } + when(state === s_outer_acquire && oacq_data_done) { state := s_busy } + when(state === s_busy && all_pending_done) { state := s_meta_write } + when(state === s_meta_write && (io.meta.write.ready || !pending_meta_write)) { + wmask_buffer.foreach { w => w := UInt(0) } + state := s_idle + } + + // These IOs are used for routing in the parent + val in_same_set = xact.addr_block(idxMSB,idxLSB) === io.iacq().addr_block(idxMSB,idxLSB) + io.has_release_match := xact.conflicts(io.irel()) && !io.irel().isVoluntary() && io.inner.release.ready + io.has_acquire_match := can_merge_iacq_put || can_merge_iacq_get + io.has_acquire_conflict := in_same_set && (state != s_idle) && !io.has_acquire_match + //TODO: relax from in_same_set to xact.conflicts(io.iacq())? + + // Checks for illegal behavior + assert(!(state != s_idle && io.inner.acquire.fire() && + io.inner.acquire.bits.client_id != xact.client_id), + "AcquireTracker accepted data beat from different network source than initial request.") +} + +class L2WritebackReq extends L2Metadata with HasL2Id { + val idx = Bits(width = idxBits) + val way_en = Bits(width = nWays) +} + +class L2WritebackResp extends L2HellaCacheBundle with HasL2Id + +class L2WritebackIO extends L2HellaCacheBundle { + val req = Decoupled(new L2WritebackReq) + val resp = Valid(new L2WritebackResp).flip +} + +class L2WritebackUnitIO extends HierarchicalXactTrackerIO { + val wb = new L2WritebackIO().flip + val data = new L2DataRWIO +} + +class L2WritebackUnit(trackerId: Int) extends L2XactTracker { + val io = new L2WritebackUnitIO + pinAllReadyValidLow(io) + + val s_idle :: s_inner_probe :: s_busy :: s_outer_grant :: s_wb_resp :: Nil = Enum(UInt(), 5) + val state = Reg(init=s_idle) + + val xact = Reg(new L2WritebackReq) + val data_buffer = Vec.fill(innerDataBeats){ Reg(init=UInt(0, width = innerDataBits)) } + val xact_addr_block = Cat(xact.tag, xact.idx) + + val pending_irels = + connectTwoWayBeatCounter(max = io.inner.tlNCachingClients, up = io.inner.probe, down = io.inner.release)._1 + val (pending_ognt, orel_data_idx, orel_data_done, ognt_data_idx, ognt_data_done) = + connectTwoWayBeatCounter(max = 1, up = io.outer.release, down = io.outer.grant) + val pending_iprbs = Reg(init = Bits(0, width = io.inner.tlNCachingClients)) + val pending_reads = Reg(init=Bits(0, width = io.inner.tlDataBeats)) + val pending_resps = Reg(init=Bits(0, width = io.inner.tlDataBeats)) + val pending_orel_data = Reg(init=Bits(0, width = io.inner.tlDataBeats)) + + // Start the writeback sub-transaction + io.wb.req.ready := state === s_idle + + // Track which clients yet need to be probed and make Probe message + pending_iprbs := pending_iprbs & dropPendingBitAtDest(io.inner.probe) + val curr_probe_dst = PriorityEncoder(pending_iprbs) + io.inner.probe.valid := state === s_inner_probe && pending_iprbs.orR + io.inner.probe.bits := xact.coh.inner.makeProbeForVoluntaryWriteback(curr_probe_dst, xact_addr_block) + + // Handle incoming releases from clients, which may reduce sharer counts + // and/or write back dirty data + val inner_coh_on_irel = xact.coh.inner.onRelease(io.irel()) + val outer_coh_on_irel = xact.coh.outer.onHit(M_XWR) + io.inner.release.ready := state === s_inner_probe || state === s_busy + when(io.inner.release.fire()) { + xact.coh.inner := inner_coh_on_irel + when(io.irel().hasData()) { xact.coh.outer := outer_coh_on_irel } // WB is a write + data_buffer(io.inner.release.bits.addr_beat) := io.inner.release.bits.data + } + + // If a release didn't write back data, have to read it from data array + pending_reads := (pending_reads & + dropPendingBit(io.data.read) & + dropPendingBitWhenBeatHasData(io.inner.release)) + val curr_read_beat = PriorityEncoder(pending_reads) + io.data.read.valid := state === s_busy && pending_reads.orR + io.data.read.bits.id := UInt(trackerId) + io.data.read.bits.way_en := xact.way_en + io.data.read.bits.addr_idx := xact.idx + io.data.read.bits.addr_beat := curr_read_beat + io.data.write.valid := Bool(false) + + pending_resps := (pending_resps & dropPendingBitInternal(io.data.resp)) | + addPendingBitInternal(io.data.read) + when(io.data.resp.valid) { + data_buffer(io.data.resp.bits.addr_beat) := io.data.resp.bits.data + } + + // Once the data is buffered we can write it back to outer memory + pending_orel_data := pending_orel_data | + addPendingBitWhenBeatHasData(io.inner.release) | + addPendingBitInternal(io.data.resp) + io.outer.release.valid := state === s_busy && + (!io.orel().hasData() || pending_orel_data(orel_data_idx)) + io.outer.release.bits := xact.coh.outer.makeVoluntaryWriteback( + client_xact_id = UInt(trackerId), + addr_block = xact_addr_block, + addr_beat = orel_data_idx, + data = data_buffer(orel_data_idx)) + + // Wait for an acknowledgement + io.outer.grant.ready := state === s_outer_grant + + // Respond to the initiating transaction handler signalling completion of the writeback + io.wb.resp.valid := state === s_wb_resp + io.wb.resp.bits.id := xact.id + + // State machine updates and transaction handler metadata intialization + when(state === s_idle && io.wb.req.valid) { + xact := io.wb.req.bits + val coh = io.wb.req.bits.coh + val needs_inner_probes = coh.inner.requiresProbesOnVoluntaryWriteback() + when(needs_inner_probes) { pending_iprbs := coh.inner.full() & ~io.incoherent.toBits } + pending_reads := SInt(-1, width = innerDataBeats) + pending_resps := UInt(0) + pending_orel_data := UInt(0) + state := Mux(needs_inner_probes, s_inner_probe, s_busy) + } + when(state === s_inner_probe && !(pending_iprbs.orR || pending_irels)) { + state := Mux(xact.coh.outer.requiresVoluntaryWriteback(), s_busy, s_wb_resp) + } + when(state === s_busy && orel_data_done) { + state := Mux(io.orel().requiresAck(), s_outer_grant, s_wb_resp) + } + when(state === s_outer_grant && ognt_data_done) { state := s_wb_resp } + when(state === s_wb_resp ) { state := s_idle } + + // These IOs are used for routing in the parent + io.has_release_match := io.irel().conflicts(xact_addr_block) && !io.irel().isVoluntary() && io.inner.release.ready + io.has_acquire_match := Bool(false) + io.has_acquire_conflict := Bool(false) +} diff --git a/uncore/src/main/scala/coherence.scala b/uncore/src/main/scala/coherence.scala new file mode 100644 index 00000000..862eb484 --- /dev/null +++ b/uncore/src/main/scala/coherence.scala @@ -0,0 +1,688 @@ +// See LICENSE for license details. + +package uncore +import Chisel._ + +/** The entire CoherencePolicy API consists of the following three traits: + * HasCustomTileLinkMessageTypes, used to define custom messages + * HasClientSideCoherencePolicy, for client coherence agents + * HasManagerSideCoherencePolicy, for manager coherence agents + */ +abstract class CoherencePolicy(val dir: DirectoryRepresentation) extends + HasCustomTileLinkMessageTypes with + HasClientSideCoherencePolicy with + HasManagerSideCoherencePolicy + +/** This API defines the custom, coherence-policy-defined message types, + * as opposed to the built-in ones found in tilelink.scala. + * Policies must enumerate the custom messages to be sent over each + * channel, as well as which of them have associated data. + */ +trait HasCustomTileLinkMessageTypes { + val nAcquireTypes: Int + def acquireTypeWidth = log2Up(nAcquireTypes) + val nProbeTypes: Int + def probeTypeWidth = log2Up(nProbeTypes) + val nReleaseTypes: Int + def releaseTypeWidth = log2Up(nReleaseTypes) + val nGrantTypes: Int + def grantTypeWidth = log2Up(nGrantTypes) + + val acquireTypesWithData = Nil // Only built-in Acquire types have data for now + val releaseTypesWithData: Vec[UInt] + val grantTypesWithData: Vec[UInt] +} + +/** This API contains all functions required for client coherence agents. + * Policies must enumerate the number of client states and define their + * permissions with respect to memory operations. Policies must fill in functions + * to control which messages are sent and how metadata is updated in response + * to coherence events. These funtions are generally called from within the + * ClientMetadata class in metadata.scala + */ +trait HasClientSideCoherencePolicy { + // Client coherence states and their permissions + val nClientStates: Int + def clientStateWidth = log2Ceil(nClientStates) + val clientStatesWithReadPermission: Vec[UInt] + val clientStatesWithWritePermission: Vec[UInt] + val clientStatesWithDirtyData: Vec[UInt] + + // Transaction initiation logic + def isValid(meta: ClientMetadata): Bool + def isHit(cmd: UInt, meta: ClientMetadata): Bool = { + Mux(isWriteIntent(cmd), + clientStatesWithWritePermission.contains(meta.state), + clientStatesWithReadPermission.contains(meta.state)) + } + //TODO: Assumes all states with write permissions also have read permissions + def requiresAcquireOnSecondaryMiss( + first_cmd: UInt, + second_cmd: UInt, + meta: ClientMetadata): Bool = { + isWriteIntent(second_cmd) && !isWriteIntent(first_cmd) + } + //TODO: Assumes all cache ctrl ops writeback dirty data, and + // doesn't issue transaction when e.g. downgrading Exclusive to Shared: + def requiresReleaseOnCacheControl(cmd: UInt, meta: ClientMetadata): Bool = + clientStatesWithDirtyData.contains(meta.state) + + // Determine which custom message type to use + def getAcquireType(cmd: UInt, meta: ClientMetadata): UInt + def getReleaseType(cmd: UInt, meta: ClientMetadata): UInt + def getReleaseType(p: Probe, meta: ClientMetadata): UInt + + // Mutate ClientMetadata based on messages or cmds + def clientMetadataOnReset: ClientMetadata + def clientMetadataOnHit(cmd: UInt, meta: ClientMetadata): ClientMetadata + def clientMetadataOnCacheControl(cmd: UInt, meta: ClientMetadata): ClientMetadata + def clientMetadataOnGrant(incoming: Grant, cmd: UInt, meta: ClientMetadata): ClientMetadata + def clientMetadataOnProbe(incoming: Probe, meta: ClientMetadata): ClientMetadata +} + +/** This API contains all functions required for manager coherence agents. + * Policies must enumerate the number of manager states. Policies must fill + * in functions to control which Probe and Grant messages are sent and how + * metadata should be updated in response to coherence events. These funtions + * are generally called from within the ManagerMetadata class in metadata.scala + */ +trait HasManagerSideCoherencePolicy extends HasDirectoryRepresentation { + val nManagerStates: Int + def masterStateWidth = log2Ceil(nManagerStates) + + // Transaction probing logic + def requiresProbes(acq: Acquire, meta: ManagerMetadata): Bool + def requiresProbes(cmd: UInt, meta: ManagerMetadata): Bool + + // Determine which custom message type to use in response + def getProbeType(cmd: UInt, meta: ManagerMetadata): UInt + def getProbeType(acq: Acquire, meta: ManagerMetadata): UInt + def getGrantType(acq: Acquire, meta: ManagerMetadata): UInt + + // Mutate ManagerMetadata based on messages or cmds + def managerMetadataOnReset: ManagerMetadata + def managerMetadataOnRelease(incoming: Release, src: UInt, meta: ManagerMetadata): ManagerMetadata + def managerMetadataOnGrant(outgoing: Grant, dst: UInt, meta: ManagerMetadata) = + ManagerMetadata(sharers=Mux(outgoing.isBuiltInType(), // Assumes all built-ins are uncached + meta.sharers, + dir.push(meta.sharers, dst))) + //state = meta.state) TODO: Fix 0-width wires in Chisel +} + +/** The following concrete implementations of CoherencePolicy each provide the + * functionality of one particular protocol. + */ + +/** A simple protocol with only two Client states. + * Data is always assumed to be dirty. + * Only a single client may ever have a copy of a block at a time. + */ +class MICoherence(dir: DirectoryRepresentation) extends CoherencePolicy(dir) { + // Message types + val nAcquireTypes = 1 + val nProbeTypes = 2 + val nReleaseTypes = 4 + val nGrantTypes = 1 + + val acquireExclusive :: Nil = Enum(UInt(), nAcquireTypes) + val probeInvalidate :: probeCopy :: Nil = Enum(UInt(), nProbeTypes) + val releaseInvalidateData :: releaseCopyData :: releaseInvalidateAck :: releaseCopyAck :: Nil = Enum(UInt(), nReleaseTypes) + val grantExclusive :: Nil = Enum(UInt(), nGrantTypes) + + val releaseTypesWithData = Vec(releaseInvalidateData, releaseCopyData) + val grantTypesWithData = Vec(grantExclusive) + + // Client states and functions + val nClientStates = 2 + val clientInvalid :: clientValid :: Nil = Enum(UInt(), nClientStates) + + val clientStatesWithReadPermission = Vec(clientValid) + val clientStatesWithWritePermission = Vec(clientValid) + val clientStatesWithDirtyData = Vec(clientValid) + + def isValid (meta: ClientMetadata): Bool = meta.state != clientInvalid + + def getAcquireType(cmd: UInt, meta: ClientMetadata): UInt = acquireExclusive + + def getReleaseType(cmd: UInt, meta: ClientMetadata): UInt = { + val dirty = clientStatesWithDirtyData.contains(meta.state) + MuxLookup(cmd, releaseCopyAck, Array( + M_FLUSH -> Mux(dirty, releaseInvalidateData, releaseInvalidateAck), + M_PRODUCE -> Mux(dirty, releaseCopyData, releaseCopyAck), + M_CLEAN -> Mux(dirty, releaseCopyData, releaseCopyAck))) + } + + def getReleaseType(incoming: Probe, meta: ClientMetadata): UInt = + MuxLookup(incoming.p_type, releaseInvalidateAck, Array( + probeInvalidate -> getReleaseType(M_FLUSH, meta), + probeCopy -> getReleaseType(M_CLEAN, meta))) + + def clientMetadataOnReset = ClientMetadata(clientInvalid) + + def clientMetadataOnHit(cmd: UInt, meta: ClientMetadata) = meta + + def clientMetadataOnCacheControl(cmd: UInt, meta: ClientMetadata) = + ClientMetadata(Mux(cmd === M_FLUSH, clientInvalid, meta.state)) + + def clientMetadataOnGrant(incoming: Grant, cmd: UInt, meta: ClientMetadata) = + ClientMetadata(Mux(incoming.isBuiltInType(), clientInvalid, clientValid)) + + def clientMetadataOnProbe(incoming: Probe, meta: ClientMetadata) = + ClientMetadata(Mux(incoming.p_type === probeInvalidate, + clientInvalid, meta.state)) + + // Manager states and functions: + val nManagerStates = 0 // We don't actually need any states for this protocol + + def requiresProbes(a: Acquire, meta: ManagerMetadata) = !dir.none(meta.sharers) + + def requiresProbes(cmd: UInt, meta: ManagerMetadata) = !dir.none(meta.sharers) + + def getProbeType(cmd: UInt, meta: ManagerMetadata): UInt = + MuxLookup(cmd, probeCopy, Array( + M_FLUSH -> probeInvalidate)) + + def getProbeType(a: Acquire, meta: ManagerMetadata): UInt = + Mux(a.isBuiltInType(), + MuxLookup(a.a_type, probeCopy, Array( + Acquire.getBlockType -> probeCopy, + Acquire.putBlockType -> probeInvalidate, + Acquire.getType -> probeCopy, + Acquire.putType -> probeInvalidate, + Acquire.putAtomicType -> probeInvalidate)), + probeInvalidate) + + def getGrantType(a: Acquire, meta: ManagerMetadata): UInt = grantExclusive + + def managerMetadataOnReset = ManagerMetadata() + + def managerMetadataOnRelease(incoming: Release, src: UInt, meta: ManagerMetadata) = { + val popped = ManagerMetadata(sharers=dir.pop(meta.sharers, src)) + MuxBundle(meta, Array( + incoming.is(releaseInvalidateData) -> popped, + incoming.is(releaseInvalidateAck) -> popped)) + } +} + +/** A simple protocol with only three Client states. + * Data is marked as dirty when written. + * Only a single client may ever have a copy of a block at a time. + */ +class MEICoherence(dir: DirectoryRepresentation) extends CoherencePolicy(dir) { + // Message types + val nAcquireTypes = 2 + val nProbeTypes = 3 + val nReleaseTypes = 6 + val nGrantTypes = 1 + + val acquireExclusiveClean :: acquireExclusiveDirty :: Nil = Enum(UInt(), nAcquireTypes) + val probeInvalidate :: probeDowngrade :: probeCopy :: Nil = Enum(UInt(), nProbeTypes) + val releaseInvalidateData :: releaseDowngradeData :: releaseCopyData :: releaseInvalidateAck :: releaseDowngradeAck :: releaseCopyAck :: Nil = Enum(UInt(), nReleaseTypes) + val grantExclusive :: Nil = Enum(UInt(), nGrantTypes) + + val releaseTypesWithData = Vec(releaseInvalidateData, releaseDowngradeData, releaseCopyData) + val grantTypesWithData = Vec(grantExclusive) + + // Client states and functions + val nClientStates = 3 + val clientInvalid :: clientExclusiveClean :: clientExclusiveDirty :: Nil = Enum(UInt(), nClientStates) + + val clientStatesWithReadPermission = Vec(clientExclusiveClean, clientExclusiveDirty) + val clientStatesWithWritePermission = Vec(clientExclusiveClean, clientExclusiveDirty) + val clientStatesWithDirtyData = Vec(clientExclusiveDirty) + + def isValid (meta: ClientMetadata) = meta.state != clientInvalid + + def getAcquireType(cmd: UInt, meta: ClientMetadata): UInt = + Mux(isWriteIntent(cmd), acquireExclusiveDirty, acquireExclusiveClean) + + def getReleaseType(cmd: UInt, meta: ClientMetadata): UInt = { + val dirty = clientStatesWithDirtyData.contains(meta.state) + MuxLookup(cmd, releaseCopyAck, Array( + M_FLUSH -> Mux(dirty, releaseInvalidateData, releaseInvalidateAck), + M_PRODUCE -> Mux(dirty, releaseDowngradeData, releaseDowngradeAck), + M_CLEAN -> Mux(dirty, releaseCopyData, releaseCopyAck))) + } + + def getReleaseType(incoming: Probe, meta: ClientMetadata): UInt = + MuxLookup(incoming.p_type, releaseInvalidateAck, Array( + probeInvalidate -> getReleaseType(M_FLUSH, meta), + probeDowngrade -> getReleaseType(M_PRODUCE, meta), + probeCopy -> getReleaseType(M_CLEAN, meta))) + + def clientMetadataOnReset = ClientMetadata(clientInvalid) + + def clientMetadataOnHit(cmd: UInt, meta: ClientMetadata) = + ClientMetadata(Mux(isWrite(cmd), clientExclusiveDirty, meta.state)) + + def clientMetadataOnCacheControl(cmd: UInt, meta: ClientMetadata) = + ClientMetadata( + MuxLookup(cmd, meta.state, Array( + M_FLUSH -> clientInvalid, + M_CLEAN -> Mux(meta.state === clientExclusiveDirty, clientExclusiveClean, meta.state)))) + + def clientMetadataOnGrant(incoming: Grant, cmd: UInt, meta: ClientMetadata) = + ClientMetadata( + Mux(incoming.isBuiltInType(), clientInvalid, + Mux(isWrite(cmd), clientExclusiveDirty, clientExclusiveClean))) + + def clientMetadataOnProbe(incoming: Probe, meta: ClientMetadata) = + ClientMetadata( + MuxLookup(incoming.p_type, meta.state, Array( + probeInvalidate -> clientInvalid, + probeDowngrade -> clientExclusiveClean, + probeCopy -> meta.state))) + + // Manager states and functions: + val nManagerStates = 0 // We don't actually need any states for this protocol + + def requiresProbes(a: Acquire, meta: ManagerMetadata) = !dir.none(meta.sharers) + def requiresProbes(cmd: UInt, meta: ManagerMetadata) = !dir.none(meta.sharers) + + def getProbeType(cmd: UInt, meta: ManagerMetadata): UInt = + MuxLookup(cmd, probeCopy, Array( + M_FLUSH -> probeInvalidate, + M_PRODUCE -> probeDowngrade)) + + def getProbeType(a: Acquire, meta: ManagerMetadata): UInt = + Mux(a.isBuiltInType(), + MuxLookup(a.a_type, probeCopy, Array( + Acquire.getBlockType -> probeCopy, + Acquire.putBlockType -> probeInvalidate, + Acquire.getType -> probeCopy, + Acquire.putType -> probeInvalidate, + Acquire.putAtomicType -> probeInvalidate)), + probeInvalidate) + + def getGrantType(a: Acquire, meta: ManagerMetadata): UInt = grantExclusive + + def managerMetadataOnReset = ManagerMetadata() + + def managerMetadataOnRelease(incoming: Release, src: UInt, meta: ManagerMetadata) = { + val popped = ManagerMetadata(sharers=dir.pop(meta.sharers, src)) + MuxBundle(meta, Array( + incoming.is(releaseInvalidateData) -> popped, + incoming.is(releaseInvalidateAck) -> popped)) + } +} + +/** A protocol with only three Client states. + * Data is always assumed to be dirty. + * Multiple clients may share read permissions on a block at the same time. + */ +class MSICoherence(dir: DirectoryRepresentation) extends CoherencePolicy(dir) { + // Message types + val nAcquireTypes = 2 + val nProbeTypes = 3 + val nReleaseTypes = 6 + val nGrantTypes = 3 + + val acquireShared :: acquireExclusive :: Nil = Enum(UInt(), nAcquireTypes) + val probeInvalidate :: probeDowngrade :: probeCopy :: Nil = Enum(UInt(), nProbeTypes) + val releaseInvalidateData :: releaseDowngradeData :: releaseCopyData :: releaseInvalidateAck :: releaseDowngradeAck :: releaseCopyAck :: Nil = Enum(UInt(), nReleaseTypes) + val grantShared :: grantExclusive :: grantExclusiveAck :: Nil = Enum(UInt(), nGrantTypes) + + val releaseTypesWithData = Vec(releaseInvalidateData, releaseDowngradeData, releaseCopyData) + val grantTypesWithData = Vec(grantShared, grantExclusive) + + // Client states and functions + val nClientStates = 3 + val clientInvalid :: clientShared :: clientExclusiveDirty :: Nil = Enum(UInt(), nClientStates) + + val clientStatesWithReadPermission = Vec(clientShared, clientExclusiveDirty) + val clientStatesWithWritePermission = Vec(clientExclusiveDirty) + val clientStatesWithDirtyData = Vec(clientExclusiveDirty) + + def isValid(meta: ClientMetadata): Bool = meta.state != clientInvalid + + def getAcquireType(cmd: UInt, meta: ClientMetadata): UInt = + Mux(isWriteIntent(cmd), acquireExclusive, acquireShared) + + def getReleaseType(cmd: UInt, meta: ClientMetadata): UInt = { + val dirty = clientStatesWithDirtyData.contains(meta.state) + MuxLookup(cmd, releaseCopyAck, Array( + M_FLUSH -> Mux(dirty, releaseInvalidateData, releaseInvalidateAck), + M_PRODUCE -> Mux(dirty, releaseDowngradeData, releaseDowngradeAck), + M_CLEAN -> Mux(dirty, releaseCopyData, releaseCopyAck))) + } + + def getReleaseType(incoming: Probe, meta: ClientMetadata): UInt = + MuxLookup(incoming.p_type, releaseInvalidateAck, Array( + probeInvalidate -> getReleaseType(M_FLUSH, meta), + probeDowngrade -> getReleaseType(M_PRODUCE, meta), + probeCopy -> getReleaseType(M_CLEAN, meta))) + + def clientMetadataOnReset = ClientMetadata(clientInvalid) + + def clientMetadataOnHit(cmd: UInt, meta: ClientMetadata) = + ClientMetadata(Mux(isWrite(cmd), clientExclusiveDirty, meta.state)) + + def clientMetadataOnCacheControl(cmd: UInt, meta: ClientMetadata) = + ClientMetadata( + MuxLookup(cmd, meta.state, Array( + M_FLUSH -> clientInvalid, + M_PRODUCE -> Mux(clientStatesWithWritePermission.contains(meta.state), + clientShared, meta.state)))) + + def clientMetadataOnGrant(incoming: Grant, cmd: UInt, meta: ClientMetadata) = + ClientMetadata( + Mux(incoming.isBuiltInType(), clientInvalid, + MuxLookup(incoming.g_type, clientInvalid, Array( + grantShared -> clientShared, + grantExclusive -> clientExclusiveDirty, + grantExclusiveAck -> clientExclusiveDirty)))) + + def clientMetadataOnProbe(incoming: Probe, meta: ClientMetadata) = + ClientMetadata( + MuxLookup(incoming.p_type, meta.state, Array( + probeInvalidate -> clientInvalid, + probeDowngrade -> clientShared, + probeCopy -> meta.state))) + + // Manager states and functions: + val nManagerStates = 0 // TODO: We could add a Shared state to avoid probing + // only a single sharer (also would need + // notification msg to track clean drops) + // Also could avoid probes on outer WBs. + + def requiresProbes(a: Acquire, meta: ManagerMetadata) = + Mux(dir.none(meta.sharers), Bool(false), + Mux(dir.one(meta.sharers), Bool(true), //TODO: for now we assume it's Exclusive + Mux(a.isBuiltInType(), a.hasData(), a.a_type != acquireShared))) + + def requiresProbes(cmd: UInt, meta: ManagerMetadata) = !dir.none(meta.sharers) + + def getProbeType(cmd: UInt, meta: ManagerMetadata): UInt = + MuxLookup(cmd, probeCopy, Array( + M_FLUSH -> probeInvalidate, + M_PRODUCE -> probeDowngrade)) + + def getProbeType(a: Acquire, meta: ManagerMetadata): UInt = + Mux(a.isBuiltInType(), + MuxLookup(a.a_type, probeCopy, Array( + Acquire.getBlockType -> probeCopy, + Acquire.putBlockType -> probeInvalidate, + Acquire.getType -> probeCopy, + Acquire.putType -> probeInvalidate, + Acquire.putAtomicType -> probeInvalidate)), + MuxLookup(a.a_type, probeCopy, Array( + acquireShared -> probeDowngrade, + acquireExclusive -> probeInvalidate))) + + def getGrantType(a: Acquire, meta: ManagerMetadata): UInt = + Mux(a.a_type === acquireShared, + Mux(!dir.none(meta.sharers), grantShared, grantExclusive), + grantExclusive) + + def managerMetadataOnReset = ManagerMetadata() + + def managerMetadataOnRelease(incoming: Release, src: UInt, meta: ManagerMetadata) = { + val popped = ManagerMetadata(sharers=dir.pop(meta.sharers, src)) + MuxBundle(meta, Array( + incoming.is(releaseInvalidateData) -> popped, + incoming.is(releaseInvalidateAck) -> popped)) + } +} + +/** A protocol with four Client states. + * Data is marked as dirty when written. + * Multiple clients may share read permissions on a block at the same time. + */ +class MESICoherence(dir: DirectoryRepresentation) extends CoherencePolicy(dir) { + // Message types + val nAcquireTypes = 2 + val nProbeTypes = 3 + val nReleaseTypes = 6 + val nGrantTypes = 3 + + val acquireShared :: acquireExclusive :: Nil = Enum(UInt(), nAcquireTypes) + val probeInvalidate :: probeDowngrade :: probeCopy :: Nil = Enum(UInt(), nProbeTypes) + val releaseInvalidateData :: releaseDowngradeData :: releaseCopyData :: releaseInvalidateAck :: releaseDowngradeAck :: releaseCopyAck :: Nil = Enum(UInt(), nReleaseTypes) + val grantShared :: grantExclusive :: grantExclusiveAck :: Nil = Enum(UInt(), nGrantTypes) + + val releaseTypesWithData = Vec(releaseInvalidateData, releaseDowngradeData, releaseCopyData) + val grantTypesWithData = Vec(grantShared, grantExclusive) + + // Client states and functions + val nClientStates = 4 + val clientInvalid :: clientShared :: clientExclusiveClean :: clientExclusiveDirty :: Nil = Enum(UInt(), nClientStates) + + val clientStatesWithReadPermission = Vec(clientShared, clientExclusiveClean, clientExclusiveDirty) + val clientStatesWithWritePermission = Vec(clientExclusiveClean, clientExclusiveDirty) + val clientStatesWithDirtyData = Vec(clientExclusiveDirty) + + def isValid (meta: ClientMetadata): Bool = meta.state != clientInvalid + + def getAcquireType(cmd: UInt, meta: ClientMetadata): UInt = + Mux(isWriteIntent(cmd), acquireExclusive, acquireShared) + + def getReleaseType(cmd: UInt, meta: ClientMetadata): UInt = { + val dirty = clientStatesWithDirtyData.contains(meta.state) + MuxLookup(cmd, releaseCopyAck, Array( + M_FLUSH -> Mux(dirty, releaseInvalidateData, releaseInvalidateAck), + M_PRODUCE -> Mux(dirty, releaseDowngradeData, releaseDowngradeAck), + M_CLEAN -> Mux(dirty, releaseCopyData, releaseCopyAck))) + } + + def getReleaseType(incoming: Probe, meta: ClientMetadata): UInt = + MuxLookup(incoming.p_type, releaseInvalidateAck, Array( + probeInvalidate -> getReleaseType(M_FLUSH, meta), + probeDowngrade -> getReleaseType(M_PRODUCE, meta), + probeCopy -> getReleaseType(M_CLEAN, meta))) + + def clientMetadataOnReset = ClientMetadata(clientInvalid) + + def clientMetadataOnHit(cmd: UInt, meta: ClientMetadata) = + ClientMetadata(Mux(isWrite(cmd), clientExclusiveDirty, meta.state)) + + def clientMetadataOnCacheControl(cmd: UInt, meta: ClientMetadata) = + ClientMetadata( + MuxLookup(cmd, meta.state, Array( + M_FLUSH -> clientInvalid, + M_PRODUCE -> Mux(clientStatesWithWritePermission.contains(meta.state), + clientShared, meta.state), + M_CLEAN -> Mux(meta.state === clientExclusiveDirty, clientExclusiveClean, meta.state)))) + + def clientMetadataOnGrant(incoming: Grant, cmd: UInt, meta: ClientMetadata) = + ClientMetadata( + Mux(incoming.isBuiltInType(), clientInvalid, + MuxLookup(incoming.g_type, clientInvalid, Array( + grantShared -> clientShared, + grantExclusive -> Mux(isWrite(cmd), clientExclusiveDirty, clientExclusiveClean), + grantExclusiveAck -> clientExclusiveDirty)))) + + def clientMetadataOnProbe(incoming: Probe, meta: ClientMetadata) = + ClientMetadata( + MuxLookup(incoming.p_type, meta.state, Array( + probeInvalidate -> clientInvalid, + probeDowngrade -> clientShared, + probeCopy -> meta.state))) + + // Manager states and functions: + val nManagerStates = 0 // TODO: We could add a Shared state to avoid probing + // only a single sharer (also would need + // notification msg to track clean drops) + // Also could avoid probes on outer WBs. + + def requiresProbes(a: Acquire, meta: ManagerMetadata) = + Mux(dir.none(meta.sharers), Bool(false), + Mux(dir.one(meta.sharers), Bool(true), //TODO: for now we assume it's Exclusive + Mux(a.isBuiltInType(), a.hasData(), a.a_type != acquireShared))) + + def requiresProbes(cmd: UInt, meta: ManagerMetadata) = !dir.none(meta.sharers) + + def getProbeType(cmd: UInt, meta: ManagerMetadata): UInt = + MuxLookup(cmd, probeCopy, Array( + M_FLUSH -> probeInvalidate, + M_PRODUCE -> probeDowngrade)) + + def getProbeType(a: Acquire, meta: ManagerMetadata): UInt = + Mux(a.isBuiltInType(), + MuxLookup(a.a_type, probeCopy, Array( + Acquire.getBlockType -> probeCopy, + Acquire.putBlockType -> probeInvalidate, + Acquire.getType -> probeCopy, + Acquire.putType -> probeInvalidate, + Acquire.putAtomicType -> probeInvalidate)), + MuxLookup(a.a_type, probeCopy, Array( + acquireShared -> probeDowngrade, + acquireExclusive -> probeInvalidate))) + + def getGrantType(a: Acquire, meta: ManagerMetadata): UInt = + Mux(a.a_type === acquireShared, + Mux(!dir.none(meta.sharers), grantShared, grantExclusive), + grantExclusive) + + def managerMetadataOnReset = ManagerMetadata() + + def managerMetadataOnRelease(incoming: Release, src: UInt, meta: ManagerMetadata) = { + val popped = ManagerMetadata(sharers=dir.pop(meta.sharers, src)) + MuxBundle(meta, Array( + incoming.is(releaseInvalidateData) -> popped, + incoming.is(releaseInvalidateAck) -> popped)) + } +} + +class MigratoryCoherence(dir: DirectoryRepresentation) extends CoherencePolicy(dir) { + // Message types + val nAcquireTypes = 3 + val nProbeTypes = 4 + val nReleaseTypes = 10 + val nGrantTypes = 4 + + val acquireShared :: acquireExclusive :: acquireInvalidateOthers :: Nil = Enum(UInt(), nAcquireTypes) + val probeInvalidate :: probeDowngrade :: probeCopy :: probeInvalidateOthers :: Nil = Enum(UInt(), nProbeTypes) + val releaseInvalidateData :: releaseDowngradeData :: releaseCopyData :: releaseInvalidateAck :: releaseDowngradeAck :: releaseCopyAck :: releaseDowngradeDataMigratory :: releaseDowngradeAckHasCopy :: releaseInvalidateDataMigratory :: releaseInvalidateAckMigratory :: Nil = Enum(UInt(), nReleaseTypes) + val grantShared :: grantExclusive :: grantExclusiveAck :: grantReadMigratory :: Nil = Enum(UInt(), nGrantTypes) + + val releaseTypesWithData = Vec(releaseInvalidateData, releaseDowngradeData, releaseCopyData, releaseInvalidateDataMigratory, releaseDowngradeDataMigratory) + val grantTypesWithData = Vec(grantShared, grantExclusive, grantReadMigratory) + + // Client states and functions + val nClientStates = 7 + val clientInvalid :: clientShared :: clientExclusiveClean :: clientExclusiveDirty :: clientSharedByTwo :: clientMigratoryClean :: clientMigratoryDirty :: Nil = Enum(UInt(), nClientStates) + + val clientStatesWithReadPermission = Vec(clientShared, clientExclusiveClean, clientExclusiveDirty, clientSharedByTwo, clientMigratoryClean, clientMigratoryDirty) + val clientStatesWithWritePermission = Vec(clientExclusiveClean, clientExclusiveDirty, clientMigratoryClean, clientMigratoryDirty) + val clientStatesWithDirtyData = Vec(clientExclusiveDirty, clientMigratoryDirty) + + def isValid (meta: ClientMetadata): Bool = meta.state != clientInvalid + + def getAcquireType(cmd: UInt, meta: ClientMetadata): UInt = + Mux(isWriteIntent(cmd), + Mux(meta.state === clientInvalid, acquireExclusive, acquireInvalidateOthers), + acquireShared) + + def getReleaseType(cmd: UInt, meta: ClientMetadata): UInt = { + val dirty = clientStatesWithDirtyData.contains(meta.state) + MuxLookup(cmd, releaseCopyAck, Array( + M_FLUSH -> Mux(dirty, releaseInvalidateData, releaseInvalidateAck), + M_PRODUCE -> Mux(dirty, releaseDowngradeData, releaseDowngradeAck), + M_CLEAN -> Mux(dirty, releaseCopyData, releaseCopyAck))) + } + + def getReleaseType(incoming: Probe, meta: ClientMetadata): UInt = { + val dirty = clientStatesWithDirtyData.contains(meta.state) + val with_data = MuxLookup(incoming.p_type, releaseInvalidateData, Array( + probeInvalidate -> Mux(Vec(clientExclusiveDirty, clientMigratoryDirty).contains(meta.state), + releaseInvalidateDataMigratory, releaseInvalidateData), + probeDowngrade -> Mux(meta.state === clientMigratoryDirty, + releaseDowngradeDataMigratory, releaseDowngradeData), + probeCopy -> releaseCopyData)) + val without_data = MuxLookup(incoming.p_type, releaseInvalidateAck, Array( + probeInvalidate -> Mux(clientExclusiveClean === meta.state, + releaseInvalidateAckMigratory, releaseInvalidateAck), + probeInvalidateOthers -> Mux(clientSharedByTwo === meta.state, + releaseInvalidateAckMigratory, releaseInvalidateAck), + probeDowngrade -> Mux(meta.state != clientInvalid, + releaseDowngradeAckHasCopy, releaseDowngradeAck), + probeCopy -> releaseCopyAck)) + Mux(dirty, with_data, without_data) + } + + def clientMetadataOnReset = ClientMetadata(clientInvalid) + + def clientMetadataOnHit(cmd: UInt, meta: ClientMetadata) = + ClientMetadata( + Mux(isWrite(cmd), MuxLookup(meta.state, clientExclusiveDirty, Array( + clientExclusiveClean -> clientExclusiveDirty, + clientMigratoryClean -> clientMigratoryDirty)), + meta.state)) + + def clientMetadataOnCacheControl(cmd: UInt, meta: ClientMetadata) = + ClientMetadata( + MuxLookup(cmd, meta.state, Array( + M_FLUSH -> clientInvalid, + M_PRODUCE -> Mux(clientStatesWithWritePermission.contains(meta.state), + clientShared, meta.state), + M_CLEAN -> MuxLookup(meta.state, meta.state, Array( + clientExclusiveDirty -> clientExclusiveClean, + clientMigratoryDirty -> clientMigratoryClean))))) + + def clientMetadataOnGrant(incoming: Grant, cmd: UInt, meta: ClientMetadata) = + ClientMetadata( + Mux(incoming.isBuiltInType(), clientInvalid, + MuxLookup(incoming.g_type, clientInvalid, Array( + grantShared -> clientShared, + grantExclusive -> Mux(isWrite(cmd), clientExclusiveDirty, clientExclusiveClean), + grantExclusiveAck -> clientExclusiveDirty, + grantReadMigratory -> Mux(isWrite(cmd), clientMigratoryDirty, clientMigratoryClean))))) + + def clientMetadataOnProbe(incoming: Probe, meta: ClientMetadata) = + ClientMetadata( + MuxLookup(incoming.p_type, meta.state, Array( + probeInvalidate -> clientInvalid, + probeInvalidateOthers -> clientInvalid, + probeCopy -> meta.state, + probeDowngrade -> MuxLookup(meta.state, clientShared, Array( + clientExclusiveClean -> clientSharedByTwo, + clientExclusiveDirty -> clientSharedByTwo, + clientSharedByTwo -> clientShared, + clientMigratoryClean -> clientSharedByTwo, + clientMigratoryDirty -> clientInvalid))))) + + // Manager states and functions: + val nManagerStates = 0 // TODO: we could add some states to reduce the number of message types + + def requiresProbes(a: Acquire, meta: ManagerMetadata) = + Mux(dir.none(meta.sharers), Bool(false), + Mux(dir.one(meta.sharers), Bool(true), //TODO: for now we assume it's Exclusive + Mux(a.isBuiltInType(), a.hasData(), a.a_type != acquireShared))) + + def requiresProbes(cmd: UInt, meta: ManagerMetadata) = !dir.none(meta.sharers) + + def getProbeType(cmd: UInt, meta: ManagerMetadata): UInt = + MuxLookup(cmd, probeCopy, Array( + M_FLUSH -> probeInvalidate, + M_PRODUCE -> probeDowngrade)) + + def getProbeType(a: Acquire, meta: ManagerMetadata): UInt = + Mux(a.isBuiltInType(), + MuxLookup(a.a_type, probeCopy, Array( + Acquire.getBlockType -> probeCopy, + Acquire.putBlockType -> probeInvalidate, + Acquire.getType -> probeCopy, + Acquire.putType -> probeInvalidate, + Acquire.putAtomicType -> probeInvalidate)), + MuxLookup(a.a_type, probeCopy, Array( + acquireShared -> probeDowngrade, + acquireExclusive -> probeInvalidate, + acquireInvalidateOthers -> probeInvalidateOthers))) + + def getGrantType(a: Acquire, meta: ManagerMetadata): UInt = + MuxLookup(a.a_type, grantShared, Array( + acquireShared -> Mux(!dir.none(meta.sharers), grantShared, grantExclusive), + acquireExclusive -> grantExclusive, + acquireInvalidateOthers -> grantExclusiveAck)) //TODO: add this to MESI for broadcast? + + def managerMetadataOnReset = ManagerMetadata() + + def managerMetadataOnRelease(incoming: Release, src: UInt, meta: ManagerMetadata) = { + val popped = ManagerMetadata(sharers=dir.pop(meta.sharers, src)) + MuxBundle(meta, Array( + incoming.is(releaseInvalidateData) -> popped, + incoming.is(releaseInvalidateAck) -> popped, + incoming.is(releaseInvalidateDataMigratory) -> popped, + incoming.is(releaseInvalidateAckMigratory) -> popped)) + } +} diff --git a/uncore/src/main/scala/consts.scala b/uncore/src/main/scala/consts.scala new file mode 100644 index 00000000..69cdc41f --- /dev/null +++ b/uncore/src/main/scala/consts.scala @@ -0,0 +1,48 @@ +// See LICENSE for license details. + +package uncore +package constants + +import Chisel._ + +object MemoryOpConstants extends MemoryOpConstants +trait MemoryOpConstants { + val MT_SZ = 3 + val MT_X = Bits("b???") + val MT_B = Bits("b000") + val MT_H = Bits("b001") + val MT_W = Bits("b010") + val MT_D = Bits("b011") + val MT_BU = Bits("b100") + val MT_HU = Bits("b101") + val MT_WU = Bits("b110") + + val NUM_XA_OPS = 9 + val M_SZ = 5 + val M_X = Bits("b?????"); + val M_XRD = Bits("b00000"); // int load + val M_XWR = Bits("b00001"); // int store + val M_PFR = Bits("b00010"); // prefetch with intent to read + val M_PFW = Bits("b00011"); // prefetch with intent to write + val M_XA_SWAP = Bits("b00100"); + val M_NOP = Bits("b00101"); + val M_XLR = Bits("b00110"); + val M_XSC = Bits("b00111"); + val M_XA_ADD = Bits("b01000"); + val M_XA_XOR = Bits("b01001"); + val M_XA_OR = Bits("b01010"); + val M_XA_AND = Bits("b01011"); + val M_XA_MIN = Bits("b01100"); + val M_XA_MAX = Bits("b01101"); + val M_XA_MINU = Bits("b01110"); + val M_XA_MAXU = Bits("b01111"); + val M_FLUSH = Bits("b10000") // write back dirty data and cede R/W permissions + val M_PRODUCE = Bits("b10001") // write back dirty data and cede W permissions + val M_CLEAN = Bits("b10011") // write back dirty data and retain R/W permissions + + def isAMO(cmd: Bits) = cmd(3) || cmd === M_XA_SWAP + def isPrefetch(cmd: Bits) = cmd === M_PFR || cmd === M_PFW + def isRead(cmd: Bits) = cmd === M_XRD || cmd === M_XLR || isAMO(cmd) + def isWrite(cmd: Bits) = cmd === M_XWR || cmd === M_XSC || isAMO(cmd) + def isWriteIntent(cmd: Bits) = isWrite(cmd) || cmd === M_PFW || cmd === M_XLR +} diff --git a/uncore/src/main/scala/directory.scala b/uncore/src/main/scala/directory.scala new file mode 100644 index 00000000..db555ad3 --- /dev/null +++ b/uncore/src/main/scala/directory.scala @@ -0,0 +1,43 @@ +// See LICENSE for license details. + +package uncore +import Chisel._ + +// This class encapsulates transformations on different directory information +// storage formats +abstract class DirectoryRepresentation(val width: Int) { + def pop(prev: UInt, id: UInt): UInt + def push(prev: UInt, id: UInt): UInt + def flush: UInt + def none(s: UInt): Bool + def one(s: UInt): Bool + def count(s: UInt): UInt + def next(s: UInt): UInt + def full(s: UInt): UInt +} + +abstract trait HasDirectoryRepresentation { + val dir: DirectoryRepresentation +} + +class NullRepresentation(nClients: Int) extends DirectoryRepresentation(1) { + def pop(prev: UInt, id: UInt) = UInt(0) + def push(prev: UInt, id: UInt) = UInt(0) + def flush = UInt(0) + def none(s: UInt) = Bool(false) + def one(s: UInt) = Bool(false) + def count(s: UInt) = UInt(nClients) + def next(s: UInt) = UInt(0) + def full(s: UInt) = SInt(-1, width = nClients).toUInt +} + +class FullRepresentation(nClients: Int) extends DirectoryRepresentation(nClients) { + def pop(prev: UInt, id: UInt) = prev & ~UIntToOH(id) + def push(prev: UInt, id: UInt) = prev | UIntToOH(id) + def flush = UInt(0, width = width) + def none(s: UInt) = s === UInt(0) + def one(s: UInt) = PopCount(s) === UInt(1) + def count(s: UInt) = PopCount(s) + def next(s: UInt) = PriorityEncoder(s) + def full(s: UInt) = s +} diff --git a/uncore/src/main/scala/ecc.scala b/uncore/src/main/scala/ecc.scala new file mode 100644 index 00000000..b5864b2c --- /dev/null +++ b/uncore/src/main/scala/ecc.scala @@ -0,0 +1,146 @@ +// See LICENSE for license details. + +package uncore + +import Chisel._ + +abstract class Decoding +{ + def uncorrected: Bits + def corrected: Bits + def correctable: Bool + def uncorrectable: Bool + def error = correctable || uncorrectable +} + +abstract class Code +{ + def width(w0: Int): Int + def encode(x: Bits): Bits + def decode(x: Bits): Decoding +} + +class IdentityCode extends Code +{ + def width(w0: Int) = w0 + def encode(x: Bits) = x + def decode(y: Bits) = new Decoding { + def uncorrected = y + def corrected = y + def correctable = Bool(false) + def uncorrectable = Bool(false) + } +} + +class ParityCode extends Code +{ + def width(w0: Int) = w0+1 + def encode(x: Bits) = Cat(x.xorR, x) + def decode(y: Bits) = new Decoding { + def uncorrected = y(y.getWidth-2,0) + def corrected = uncorrected + def correctable = Bool(false) + def uncorrectable = y.xorR + } +} + +class SECCode extends Code +{ + def width(k: Int) = { + val m = new Unsigned(k).log2 + 1 + k + m + (if((1 << m) < m+k+1) 1 else 0) + } + def encode(x: Bits) = { + val k = x.getWidth + require(k > 0) + val n = width(k) + + val y = for (i <- 1 to n) yield { + if (isPow2(i)) { + val r = for (j <- 1 to n; if j != i && (j & i) != 0) + yield x(mapping(j)) + r reduce (_^_) + } else + x(mapping(i)) + } + Vec(y).toBits + } + def decode(y: Bits) = new Decoding { + val n = y.getWidth + require(n > 0 && !isPow2(n)) + + val p2 = for (i <- 0 until log2Up(n)) yield 1 << i + val syndrome = p2 map { i => + val r = for (j <- 1 to n; if (j & i) != 0) + yield y(j-1) + r reduce (_^_) + } + val s = Vec(syndrome).toBits + + private def swizzle(z: Bits) = Vec((1 to n).filter(i => !isPow2(i)).map(i => z(i-1))).toBits + def uncorrected = swizzle(y) + def corrected = swizzle(((y.toUInt << UInt(1)) ^ UIntToOH(s)) >> UInt(1)) + def correctable = s.orR + def uncorrectable = Bool(false) + } + private def mapping(i: Int) = i-1-log2Up(i) +} + +class SECDEDCode extends Code +{ + private val sec = new SECCode + private val par = new ParityCode + + def width(k: Int) = sec.width(k)+1 + def encode(x: Bits) = par.encode(sec.encode(x)) + def decode(x: Bits) = new Decoding { + val secdec = sec.decode(x(x.getWidth-2,0)) + val pardec = par.decode(x) + + def uncorrected = secdec.uncorrected + def corrected = secdec.corrected + def correctable = pardec.uncorrectable + def uncorrectable = !pardec.uncorrectable && secdec.correctable + } +} + +object ErrGen +{ + // generate a 1-bit error with approximate probability 2^-f + def apply(width: Int, f: Int): Bits = { + require(width > 0 && f >= 0 && log2Up(width) + f <= 16) + UIntToOH(LFSR16()(log2Up(width)+f-1,0))(width-1,0) + } + def apply(x: Bits, f: Int): Bits = x ^ apply(x.getWidth, f) +} + +class SECDEDTest extends Module +{ + val code = new SECDEDCode + val k = 4 + val n = code.width(k) + + val io = new Bundle { + val original = Bits(OUTPUT, k) + val encoded = Bits(OUTPUT, n) + val injected = Bits(OUTPUT, n) + val uncorrected = Bits(OUTPUT, k) + val corrected = Bits(OUTPUT, k) + val correctable = Bool(OUTPUT) + val uncorrectable = Bool(OUTPUT) + } + + val c = Counter(Bool(true), 1 << k) + val numErrors = Counter(c._2, 3)._1 + val e = code.encode(c._1) + val i = e ^ Mux(numErrors < UInt(1), UInt(0), ErrGen(n, 1)) ^ Mux(numErrors < UInt(2), UInt(0), ErrGen(n, 1)) + val d = code.decode(i) + + io.original := c._1 + io.encoded := e + io.injected := i + io.uncorrected := d.uncorrected + io.corrected := d.corrected + io.correctable := d.correctable + io.uncorrectable := d.uncorrectable +} diff --git a/uncore/src/main/scala/htif.scala b/uncore/src/main/scala/htif.scala new file mode 100644 index 00000000..8a79cda1 --- /dev/null +++ b/uncore/src/main/scala/htif.scala @@ -0,0 +1,255 @@ +// See LICENSE for license details. + +package uncore + +import Chisel._ +import Node._ +import uncore._ + +case object HTIFWidth extends Field[Int] +case object HTIFNSCR extends Field[Int] +case object HTIFOffsetBits extends Field[Int] +case object HTIFNCores extends Field[Int] + +abstract trait HTIFParameters extends UsesParameters { + val dataBits = params(TLDataBits) + val dataBeats = params(TLDataBeats) + val w = params(HTIFWidth) + val nSCR = params(HTIFNSCR) + val offsetBits = params(HTIFOffsetBits) + val nCores = params(HTIFNCores) +} + +abstract class HTIFBundle extends Bundle with HTIFParameters + +class HostIO extends HTIFBundle +{ + val clk = Bool(OUTPUT) + val clk_edge = Bool(OUTPUT) + val in = Decoupled(Bits(width = w)).flip + val out = Decoupled(Bits(width = w)) + val debug_stats_pcr = Bool(OUTPUT) +} + +class PCRReq extends Bundle +{ + val rw = Bool() + val addr = Bits(width = 12) + val data = Bits(width = 64) +} + +class HTIFIO extends HTIFBundle { + val reset = Bool(INPUT) + val id = UInt(INPUT, log2Up(nCores)) + val pcr_req = Decoupled(new PCRReq).flip + val pcr_rep = Decoupled(Bits(width = 64)) + val ipi_req = Decoupled(Bits(width = log2Up(nCores))) + val ipi_rep = Decoupled(Bool()).flip + val debug_stats_pcr = Bool(OUTPUT) + // wired directly to stats register + // expected to be used to quickly indicate to testbench to do logging b/c in 'interesting' work +} + +class SCRIO extends HTIFBundle { + val rdata = Vec.fill(nSCR){Bits(INPUT, 64)} + val wen = Bool(OUTPUT) + val waddr = UInt(OUTPUT, log2Up(nSCR)) + val wdata = Bits(OUTPUT, 64) +} + +class HTIFModuleIO extends HTIFBundle { + val host = new HostIO + val cpu = Vec.fill(nCores){new HTIFIO}.flip + val mem = new ClientUncachedTileLinkIO + val scr = new SCRIO +} + +class HTIF(pcr_RESET: Int) extends Module with HTIFParameters { + val io = new HTIFModuleIO + + io.host.debug_stats_pcr := io.cpu.map(_.debug_stats_pcr).reduce(_||_) + // system is 'interesting' if any tile is 'interesting' + + val short_request_bits = 64 + val long_request_bits = short_request_bits + dataBits*dataBeats + require(short_request_bits % w == 0) + + val rx_count_w = 13 + log2Up(64) - log2Up(w) // data size field is 12 bits + val rx_count = Reg(init=UInt(0,rx_count_w)) + val rx_shifter = Reg(Bits(width = short_request_bits)) + val rx_shifter_in = Cat(io.host.in.bits, rx_shifter(short_request_bits-1,w)) + val next_cmd = rx_shifter_in(3,0) + val cmd = Reg(Bits()) + val size = Reg(Bits()) + val pos = Reg(Bits()) + val seqno = Reg(Bits()) + val addr = Reg(Bits()) + when (io.host.in.valid && io.host.in.ready) { + rx_shifter := rx_shifter_in + rx_count := rx_count + UInt(1) + when (rx_count === UInt(short_request_bits/w-1)) { + cmd := next_cmd + size := rx_shifter_in(15,4) + pos := rx_shifter_in(15,4+offsetBits-3) + seqno := rx_shifter_in(23,16) + addr := rx_shifter_in(63,24) + } + } + + val rx_word_count = (rx_count >> UInt(log2Up(short_request_bits/w))) + val rx_word_done = io.host.in.valid && rx_count(log2Up(short_request_bits/w)-1,0).andR + val packet_ram_depth = long_request_bits/short_request_bits-1 + val packet_ram = Mem(Bits(width = short_request_bits), packet_ram_depth) + when (rx_word_done && io.host.in.ready) { + packet_ram(rx_word_count(log2Up(packet_ram_depth)-1,0) - UInt(1)) := rx_shifter_in + } + + val cmd_readmem :: cmd_writemem :: cmd_readcr :: cmd_writecr :: cmd_ack :: cmd_nack :: Nil = Enum(UInt(), 6) + + val pcr_addr = addr(io.cpu(0).pcr_req.bits.addr.getWidth-1, 0) + val pcr_coreid = addr(log2Up(nCores)-1+20+1,20) + val pcr_wdata = packet_ram(0) + + val bad_mem_packet = size(offsetBits-1-3,0).orR || addr(offsetBits-1-3,0).orR + val nack = Mux(cmd === cmd_readmem || cmd === cmd_writemem, bad_mem_packet, + Mux(cmd === cmd_readcr || cmd === cmd_writecr, size != UInt(1), + Bool(true))) + + val tx_count = Reg(init=UInt(0, rx_count_w)) + val tx_subword_count = tx_count(log2Up(short_request_bits/w)-1,0) + val tx_word_count = tx_count(rx_count_w-1, log2Up(short_request_bits/w)) + val packet_ram_raddr = tx_word_count(log2Up(packet_ram_depth)-1,0) - UInt(1) + when (io.host.out.valid && io.host.out.ready) { + tx_count := tx_count + UInt(1) + } + + val rx_done = rx_word_done && Mux(rx_word_count === UInt(0), next_cmd != cmd_writemem && next_cmd != cmd_writecr, rx_word_count === size || rx_word_count(log2Up(packet_ram_depth)-1,0) === UInt(0)) + val tx_size = Mux(!nack && (cmd === cmd_readmem || cmd === cmd_readcr || cmd === cmd_writecr), size, UInt(0)) + val tx_done = io.host.out.ready && tx_subword_count.andR && (tx_word_count === tx_size || tx_word_count > UInt(0) && packet_ram_raddr.andR) + + val state_rx :: state_pcr_req :: state_pcr_resp :: state_mem_rreq :: state_mem_wreq :: state_mem_rresp :: state_mem_wresp :: state_tx :: Nil = Enum(UInt(), 8) + val state = Reg(init=state_rx) + + val (cnt, cnt_done) = Counter((state === state_mem_wreq && io.mem.acquire.ready) || + (state === state_mem_rresp && io.mem.grant.valid), dataBeats) + val rx_cmd = Mux(rx_word_count === UInt(0), next_cmd, cmd) + when (state === state_rx && rx_done) { + state := Mux(rx_cmd === cmd_readmem, state_mem_rreq, + Mux(rx_cmd === cmd_writemem, state_mem_wreq, + Mux(rx_cmd === cmd_readcr || rx_cmd === cmd_writecr, state_pcr_req, + state_tx))) + } + when (state === state_mem_wreq) { + when (cnt_done) { state := state_mem_wresp } + } + when (state === state_mem_rreq) { + when(io.mem.acquire.ready) { state := state_mem_rresp } + } + when (state === state_mem_wresp && io.mem.grant.valid) { + state := Mux(cmd === cmd_readmem || pos === UInt(1), state_tx, state_rx) + pos := pos - UInt(1) + addr := addr + UInt(1 << offsetBits-3) + } + when (state === state_mem_rresp && cnt_done) { + state := Mux(cmd === cmd_readmem || pos === UInt(1), state_tx, state_rx) + pos := pos - UInt(1) + addr := addr + UInt(1 << offsetBits-3) + } + when (state === state_tx && tx_done) { + when (tx_word_count === tx_size) { + rx_count := UInt(0) + tx_count := UInt(0) + } + state := Mux(cmd === cmd_readmem && pos != UInt(0), state_mem_rreq, state_rx) + } + + val n = dataBits/short_request_bits + val mem_req_data = (0 until n).map { i => + val ui = UInt(i, log2Up(n)) + when (state === state_mem_rresp && io.mem.grant.valid) { + packet_ram(Cat(io.mem.grant.bits.addr_beat, ui)) := + io.mem.grant.bits.data((i+1)*short_request_bits-1, i*short_request_bits) + } + packet_ram(Cat(cnt, ui)) + }.reverse.reduce(_##_) + + val init_addr = addr.toUInt >> UInt(offsetBits-3) + io.mem.acquire.valid := state === state_mem_rreq || state === state_mem_wreq + io.mem.acquire.bits := Mux(cmd === cmd_writemem, + PutBlock( + addr_block = init_addr, + addr_beat = cnt, + client_xact_id = UInt(0), + data = mem_req_data), + GetBlock(addr_block = init_addr)) + io.mem.grant.ready := Bool(true) + + val pcrReadData = Reg(Bits(width = io.cpu(0).pcr_rep.bits.getWidth)) + for (i <- 0 until nCores) { + val my_reset = Reg(init=Bool(true)) + val my_ipi = Reg(init=Bool(false)) + + val cpu = io.cpu(i) + val me = pcr_coreid === UInt(i) + cpu.pcr_req.valid := state === state_pcr_req && me && pcr_addr != UInt(pcr_RESET) + cpu.pcr_req.bits.rw := cmd === cmd_writecr + cpu.pcr_req.bits.addr := pcr_addr + cpu.pcr_req.bits.data := pcr_wdata + cpu.reset := my_reset + + when (cpu.ipi_rep.ready) { + my_ipi := Bool(false) + } + cpu.ipi_rep.valid := my_ipi + cpu.ipi_req.ready := Bool(true) + for (j <- 0 until nCores) { + when (io.cpu(j).ipi_req.valid && io.cpu(j).ipi_req.bits === UInt(i)) { + my_ipi := Bool(true) + } + } + + when (cpu.pcr_req.valid && cpu.pcr_req.ready) { + state := state_pcr_resp + } + when (state === state_pcr_req && me && pcr_addr === UInt(pcr_RESET)) { + when (cmd === cmd_writecr) { + my_reset := pcr_wdata(0) + } + pcrReadData := my_reset.toBits + state := state_tx + } + + cpu.pcr_rep.ready := Bool(true) + when (cpu.pcr_rep.valid) { + pcrReadData := cpu.pcr_rep.bits + state := state_tx + } + } + + val scr_addr = addr(log2Up(nSCR)-1, 0) + val scr_rdata = Vec.fill(io.scr.rdata.size){Bits(width = 64)} + for (i <- 0 until scr_rdata.size) + scr_rdata(i) := io.scr.rdata(i) + scr_rdata(0) := UInt(nCores) + scr_rdata(1) := UInt((BigInt(dataBits*dataBeats/8) << params(TLBlockAddrBits)) >> 20) + + io.scr.wen := Bool(false) + io.scr.wdata := pcr_wdata + io.scr.waddr := scr_addr.toUInt + when (state === state_pcr_req && pcr_coreid === SInt(-1)) { + io.scr.wen := cmd === cmd_writecr + pcrReadData := scr_rdata(scr_addr) + state := state_tx + } + + val tx_cmd = Mux(nack, cmd_nack, cmd_ack) + val tx_cmd_ext = Cat(Bits(0, 4-tx_cmd.getWidth), tx_cmd) + val tx_header = Cat(addr, seqno, tx_size, tx_cmd_ext) + val tx_data = Mux(tx_word_count === UInt(0), tx_header, + Mux(cmd === cmd_readcr || cmd === cmd_writecr, pcrReadData, + packet_ram(packet_ram_raddr))) + + io.host.in.ready := state === state_rx + io.host.out.valid := state === state_tx + io.host.out.bits := tx_data >> Cat(tx_count(log2Up(short_request_bits/w)-1,0), Bits(0, log2Up(w))) +} diff --git a/uncore/src/main/scala/memserdes.scala b/uncore/src/main/scala/memserdes.scala new file mode 100644 index 00000000..7497e2e4 --- /dev/null +++ b/uncore/src/main/scala/memserdes.scala @@ -0,0 +1,584 @@ +// See LICENSE for license details. + +package uncore +import Chisel._ +import scala.math._ + +case object PAddrBits extends Field[Int] +case object VAddrBits extends Field[Int] +case object PgIdxBits extends Field[Int] +case object PgLevels extends Field[Int] +case object PgLevelBits extends Field[Int] +case object ASIdBits extends Field[Int] +case object PPNBits extends Field[Int] +case object VPNBits extends Field[Int] + +case object MIFAddrBits extends Field[Int] +case object MIFDataBits extends Field[Int] +case object MIFTagBits extends Field[Int] +case object MIFDataBeats extends Field[Int] + +trait MIFParameters extends UsesParameters { + val mifTagBits = params(MIFTagBits) + val mifAddrBits = params(MIFAddrBits) + val mifDataBits = params(MIFDataBits) + val mifDataBeats = params(MIFDataBeats) +} + +abstract class MIFBundle extends Bundle with MIFParameters +abstract class MIFModule extends Module with MIFParameters + +trait HasMemData extends MIFBundle { + val data = Bits(width = mifDataBits) +} + +trait HasMemAddr extends MIFBundle { + val addr = UInt(width = mifAddrBits) +} + +trait HasMemTag extends MIFBundle { + val tag = UInt(width = mifTagBits) +} + +class MemReqCmd extends HasMemAddr with HasMemTag { + val rw = Bool() +} + +class MemTag extends HasMemTag +class MemData extends HasMemData +class MemResp extends HasMemData with HasMemTag + +class MemIO extends Bundle { + val req_cmd = Decoupled(new MemReqCmd) + val req_data = Decoupled(new MemData) + val resp = Decoupled(new MemResp).flip +} + +class MemPipeIO extends Bundle { + val req_cmd = Decoupled(new MemReqCmd) + val req_data = Decoupled(new MemData) + val resp = Valid(new MemResp).flip +} + +class MemSerializedIO(w: Int) extends Bundle +{ + val req = Decoupled(Bits(width = w)) + val resp = Valid(Bits(width = w)).flip +} + +class MemSerdes(w: Int) extends MIFModule +{ + val io = new Bundle { + val wide = new MemIO().flip + val narrow = new MemSerializedIO(w) + } + val abits = io.wide.req_cmd.bits.toBits.getWidth + val dbits = io.wide.req_data.bits.toBits.getWidth + val rbits = io.wide.resp.bits.getWidth + + val out_buf = Reg(Bits()) + val in_buf = Reg(Bits()) + + val s_idle :: s_read_addr :: s_write_addr :: s_write_idle :: s_write_data :: Nil = Enum(UInt(), 5) + val state = Reg(init=s_idle) + val send_cnt = Reg(init=UInt(0, log2Up((max(abits, dbits)+w-1)/w))) + val data_send_cnt = Reg(init=UInt(0, log2Up(mifDataBeats))) + val adone = io.narrow.req.ready && send_cnt === UInt((abits-1)/w) + val ddone = io.narrow.req.ready && send_cnt === UInt((dbits-1)/w) + + when (io.narrow.req.valid && io.narrow.req.ready) { + send_cnt := send_cnt + UInt(1) + out_buf := out_buf >> UInt(w) + } + when (io.wide.req_cmd.valid && io.wide.req_cmd.ready) { + out_buf := io.wide.req_cmd.bits.toBits + } + when (io.wide.req_data.valid && io.wide.req_data.ready) { + out_buf := io.wide.req_data.bits.toBits + } + + io.wide.req_cmd.ready := state === s_idle + io.wide.req_data.ready := state === s_write_idle + io.narrow.req.valid := state === s_read_addr || state === s_write_addr || state === s_write_data + io.narrow.req.bits := out_buf + + when (state === s_idle && io.wide.req_cmd.valid) { + state := Mux(io.wide.req_cmd.bits.rw, s_write_addr, s_read_addr) + } + when (state === s_read_addr && adone) { + state := s_idle + send_cnt := UInt(0) + } + when (state === s_write_addr && adone) { + state := s_write_idle + send_cnt := UInt(0) + } + when (state === s_write_idle && io.wide.req_data.valid) { + state := s_write_data + } + when (state === s_write_data && ddone) { + data_send_cnt := data_send_cnt + UInt(1) + state := Mux(data_send_cnt === UInt(mifDataBeats-1), s_idle, s_write_idle) + send_cnt := UInt(0) + } + + val recv_cnt = Reg(init=UInt(0, log2Up((rbits+w-1)/w))) + val data_recv_cnt = Reg(init=UInt(0, log2Up(mifDataBeats))) + val resp_val = Reg(init=Bool(false)) + + resp_val := Bool(false) + when (io.narrow.resp.valid) { + recv_cnt := recv_cnt + UInt(1) + when (recv_cnt === UInt((rbits-1)/w)) { + recv_cnt := UInt(0) + data_recv_cnt := data_recv_cnt + UInt(1) + resp_val := Bool(true) + } + in_buf := Cat(io.narrow.resp.bits, in_buf((rbits+w-1)/w*w-1,w)) + } + + io.wide.resp.valid := resp_val + io.wide.resp.bits := io.wide.resp.bits.fromBits(in_buf) +} + +class MemDesserIO(w: Int) extends Bundle { + val narrow = new MemSerializedIO(w).flip + val wide = new MemIO +} + +class MemDesser(w: Int) extends Module // test rig side +{ + val io = new MemDesserIO(w) + val abits = io.wide.req_cmd.bits.toBits.getWidth + val dbits = io.wide.req_data.bits.toBits.getWidth + val rbits = io.wide.resp.bits.getWidth + val mifDataBeats = params(MIFDataBeats) + + require(dbits >= abits && rbits >= dbits) + val recv_cnt = Reg(init=UInt(0, log2Up((rbits+w-1)/w))) + val data_recv_cnt = Reg(init=UInt(0, log2Up(mifDataBeats))) + val adone = io.narrow.req.valid && recv_cnt === UInt((abits-1)/w) + val ddone = io.narrow.req.valid && recv_cnt === UInt((dbits-1)/w) + val rdone = io.narrow.resp.valid && recv_cnt === UInt((rbits-1)/w) + + val s_cmd_recv :: s_cmd :: s_data_recv :: s_data :: s_reply :: Nil = Enum(UInt(), 5) + val state = Reg(init=s_cmd_recv) + + val in_buf = Reg(Bits()) + when (io.narrow.req.valid && io.narrow.req.ready || io.narrow.resp.valid) { + recv_cnt := recv_cnt + UInt(1) + in_buf := Cat(io.narrow.req.bits, in_buf((rbits+w-1)/w*w-1,w)) + } + io.narrow.req.ready := state === s_cmd_recv || state === s_data_recv + + when (state === s_cmd_recv && adone) { + state := s_cmd + recv_cnt := UInt(0) + } + when (state === s_cmd && io.wide.req_cmd.ready) { + state := Mux(io.wide.req_cmd.bits.rw, s_data_recv, s_reply) + } + when (state === s_data_recv && ddone) { + state := s_data + recv_cnt := UInt(0) + } + when (state === s_data && io.wide.req_data.ready) { + state := s_data_recv + when (data_recv_cnt === UInt(mifDataBeats-1)) { + state := s_cmd_recv + } + data_recv_cnt := data_recv_cnt + UInt(1) + } + when (rdone) { // state === s_reply + when (data_recv_cnt === UInt(mifDataBeats-1)) { + state := s_cmd_recv + } + recv_cnt := UInt(0) + data_recv_cnt := data_recv_cnt + UInt(1) + } + + val req_cmd = in_buf >> UInt(((rbits+w-1)/w - (abits+w-1)/w)*w) + io.wide.req_cmd.valid := state === s_cmd + io.wide.req_cmd.bits := io.wide.req_cmd.bits.fromBits(req_cmd) + + io.wide.req_data.valid := state === s_data + io.wide.req_data.bits.data := in_buf >> UInt(((rbits+w-1)/w - (dbits+w-1)/w)*w) + + val dataq = Module(new Queue(new MemResp, mifDataBeats)) + dataq.io.enq <> io.wide.resp + dataq.io.deq.ready := recv_cnt === UInt((rbits-1)/w) + + io.narrow.resp.valid := dataq.io.deq.valid + io.narrow.resp.bits := dataq.io.deq.bits.toBits >> (recv_cnt * UInt(w)) +} + +//Adapter betweewn an UncachedTileLinkIO and a mem controller MemIO +class MemIOTileLinkIOConverter(qDepth: Int) extends TLModule with MIFParameters { + val io = new Bundle { + val tl = new ManagerTileLinkIO + val mem = new MemIO + } + val dataBits = tlDataBits*tlDataBeats + val dstIdBits = params(LNHeaderBits) + require(tlDataBits*tlDataBeats == mifDataBits*mifDataBeats, "Data sizes between LLC and MC don't agree") + require(dstIdBits + tlClientXactIdBits < mifTagBits, "MemIO converter is going truncate tags: " + dstIdBits + " + " + tlClientXactIdBits + " >= " + mifTagBits) + + io.tl.acquire.ready := Bool(false) + io.tl.probe.valid := Bool(false) + io.tl.release.ready := Bool(false) + io.tl.finish.ready := Bool(true) + io.mem.resp.ready := Bool(false) + + val gnt_arb = Module(new Arbiter(new GrantToDst, 2)) + io.tl.grant <> gnt_arb.io.out + + val dst_off = dstIdBits + tlClientXactIdBits + val acq_has_data = io.tl.acquire.bits.hasData() + val rel_has_data = io.tl.release.bits.hasData() + + // Decompose outgoing TL Acquires into MemIO cmd and data + val active_out = Reg(init=Bool(false)) + val cmd_sent_out = Reg(init=Bool(false)) + val tag_out = Reg(UInt(width = mifTagBits)) + val addr_out = Reg(UInt(width = mifAddrBits)) + val has_data = Reg(init=Bool(false)) + val data_from_rel = Reg(init=Bool(false)) + val (tl_cnt_out, tl_wrap_out) = + Counter((io.tl.acquire.fire() && acq_has_data) || + (io.tl.release.fire() && rel_has_data), tlDataBeats) + val tl_done_out = Reg(init=Bool(false)) + val make_grant_ack = Reg(init=Bool(false)) + + gnt_arb.io.in(1).valid := Bool(false) + gnt_arb.io.in(1).bits := Grant( + dst = (if(dstIdBits > 0) tag_out(dst_off, tlClientXactIdBits + 1) else UInt(0)), + is_builtin_type = Bool(true), + g_type = Mux(data_from_rel, Grant.voluntaryAckType, Grant.putAckType), + client_xact_id = tag_out >> UInt(1), + manager_xact_id = UInt(0)) + + if(tlDataBits != mifDataBits || tlDataBeats != mifDataBeats) { + val mem_cmd_q = Module(new Queue(new MemReqCmd, qDepth)) + val mem_data_q = Module(new Queue(new MemData, qDepth)) + mem_cmd_q.io.enq.valid := Bool(false) + mem_data_q.io.enq.valid := Bool(false) + val (mif_cnt_out, mif_wrap_out) = Counter(mem_data_q.io.enq.fire(), mifDataBeats) + val mif_done_out = Reg(init=Bool(false)) + val tl_buf_out = Vec.fill(tlDataBeats){ Reg(io.tl.acquire.bits.data.clone) } + val mif_buf_out = Vec.fill(mifDataBeats){ new MemData } + mif_buf_out := mif_buf_out.fromBits(tl_buf_out.toBits) + val mif_prog_out = (mif_cnt_out+UInt(1, width = log2Up(mifDataBeats+1)))*UInt(mifDataBits) + val tl_prog_out = tl_cnt_out*UInt(tlDataBits) + + when(!active_out){ + io.tl.release.ready := Bool(true) + io.tl.acquire.ready := !io.tl.release.valid + when(io.tl.release.valid) { + active_out := Bool(true) + cmd_sent_out := Bool(false) + tag_out := Cat(io.tl.release.bits.client_id, + io.tl.release.bits.client_xact_id, + io.tl.release.bits.isVoluntary()) + addr_out := io.tl.release.bits.addr_block + has_data := rel_has_data + data_from_rel := Bool(true) + make_grant_ack := io.tl.release.bits.requiresAck() + tl_done_out := tl_wrap_out + tl_buf_out(tl_cnt_out) := io.tl.release.bits.data + } .elsewhen(io.tl.acquire.valid) { + active_out := Bool(true) + cmd_sent_out := Bool(false) + tag_out := Cat(io.tl.release.bits.client_id, + io.tl.acquire.bits.client_xact_id, + io.tl.acquire.bits.isBuiltInType()) + addr_out := io.tl.acquire.bits.addr_block + has_data := acq_has_data + data_from_rel := Bool(false) + make_grant_ack := acq_has_data + tl_done_out := tl_wrap_out + tl_buf_out(tl_cnt_out) := io.tl.acquire.bits.data + } + } + when(active_out) { + mem_cmd_q.io.enq.valid := !cmd_sent_out + cmd_sent_out := cmd_sent_out || mem_cmd_q.io.enq.fire() + when(has_data) { + when(!tl_done_out) { + io.tl.acquire.ready := Bool(true) + when(io.tl.acquire.valid) { + tl_buf_out(tl_cnt_out) := Mux(data_from_rel, + io.tl.release.bits.data, + io.tl.acquire.bits.data) + } + } + when(!mif_done_out) { + mem_data_q.io.enq.valid := tl_done_out || mif_prog_out <= tl_prog_out + } + } + when(tl_wrap_out) { tl_done_out := Bool(true) } + when(mif_wrap_out) { mif_done_out := Bool(true) } + when(tl_done_out && make_grant_ack) { + gnt_arb.io.in(1).valid := Bool(true) + when(gnt_arb.io.in(1).ready) { make_grant_ack := Bool(false) } + } + when(cmd_sent_out && (!has_data || mif_done_out) && !make_grant_ack) { + active_out := Bool(false) + } + } + + mem_cmd_q.io.enq.bits.rw := has_data + mem_cmd_q.io.enq.bits.tag := tag_out + mem_cmd_q.io.enq.bits.addr := addr_out + mem_data_q.io.enq.bits.data := mif_buf_out(mif_cnt_out).data + io.mem.req_cmd <> mem_cmd_q.io.deq + io.mem.req_data <> mem_data_q.io.deq + } else { // Don't make the data buffers and try to flow cmd and data + io.mem.req_cmd.valid := Bool(false) + io.mem.req_data.valid := Bool(false) + io.mem.req_cmd.bits.rw := has_data + io.mem.req_cmd.bits.tag := tag_out + io.mem.req_cmd.bits.addr := addr_out + io.mem.req_data.bits.data := Mux(data_from_rel, + io.tl.release.bits.data, + io.tl.acquire.bits.data) + when(!active_out){ + io.tl.release.ready := io.mem.req_data.ready + io.tl.acquire.ready := io.mem.req_data.ready && !io.tl.release.valid + io.mem.req_data.valid := (io.tl.release.valid && rel_has_data) || + (io.tl.acquire.valid && acq_has_data) + when(io.mem.req_data.ready && (io.tl.release.valid || io.tl.acquire.valid)) { + active_out := !io.mem.req_cmd.ready || io.mem.req_data.valid + io.mem.req_cmd.valid := Bool(true) + cmd_sent_out := io.mem.req_cmd.ready + tl_done_out := tl_wrap_out + when(io.tl.release.valid) { + data_from_rel := Bool(true) + make_grant_ack := io.tl.release.bits.requiresAck() + io.mem.req_data.bits.data := io.tl.release.bits.data + val tag = Cat(io.tl.release.bits.client_id, + io.tl.release.bits.client_xact_id, + io.tl.release.bits.isVoluntary()) + val addr = io.tl.release.bits.addr_block + io.mem.req_cmd.bits.tag := tag + io.mem.req_cmd.bits.addr := addr + io.mem.req_cmd.bits.rw := rel_has_data + tag_out := tag + addr_out := addr + has_data := rel_has_data + } .elsewhen(io.tl.acquire.valid) { + data_from_rel := Bool(false) + make_grant_ack := acq_has_data // i.e. is it a Put + io.mem.req_data.bits.data := io.tl.acquire.bits.data + io.mem.req_cmd.bits.rw := acq_has_data + val tag = Cat(io.tl.acquire.bits.client_id, + io.tl.acquire.bits.client_xact_id, + io.tl.acquire.bits.isBuiltInType()) + val addr = io.tl.acquire.bits.addr_block + io.mem.req_cmd.bits.tag := tag + io.mem.req_cmd.bits.addr := addr + io.mem.req_cmd.bits.rw := acq_has_data + tag_out := tag + addr_out := addr + has_data := acq_has_data + } + } + } + when(active_out) { + io.mem.req_cmd.valid := !cmd_sent_out + cmd_sent_out := cmd_sent_out || io.mem.req_cmd.fire() + when(has_data && !tl_done_out) { + when(data_from_rel) { + io.tl.release.ready := io.mem.req_data.ready + io.mem.req_data.valid := io.tl.release.valid + } .otherwise { + io.tl.acquire.ready := io.mem.req_data.ready + io.mem.req_data.valid := io.tl.acquire.valid + } + } + when(tl_wrap_out) { tl_done_out := Bool(true) } + when(tl_done_out && make_grant_ack) { + gnt_arb.io.in(1).valid := Bool(true) // TODO: grants for voluntary acks? + when(gnt_arb.io.in(1).ready) { make_grant_ack := Bool(false) } + } + when(cmd_sent_out && (!has_data || tl_done_out) && !make_grant_ack) { + active_out := Bool(false) + } + } + } + + // Aggregate incoming MemIO responses into TL Grants + val active_in = Reg(init=Bool(false)) + val (tl_cnt_in, tl_wrap_in) = Counter(io.tl.grant.fire() && io.tl.grant.bits.hasMultibeatData(), tlDataBeats) + val tag_in = Reg(UInt(width = mifTagBits)) + + if(tlDataBits != mifDataBits || tlDataBeats != mifDataBeats) { + val (mif_cnt_in, mif_wrap_in) = Counter(io.mem.resp.fire(), mifDataBeats) // TODO: Assumes all resps have data + val mif_done_in = Reg(init=Bool(false)) + val mif_buf_in = Vec.fill(mifDataBeats){ Reg(new MemData) } + val tl_buf_in = Vec.fill(tlDataBeats){ io.tl.acquire.bits.data.clone } + tl_buf_in := tl_buf_in.fromBits(mif_buf_in.toBits) + val tl_prog_in = (tl_cnt_in+UInt(1, width = log2Up(tlDataBeats+1)))*UInt(tlDataBits) + val mif_prog_in = mif_cnt_in*UInt(mifDataBits) + gnt_arb.io.in(0).bits := Grant( + dst = (if(dstIdBits > 0) tag_in(dst_off, tlClientXactIdBits + 1) else UInt(0)), + is_builtin_type = tag_in(0), + g_type = Mux(tag_in(0), Grant.getDataBlockType, UInt(0)), // TODO: Assumes MI or MEI protocol + client_xact_id = tag_in >> UInt(1), + manager_xact_id = UInt(0), + addr_beat = tl_cnt_in, + data = tl_buf_in(tl_cnt_in)) + + when(!active_in) { + io.mem.resp.ready := Bool(true) + when(io.mem.resp.valid) { + active_in := Bool(true) + mif_done_in := mif_wrap_in + tag_in := io.mem.resp.bits.tag + mif_buf_in(tl_cnt_in).data := io.mem.resp.bits.data + } + } + when(active_in) { + gnt_arb.io.in(0).valid := mif_done_in || tl_prog_in <= mif_prog_in + when(!mif_done_in) { + io.mem.resp.ready := Bool(true) + when(io.mem.resp.valid) { + mif_buf_in(mif_cnt_in).data := io.mem.resp.bits.data + } + } + when(mif_wrap_in) { mif_done_in := Bool(true) } + when(tl_wrap_in) { active_in := Bool(false) } + } + } else { // Don't generate all the uneeded data buffers and flow resp + gnt_arb.io.in(0).valid := io.mem.resp.valid + io.mem.resp.ready := gnt_arb.io.in(0).ready + gnt_arb.io.in(0).bits := Grant( + dst = (if(dstIdBits > 0) io.mem.resp.bits.tag(dst_off, tlClientXactIdBits + 1) else UInt(0)), + is_builtin_type = io.mem.resp.bits.tag(0), + g_type = Mux(io.mem.resp.bits.tag(0), Grant.getDataBlockType, UInt(0)), // TODO: Assumes MI or MEI protocol + client_xact_id = io.mem.resp.bits.tag >> UInt(1), + manager_xact_id = UInt(0), + addr_beat = tl_cnt_in, + data = io.mem.resp.bits.data) + } +} + +class HellaFlowQueue[T <: Data](val entries: Int)(data: => T) extends Module +{ + val io = new QueueIO(data, entries) + require(entries > 1) + + val do_flow = Bool() + val do_enq = io.enq.fire() && !do_flow + val do_deq = io.deq.fire() && !do_flow + + val maybe_full = Reg(init=Bool(false)) + val enq_ptr = Counter(do_enq, entries)._1 + val (deq_ptr, deq_done) = Counter(do_deq, entries) + when (do_enq != do_deq) { maybe_full := do_enq } + + val ptr_match = enq_ptr === deq_ptr + val empty = ptr_match && !maybe_full + val full = ptr_match && maybe_full + val atLeastTwo = full || enq_ptr - deq_ptr >= UInt(2) + do_flow := empty && io.deq.ready + + val ram = Mem(data, entries, seqRead = true) + val ram_addr = Reg(Bits()) + val ram_out_valid = Reg(Bool()) + ram_out_valid := Bool(false) + when (do_enq) { ram(enq_ptr) := io.enq.bits } + when (io.deq.ready && (atLeastTwo || !io.deq.valid && !empty)) { + ram_out_valid := Bool(true) + ram_addr := Mux(io.deq.valid, Mux(deq_done, UInt(0), deq_ptr + UInt(1)), deq_ptr) + } + + io.deq.valid := Mux(empty, io.enq.valid, ram_out_valid) + io.enq.ready := !full + io.deq.bits := Mux(empty, io.enq.bits, ram(ram_addr)) +} + +class HellaQueue[T <: Data](val entries: Int)(data: => T) extends Module +{ + val io = new QueueIO(data, entries) + + val fq = Module(new HellaFlowQueue(entries)(data)) + io.enq <> fq.io.enq + io.deq <> Queue(fq.io.deq, 1, pipe = true) +} + +object HellaQueue +{ + def apply[T <: Data](enq: DecoupledIO[T], entries: Int) = { + val q = Module((new HellaQueue(entries)) { enq.bits.clone }) + q.io.enq.valid := enq.valid // not using <> so that override is allowed + q.io.enq.bits := enq.bits + enq.ready := q.io.enq.ready + q.io.deq + } +} + +class MemPipeIOMemIOConverter(numRequests: Int) extends MIFModule { + val io = new Bundle { + val cpu = new MemIO().flip + val mem = new MemPipeIO + } + + val numEntries = numRequests * mifDataBeats + val size = log2Down(numEntries) + 1 + + val inc = Bool() + val dec = Bool() + val count = Reg(init=UInt(numEntries, size)) + val watermark = count >= UInt(mifDataBeats) + + when (inc && !dec) { + count := count + UInt(1) + } + when (!inc && dec) { + count := count - UInt(mifDataBeats) + } + when (inc && dec) { + count := count - UInt(mifDataBeats-1) + } + + val cmdq_mask = io.cpu.req_cmd.bits.rw || watermark + + io.mem.req_cmd.valid := io.cpu.req_cmd.valid && cmdq_mask + io.cpu.req_cmd.ready := io.mem.req_cmd.ready && cmdq_mask + io.mem.req_cmd.bits := io.cpu.req_cmd.bits + + io.mem.req_data <> io.cpu.req_data + + // Have separate queues to allow for different mem implementations + val resp_data_q = Module((new HellaQueue(numEntries)) { new MemData }) + resp_data_q.io.enq.valid := io.mem.resp.valid + resp_data_q.io.enq.bits.data := io.mem.resp.bits.data + + val resp_tag_q = Module((new HellaQueue(numEntries)) { new MemTag }) + resp_tag_q.io.enq.valid := io.mem.resp.valid + resp_tag_q.io.enq.bits.tag := io.mem.resp.bits.tag + + io.cpu.resp.valid := resp_data_q.io.deq.valid && resp_tag_q.io.deq.valid + io.cpu.resp.bits.data := resp_data_q.io.deq.bits.data + io.cpu.resp.bits.tag := resp_tag_q.io.deq.bits.tag + resp_data_q.io.deq.ready := io.cpu.resp.ready + resp_tag_q.io.deq.ready := io.cpu.resp.ready + + inc := resp_data_q.io.deq.fire() && resp_tag_q.io.deq.fire() + dec := io.mem.req_cmd.fire() && !io.mem.req_cmd.bits.rw +} + +class MemPipeIOTileLinkIOConverter(outstanding: Int) extends MIFModule { + val io = new Bundle { + val tl = new ManagerTileLinkIO + val mem = new MemPipeIO + } + + val a = Module(new MemIOTileLinkIOConverter(1)) + val b = Module(new MemPipeIOMemIOConverter(outstanding)) + a.io.tl <> io.tl + b.io.cpu.req_cmd <> Queue(a.io.mem.req_cmd, 2, pipe=true) + b.io.cpu.req_data <> Queue(a.io.mem.req_data, mifDataBeats, pipe=true) + a.io.mem.resp <> b.io.cpu.resp + b.io.mem <> io.mem +} diff --git a/uncore/src/main/scala/metadata.scala b/uncore/src/main/scala/metadata.scala new file mode 100644 index 00000000..b8d4446c --- /dev/null +++ b/uncore/src/main/scala/metadata.scala @@ -0,0 +1,315 @@ +// See LICENSE for license details. + +package uncore +import Chisel._ + +/** Base class to represent coherence information in clients and managers */ +abstract class CoherenceMetadata extends Bundle { + val co = params(TLCoherencePolicy) + val id = params(TLId) +} + +/** Stores the client-side coherence information, + * such as permissions on the data and whether the data is dirty. + * Its API can be used to make TileLink messages in response to + * memory operations or [[uncore.Probe]] messages. + */ +class ClientMetadata extends CoherenceMetadata { + /** Actual state information stored in this bundle */ + val state = UInt(width = co.clientStateWidth) + + /** Metadata equality */ + def ===(rhs: ClientMetadata): Bool = this.state === rhs.state + def !=(rhs: ClientMetadata): Bool = !this.===(rhs) + + /** Is the block's data present in this cache */ + def isValid(dummy: Int = 0): Bool = co.isValid(this) + /** Does this cache have permissions on this block sufficient to perform op */ + def isHit(op_code: UInt): Bool = co.isHit(op_code, this) + /** Does this cache lack permissions on this block sufficient to perform op */ + def isMiss(op_code: UInt): Bool = !co.isHit(op_code, this) + /** Does a secondary miss on the block require another Acquire message */ + def requiresAcquireOnSecondaryMiss(first_op: UInt, second_op: UInt): Bool = + co.requiresAcquireOnSecondaryMiss(first_op, second_op, this) + /** Does op require a Release to be made to outer memory */ + def requiresReleaseOnCacheControl(op_code: UInt): Bool = + co.requiresReleaseOnCacheControl(op_code: UInt, this) + /** Does an eviction require a Release to be made to outer memory */ + def requiresVoluntaryWriteback(dummy: Int = 0): Bool = + co.requiresReleaseOnCacheControl(M_FLUSH, this) + + /** Constructs an Acquire message based on this metdata and a memory operation + * + * @param client_xact_id client's transaction id + * @param addr_block address of the cache block + * @param op_code a memory operation from [[uncore.constants.MemoryOpConstants]] + */ + def makeAcquire( + client_xact_id: UInt, + addr_block: UInt, + op_code: UInt): Acquire = { + Bundle(Acquire( + is_builtin_type = Bool(false), + a_type = co.getAcquireType(op_code, this), + client_xact_id = client_xact_id, + addr_block = addr_block, + union = Cat(op_code, Bool(true))), + { case TLId => id }) + } + + /** Constructs a Release message based on this metadata on an eviction + * + * @param client_xact_id client's transaction id + * @param addr_block address of the cache block + * @param addr_beat sub-block address (which beat) + * @param data data being written back + */ + def makeVoluntaryWriteback( + client_xact_id: UInt, + addr_block: UInt, + addr_beat: UInt = UInt(0), + data: UInt = UInt(0)): Release = { + Bundle(Release( + voluntary = Bool(true), + r_type = co.getReleaseType(M_FLUSH, this), + client_xact_id = client_xact_id, + addr_block = addr_block, + addr_beat = addr_beat, + data = data), { case TLId => id }) + } + + /** Constructs a Release message based on this metadata and a [[uncore.Probe]] + * + * @param the incoming [[uncore.Probe]] + * @param addr_beat sub-block address (which beat) + * @param data data being released + */ + def makeRelease( + prb: Probe, + addr_beat: UInt = UInt(0), + data: UInt = UInt(0)): Release = { + Bundle(Release( + voluntary = Bool(false), + r_type = co.getReleaseType(prb, this), + client_xact_id = UInt(0), + addr_block = prb.addr_block, + addr_beat = addr_beat, + data = data), { case TLId => id }) + } + + /** New metadata after receiving a [[uncore.Grant]] + * + * @param incoming the incoming [[uncore.Grant]] + * @param pending the mem op that triggered this transaction + */ + def onGrant(incoming: Grant, pending: UInt): ClientMetadata = + Bundle(co.clientMetadataOnGrant(incoming, pending, this), { case TLId => id }) + + /** New metadata after receiving a [[uncore.Probe]] + * + * @param incoming the incoming [[uncore.Probe]] + */ + def onProbe(incoming: Probe): ClientMetadata = + Bundle(co.clientMetadataOnProbe(incoming, this), { case TLId => id }) + + /** New metadata after a op_code hits this block + * + * @param op_code a memory operation from [[uncore.constants.MemoryOpConstants]] + */ + def onHit(op_code: UInt): ClientMetadata = + Bundle(co.clientMetadataOnHit(op_code, this), { case TLId => id }) + + /** New metadata after receiving a [[uncore.Probe]] + * + * @param op_code a memory operation from [[uncore.constants.MemoryOpConstants]] + */ + def onCacheControl(op_code: UInt): ClientMetadata = + Bundle(co.clientMetadataOnCacheControl(op_code, this), { case TLId => id }) +} + +/** Factories for ClientMetadata, including on reset */ +object ClientMetadata { + def apply(state: UInt) = { + val meta = new ClientMetadata + meta.state := state + meta + } + def onReset = new ClientMetadata().co.clientMetadataOnReset +} + +/** Stores manager-side information about the status + * of a cache block, including whether it has any known sharers. + * + * Its API can be used to create [[uncore.Probe]] and [[uncore.Grant]] messages. + */ +class ManagerMetadata extends CoherenceMetadata { + // Currently no coherence policies assume manager-side state information + // val state = UInt(width = co.masterStateWidth) TODO: Fix 0-width wires in Chisel + + /** The directory information for this block */ + val sharers = UInt(width = co.dir.width) + + /** Metadata equality */ + def ===(rhs: ManagerMetadata): Bool = //this.state === rhs.state && TODO: Fix 0-width wires in Chisel + this.sharers === rhs.sharers + def !=(rhs: ManagerMetadata): Bool = !this.===(rhs) + + /** Converts the directory info into an N-hot sharer bitvector (i.e. full representation) */ + def full(dummy: Int = 0): UInt = co.dir.full(this.sharers) + + /** Does this [[uncore.Acquire]] require [[uncore.Probe Probes]] to be sent */ + def requiresProbes(acq: Acquire): Bool = co.requiresProbes(acq, this) + /** Does this memory op require [[uncore.Probe Probes]] to be sent */ + def requiresProbes(op_code: UInt): Bool = co.requiresProbes(op_code, this) + /** Does an eviction require [[uncore.Probe Probes]] to be sent */ + def requiresProbesOnVoluntaryWriteback(dummy: Int = 0): Bool = + co.requiresProbes(M_FLUSH, this) + + /** Construct an appropriate [[uncore.ProbeToDst]] for a given [[uncore.Acquire]] + * + * @param dst Destination client id for this Probe + * @param acq Acquire message triggering this Probe + */ + def makeProbe(dst: UInt, acq: Acquire): ProbeToDst = + Bundle(Probe(dst, co.getProbeType(acq, this), acq.addr_block), { case TLId => id }) + + /** Construct an appropriate [[uncore.ProbeToDst]] for a given mem op + * + * @param dst Destination client id for this Probe + * @param op_code memory operation triggering this Probe + * @param addr_block address of the cache block being probed + */ + def makeProbe(dst: UInt, op_code: UInt, addr_block: UInt): ProbeToDst = + Bundle(Probe(dst, co.getProbeType(op_code, this), addr_block), { case TLId => id }) + + /** Construct an appropriate [[uncore.ProbeToDst]] for an eviction + * + * @param dst Destination client id for this Probe + * @param addr_block address of the cache block being probed prior to eviction + */ + def makeProbeForVoluntaryWriteback(dst: UInt, addr_block: UInt): ProbeToDst = + makeProbe(dst, M_FLUSH, addr_block) + + /** Construct an appropriate [[uncore.GrantToDst]] to acknowledge an [[uncore.Release]] + * + * @param rel Release message being acknowledged by this Grant + * @param manager_xact_id manager's transaction id + */ + def makeGrant(rel: ReleaseFromSrc, manager_xact_id: UInt): GrantToDst = { + Bundle(Grant( + dst = rel.client_id, + is_builtin_type = Bool(true), + g_type = Grant.voluntaryAckType, + client_xact_id = rel.client_xact_id, + manager_xact_id = manager_xact_id), { case TLId => id }) + } + + /** Construct an appropriate [[uncore.GrantToDst]] to respond to an [[uncore.Acquire]] + * + * May contain single or multiple beats of data, or just be a permissions upgrade. + * + * @param acq Acquire message being responded to by this Grant + * @param manager_xact_id manager's transaction id + * @param addr_beat beat id of the data + * @param data data being refilled to the original requestor + */ + def makeGrant( + acq: AcquireFromSrc, + manager_xact_id: UInt, + addr_beat: UInt = UInt(0), + data: UInt = UInt(0)): GrantToDst = { + Bundle(Grant( + dst = acq.client_id, + is_builtin_type = acq.isBuiltInType(), + g_type = Mux(acq.isBuiltInType(), + acq.getBuiltInGrantType(), + co.getGrantType(acq, this)), + client_xact_id = acq.client_xact_id, + manager_xact_id = manager_xact_id, + addr_beat = addr_beat, + data = data), { case TLId => id }) + } + + /** Construct an [[uncore.GrantToDst]] to respond to an [[uncore.Acquire]] with some overrides + * + * Used to respond to secondary misses merged into this transaction. + * May contain single or multiple beats of data. + * + * @param pri Primary miss's Acquire message, used to get g_type and dst + * @param sec Secondary miss info, used to get beat and client_xact_id + * @param manager_xact_id manager's transaction id + * @param data data being refilled to the original requestor + */ + def makeGrant( + pri: AcquireFromSrc, + sec: SecondaryMissInfo, + manager_xact_id: UInt, + data: UInt): GrantToDst = { + val g = makeGrant(pri, manager_xact_id, sec.addr_beat, data) + g.client_xact_id := sec.client_xact_id + g + } + + /** New metadata after receiving a [[uncore.ReleaseFromSrc]] + * + * @param incoming the incoming [[uncore.ReleaseFromSrc]] + */ + def onRelease(incoming: ReleaseFromSrc): ManagerMetadata = + Bundle(co.managerMetadataOnRelease(incoming, incoming.client_id, this), { case TLId => id }) + + /** New metadata after sending a [[uncore.GrantToDst]] + * + * @param outgoing the outgoing [[uncore.GrantToDst]] + */ + def onGrant(outgoing: GrantToDst): ManagerMetadata = + Bundle(co.managerMetadataOnGrant(outgoing, outgoing.client_id, this), { case TLId => id }) +} + +/** Factories for ManagerMetadata, including on reset */ +object ManagerMetadata { + def apply(sharers: UInt, state: UInt = UInt(width = 0)) = { + val meta = new ManagerMetadata + //meta.state := state TODO: Fix 0-width wires in Chisel + meta.sharers := sharers + meta + } + def apply() = { + val meta = new ManagerMetadata + //meta.state := UInt(width = 0) TODO: Fix 0-width wires in Chisel + meta.sharers := meta.co.dir.flush + meta + } + def onReset = new ManagerMetadata().co.managerMetadataOnReset +} + +/** HierarchicalMetadata is used in a cache in a multi-level memory hierarchy + * that is a manager with respect to some inner caches and a client with + * respect to some outer cache. + * + * This class makes use of two different sets of TileLink parameters, which are + * applied by contextually mapping [[uncore.TLId]] to one of + * [[uncore.InnerTLId]] or [[uncore.OuterTLId]]. + */ +class HierarchicalMetadata extends CoherenceMetadata { + val inner: ManagerMetadata = Bundle(new ManagerMetadata, {case TLId => params(InnerTLId)}) + val outer: ClientMetadata = Bundle(new ClientMetadata, {case TLId => params(OuterTLId)}) + def ===(rhs: HierarchicalMetadata): Bool = + this.inner === rhs.inner && this.outer === rhs.outer + def !=(rhs: HierarchicalMetadata): Bool = !this.===(rhs) +} + +/** Factories for HierarchicalMetadata, including on reset */ +object HierarchicalMetadata { + def apply(inner: ManagerMetadata, outer: ClientMetadata): HierarchicalMetadata = { + val m = new HierarchicalMetadata + m.inner := inner + m.outer := outer + m + } + def onReset: HierarchicalMetadata = apply(ManagerMetadata.onReset, ClientMetadata.onReset) +} + +/** Identifies the TLId of the inner network in a hierarchical cache controller */ +case object InnerTLId extends Field[String] +/** Identifies the TLId of the outer network in a hierarchical cache controller */ +case object OuterTLId extends Field[String] diff --git a/uncore/src/main/scala/network.scala b/uncore/src/main/scala/network.scala new file mode 100644 index 00000000..4b00091d --- /dev/null +++ b/uncore/src/main/scala/network.scala @@ -0,0 +1,104 @@ +// See LICENSE for license details. + +package uncore +import Chisel._ + +case object LNEndpoints extends Field[Int] +case object LNHeaderBits extends Field[Int] + +class PhysicalHeader(n: Int) extends Bundle { + val src = UInt(width = log2Up(n)) + val dst = UInt(width = log2Up(n)) +} + +class PhysicalNetworkIO[T <: Data](n: Int, dType: T) extends Bundle { + val header = new PhysicalHeader(n) + val payload = dType.clone + override def clone = new PhysicalNetworkIO(n,dType).asInstanceOf[this.type] +} + +class BasicCrossbarIO[T <: Data](n: Int, dType: T) extends Bundle { + val in = Vec.fill(n){Decoupled(new PhysicalNetworkIO(n,dType))}.flip + val out = Vec.fill(n){Decoupled(new PhysicalNetworkIO(n,dType))} +} + +abstract class PhysicalNetwork extends Module + +class BasicCrossbar[T <: Data](n: Int, dType: T, count: Int = 1, needsLock: Option[PhysicalNetworkIO[T] => Bool] = None) extends PhysicalNetwork { + val io = new BasicCrossbarIO(n, dType) + + val rdyVecs = List.fill(n){Vec.fill(n)(Bool())} + + io.out.zip(rdyVecs).zipWithIndex.map{ case ((out, rdys), i) => { + val rrarb = Module(new LockingRRArbiter(io.in(0).bits, n, count, needsLock)) + (rrarb.io.in, io.in, rdys).zipped.map{ case (arb, in, rdy) => { + arb.valid := in.valid && (in.bits.header.dst === UInt(i)) + arb.bits := in.bits + rdy := arb.ready && (in.bits.header.dst === UInt(i)) + }} + out <> rrarb.io.out + }} + for(i <- 0 until n) { + io.in(i).ready := rdyVecs.map(r => r(i)).reduceLeft(_||_) + } +} + +abstract class LogicalNetwork extends Module + +class LogicalHeader extends Bundle { + val src = UInt(width = params(LNHeaderBits)) + val dst = UInt(width = params(LNHeaderBits)) +} + +class LogicalNetworkIO[T <: Data](dType: T) extends Bundle { + val header = new LogicalHeader + val payload = dType.clone + override def clone = { new LogicalNetworkIO(dType).asInstanceOf[this.type] } +} + +object DecoupledLogicalNetworkIOWrapper { + def apply[T <: Data]( + in: DecoupledIO[T], + src: UInt = UInt(0), + dst: UInt = UInt(0)): DecoupledIO[LogicalNetworkIO[T]] = { + val out = Decoupled(new LogicalNetworkIO(in.bits.clone)).asDirectionless + out.valid := in.valid + out.bits.payload := in.bits + out.bits.header.dst := dst + out.bits.header.src := src + in.ready := out.ready + out + } +} + +object DecoupledLogicalNetworkIOUnwrapper { + def apply[T <: Data](in: DecoupledIO[LogicalNetworkIO[T]]): DecoupledIO[T] = { + val out = Decoupled(in.bits.payload.clone).asDirectionless + out.valid := in.valid + out.bits := in.bits.payload + in.ready := out.ready + out + } +} + +object DefaultFromPhysicalShim { + def apply[T <: Data](in: DecoupledIO[PhysicalNetworkIO[T]]): DecoupledIO[LogicalNetworkIO[T]] = { + val out = Decoupled(new LogicalNetworkIO(in.bits.payload)).asDirectionless + out.bits.header := in.bits.header + out.bits.payload := in.bits.payload + out.valid := in.valid + in.ready := out.ready + out + } +} + +object DefaultToPhysicalShim { + def apply[T <: Data](n: Int, in: DecoupledIO[LogicalNetworkIO[T]]): DecoupledIO[PhysicalNetworkIO[T]] = { + val out = Decoupled(new PhysicalNetworkIO(n, in.bits.payload)).asDirectionless + out.bits.header := in.bits.header + out.bits.payload := in.bits.payload + out.valid := in.valid + in.ready := out.ready + out + } +} diff --git a/uncore/src/main/scala/package.scala b/uncore/src/main/scala/package.scala new file mode 100644 index 00000000..2c6c4a5f --- /dev/null +++ b/uncore/src/main/scala/package.scala @@ -0,0 +1,6 @@ +// See LICENSE for license details. + +package object uncore extends uncore.constants.MemoryOpConstants +{ + implicit def toOption[A](a: A) = Option(a) +} diff --git a/uncore/src/main/scala/slowio.scala b/uncore/src/main/scala/slowio.scala new file mode 100644 index 00000000..95ca34e6 --- /dev/null +++ b/uncore/src/main/scala/slowio.scala @@ -0,0 +1,70 @@ +// See LICENSE for license details. + +package uncore +import Chisel._ + +class SlowIO[T <: Data](val divisor_max: Int)(data: => T) extends Module +{ + val io = new Bundle { + val out_fast = Decoupled(data).flip + val out_slow = Decoupled(data) + val in_fast = Decoupled(data) + val in_slow = Decoupled(data).flip + val clk_slow = Bool(OUTPUT) + val set_divisor = Valid(Bits(width = 32)).flip + val divisor = Bits(OUTPUT, 32) + } + + require(divisor_max >= 8 && divisor_max <= 65536 && isPow2(divisor_max)) + val divisor = Reg(init=UInt(divisor_max-1)) + val d_shadow = Reg(init=UInt(divisor_max-1)) + val hold = Reg(init=UInt(divisor_max/4-1)) + val h_shadow = Reg(init=UInt(divisor_max/4-1)) + when (io.set_divisor.valid) { + d_shadow := io.set_divisor.bits(log2Up(divisor_max)-1, 0).toUInt + h_shadow := io.set_divisor.bits(log2Up(divisor_max)-1+16, 16).toUInt + } + io.divisor := hold << UInt(16) | divisor + + val count = Reg{UInt(width = log2Up(divisor_max))} + val myclock = Reg{Bool()} + count := count + UInt(1) + + val rising = count === (divisor >> UInt(1)) + val falling = count === divisor + val held = count === (divisor >> UInt(1)) + hold + + when (falling) { + divisor := d_shadow + hold := h_shadow + count := UInt(0) + myclock := Bool(false) + } + when (rising) { + myclock := Bool(true) + } + + val in_slow_rdy = Reg(init=Bool(false)) + val out_slow_val = Reg(init=Bool(false)) + val out_slow_bits = Reg(data) + + val fromhost_q = Module(new Queue(data,1)) + fromhost_q.io.enq.valid := rising && (io.in_slow.valid && in_slow_rdy || this.reset) + fromhost_q.io.enq.bits := io.in_slow.bits + fromhost_q.io.deq <> io.in_fast + + val tohost_q = Module(new Queue(data,1)) + tohost_q.io.enq <> io.out_fast + tohost_q.io.deq.ready := rising && io.out_slow.ready && out_slow_val + + when (held) { + in_slow_rdy := fromhost_q.io.enq.ready + out_slow_val := tohost_q.io.deq.valid + out_slow_bits := Mux(this.reset, fromhost_q.io.deq.bits, tohost_q.io.deq.bits) + } + + io.in_slow.ready := in_slow_rdy + io.out_slow.valid := out_slow_val + io.out_slow.bits := out_slow_bits + io.clk_slow := myclock +} diff --git a/uncore/src/main/scala/tilelink.scala b/uncore/src/main/scala/tilelink.scala new file mode 100644 index 00000000..306c8731 --- /dev/null +++ b/uncore/src/main/scala/tilelink.scala @@ -0,0 +1,1221 @@ +// See LICENSE for license details. + +package uncore +import Chisel._ +import scala.math.max + +/** Parameters exposed to the top-level design, set based on + * external requirements or design space exploration + */ +/** Unique name per TileLink network*/ +case object TLId extends Field[String] +/** Coherency policy used to define custom mesage types */ +case object TLCoherencePolicy extends Field[CoherencePolicy] +/** Number of manager agents */ +case object TLNManagers extends Field[Int] +/** Number of client agents */ +case object TLNClients extends Field[Int] +/** Number of client agents that cache data and use custom [[uncore.Acquire]] types */ +case object TLNCachingClients extends Field[Int] +/** Number of client agents that do not cache data and use built-in [[uncore.Acquire]] types */ +case object TLNCachelessClients extends Field[Int] +/** Maximum number of unique outstanding transactions per client */ +case object TLMaxClientXacts extends Field[Int] +/** Maximum number of clients multiplexed onto a single port */ +case object TLMaxClientsPerPort extends Field[Int] +/** Maximum number of unique outstanding transactions per manager */ +case object TLMaxManagerXacts extends Field[Int] +/** Width of cache block addresses */ +case object TLBlockAddrBits extends Field[Int] +/** Width of data beats */ +case object TLDataBits extends Field[Int] +/** Number of data beats per cache block */ +case object TLDataBeats extends Field[Int] +/** Whether the underlying physical network preserved point-to-point ordering of messages */ +case object TLNetworkIsOrderedP2P extends Field[Boolean] + +/** Utility trait for building Modules and Bundles that use TileLink parameters */ +trait TileLinkParameters extends UsesParameters { + val tlCoh = params(TLCoherencePolicy) + val tlNManagers = params(TLNManagers) + val tlNClients = params(TLNClients) + val tlNCachingClients = params(TLNCachingClients) + val tlNCachelessClients = params(TLNCachelessClients) + val tlClientIdBits = log2Up(tlNClients) + val tlManagerIdBits = log2Up(tlNManagers) + val tlMaxClientXacts = params(TLMaxClientXacts) + val tlMaxClientsPerPort = params(TLMaxClientsPerPort) + val tlMaxManagerXacts = params(TLMaxManagerXacts) + val tlClientXactIdBits = log2Up(tlMaxClientXacts*tlMaxClientsPerPort) + val tlManagerXactIdBits = log2Up(tlMaxManagerXacts) + val tlBlockAddrBits = params(TLBlockAddrBits) + val tlDataBits = params(TLDataBits) + val tlDataBytes = tlDataBits/8 + val tlDataBeats = params(TLDataBeats) + val tlWriteMaskBits = if(tlDataBits/8 < 1) 1 else tlDataBits/8 + val tlBeatAddrBits = log2Up(tlDataBeats) + val tlByteAddrBits = log2Up(tlWriteMaskBits) + val tlMemoryOpcodeBits = M_SZ + val tlMemoryOperandSizeBits = MT_SZ + val tlAcquireTypeBits = max(log2Up(Acquire.nBuiltInTypes), + tlCoh.acquireTypeWidth) + val tlAcquireUnionBits = max(tlWriteMaskBits, + (tlByteAddrBits + + tlMemoryOperandSizeBits + + tlMemoryOpcodeBits)) + 1 + val tlGrantTypeBits = max(log2Up(Grant.nBuiltInTypes), + tlCoh.grantTypeWidth) + 1 + val tlNetworkPreservesPointToPointOrdering = params(TLNetworkIsOrderedP2P) + val tlNetworkDoesNotInterleaveBeats = true + val amoAluOperandBits = params(AmoAluOperandBits) +} + +abstract class TLBundle extends Bundle with TileLinkParameters +abstract class TLModule extends Module with TileLinkParameters + +/** Base trait for all TileLink channels */ +trait TileLinkChannel extends TLBundle { + def hasData(dummy: Int = 0): Bool + def hasMultibeatData(dummy: Int = 0): Bool +} +/** Directionality of message channel. Used to hook up logical network ports to physical network ports */ +trait ClientToManagerChannel extends TileLinkChannel +/** Directionality of message channel. Used to hook up logical network ports to physical network ports */ +trait ManagerToClientChannel extends TileLinkChannel +/** Directionality of message channel. Used to hook up logical network ports to physical network ports */ +trait ClientToClientChannel extends TileLinkChannel // Unused for now + +/** Common signals that are used in multiple channels. + * These traits are useful for type parameterizing bundle wiring functions. + */ + +/** Address of a cache block. */ +trait HasCacheBlockAddress extends TLBundle { + val addr_block = UInt(width = tlBlockAddrBits) + + def conflicts(that: HasCacheBlockAddress) = this.addr_block === that.addr_block + def conflicts(addr: UInt) = this.addr_block === addr +} + +/** Sub-block address or beat id of multi-beat data */ +trait HasTileLinkBeatId extends TLBundle { + val addr_beat = UInt(width = tlBeatAddrBits) +} + +/* Client-side transaction id. Usually Miss Status Handling Register File index */ +trait HasClientTransactionId extends TLBundle { + val client_xact_id = Bits(width = tlClientXactIdBits) +} + +/** Manager-side transaction id. Usually Transaction Status Handling Register File index. */ +trait HasManagerTransactionId extends TLBundle { + val manager_xact_id = Bits(width = tlManagerXactIdBits) +} + +/** A single beat of cache block data */ +trait HasTileLinkData extends HasTileLinkBeatId { + val data = UInt(width = tlDataBits) + + def hasData(dummy: Int = 0): Bool + def hasMultibeatData(dummy: Int = 0): Bool +} + +/** The id of a client source or destination. Used in managers. */ +trait HasClientId extends TLBundle { + val client_id = UInt(width = tlClientIdBits) +} + +/** TileLink channel bundle definitions */ + +/** The Acquire channel is used to intiate coherence protocol transactions in + * order to gain access to a cache block's data with certain permissions + * enabled. Messages sent over this channel may be custom types defined by + * a [[uncore.CoherencePolicy]] for cached data accesse or may be built-in types + * used for uncached data accesses. Acquires may contain data for Put or + * PutAtomic built-in types. After sending an Acquire, clients must + * wait for a manager to send them a [[uncore.Grant]] message in response. + */ +class Acquire extends ClientToManagerChannel + with HasCacheBlockAddress + with HasClientTransactionId + with HasTileLinkData { + // Actual bundle fields: + val is_builtin_type = Bool() + val a_type = UInt(width = tlAcquireTypeBits) + val union = Bits(width = tlAcquireUnionBits) + + // Utility funcs for accessing subblock union: + val opCodeOff = 1 + val opSizeOff = tlMemoryOpcodeBits + opCodeOff + val addrByteOff = tlMemoryOperandSizeBits + opSizeOff + val addrByteMSB = tlByteAddrBits + addrByteOff + /** Hint whether to allocate the block in any interveneing caches */ + def allocate(dummy: Int = 0) = union(0) + /** Op code for [[uncore.PutAtomic]] operations */ + def op_code(dummy: Int = 0) = Mux( + isBuiltInType(Acquire.putType) || isBuiltInType(Acquire.putBlockType), + M_XWR, union(opSizeOff-1, opCodeOff)) + /** Operand size for [[uncore.PutAtomic]] */ + def op_size(dummy: Int = 0) = union(addrByteOff-1, opSizeOff) + /** Byte address for [[uncore.PutAtomic]] operand */ + def addr_byte(dummy: Int = 0) = union(addrByteMSB-1, addrByteOff) + private def amo_offset(dummy: Int = 0) = addr_byte()(tlByteAddrBits-1, log2Up(amoAluOperandBits/8)) + /** Bit offset of [[uncore.PutAtomic]] operand */ + def amo_shift_bits(dummy: Int = 0) = UInt(amoAluOperandBits)*amo_offset() + /** Write mask for [[uncore.Put]], [[uncore.PutBlock]], [[uncore.PutAtomic]] */ + def wmask(dummy: Int = 0) = + Mux(isBuiltInType(Acquire.putAtomicType), + FillInterleaved(amoAluOperandBits/8, UIntToOH(amo_offset())), + Mux(isBuiltInType(Acquire.putBlockType) || isBuiltInType(Acquire.putType), + union(tlWriteMaskBits, 1), + UInt(0, width = tlWriteMaskBits))) + /** Full, beat-sized writemask */ + def full_wmask(dummy: Int = 0) = FillInterleaved(8, wmask()) + /** Complete physical address for block, beat or operand */ + def addr(dummy: Int = 0) = Cat(this.addr_block, this.addr_beat, this.addr_byte()) + + // Other helper functions: + /** Message type equality */ + def is(t: UInt) = a_type === t //TODO: make this more opaque; def ===? + + /** Is this message a built-in or custom type */ + def isBuiltInType(dummy: Int = 0): Bool = is_builtin_type + /** Is this message a particular built-in type */ + def isBuiltInType(t: UInt): Bool = is_builtin_type && a_type === t + + /** Does this message refer to subblock operands using info in the Acquire.union subbundle */ + def isSubBlockType(dummy: Int = 0): Bool = isBuiltInType() && Acquire.typesOnSubBlocks.contains(a_type) + + /** Is this message a built-in prefetch message */ + def isPrefetch(dummy: Int = 0): Bool = isBuiltInType() && is(Acquire.prefetchType) + + /** Does this message contain data? Assumes that no custom message types have data. */ + def hasData(dummy: Int = 0): Bool = isBuiltInType() && Acquire.typesWithData.contains(a_type) + + /** Does this message contain multiple beats of data? Assumes that no custom message types have data. */ + def hasMultibeatData(dummy: Int = 0): Bool = Bool(tlDataBeats > 1) && isBuiltInType() && + Acquire.typesWithMultibeatData.contains(a_type) + + /** Does this message require the manager to probe the client the very client that sent it? + * Needed if multiple caches are attached to the same port. + */ + def requiresSelfProbe(dummy: Int = 0) = Bool(false) + + /** Mapping between each built-in Acquire type (defined in companion object) + * and a built-in Grant type. + */ + def getBuiltInGrantType(dummy: Int = 0): UInt = { + MuxLookup(this.a_type, Grant.putAckType, Array( + Acquire.getType -> Grant.getDataBeatType, + Acquire.getBlockType -> Grant.getDataBlockType, + Acquire.putType -> Grant.putAckType, + Acquire.putBlockType -> Grant.putAckType, + Acquire.putAtomicType -> Grant.getDataBeatType, + Acquire.prefetchType -> Grant.prefetchAckType)) + } +} + +/** [[uncore.Acquire]] with an extra field stating its source id */ +class AcquireFromSrc extends Acquire with HasClientId + +/** Contains definitions of the the built-in Acquire types and a factory + * for [[uncore.Acquire]] + * + * In general you should avoid using this factory directly and use + * [[uncore.ClientMetadata.makeAcquire]] for custom cached Acquires and + * [[uncore.Get]], [[uncore.Put]], etc. for built-in uncached Acquires. + * + * @param is_builtin_type built-in or custom type message? + * @param a_type built-in type enum or custom type enum + * @param client_xact_id client's transaction id + * @param addr_block address of the cache block + * @param addr_beat sub-block address (which beat) + * @param data data being put outwards + * @param union additional fields used for uncached types + */ +object Acquire { + val nBuiltInTypes = 5 + //TODO: Use Enum + def getType = UInt("b000") // Get a single beat of data + def getBlockType = UInt("b001") // Get a whole block of data + def putType = UInt("b010") // Put a single beat of data + def putBlockType = UInt("b011") // Put a whole block of data + def putAtomicType = UInt("b100") // Perform an atomic memory op + def prefetchType = UInt("b101") // Prefetch a whole block of data + def typesWithData = Vec(putType, putBlockType, putAtomicType) + def typesWithMultibeatData = Vec(putBlockType) + def typesOnSubBlocks = Vec(putType, getType, putAtomicType) + + def fullWriteMask = SInt(-1, width = new Acquire().tlWriteMaskBits).toUInt + + // Most generic constructor + def apply( + is_builtin_type: Bool, + a_type: Bits, + client_xact_id: UInt, + addr_block: UInt, + addr_beat: UInt = UInt(0), + data: UInt = UInt(0), + union: UInt = UInt(0)): Acquire = { + val acq = new Acquire + acq.is_builtin_type := is_builtin_type + acq.a_type := a_type + acq.client_xact_id := client_xact_id + acq.addr_block := addr_block + acq.addr_beat := addr_beat + acq.data := data + acq.union := union + acq + } + // Copy constructor + def apply(a: Acquire): Acquire = { + val acq = new Acquire + acq := a + acq + } +} + +/** Get a single beat of data from the outer memory hierarchy + * + * The client can hint whether he block containing this beat should be + * allocated in the intervening levels of the hierarchy. + * + * @param client_xact_id client's transaction id + * @param addr_block address of the cache block + * @param addr_beat sub-block address (which beat) + * @param alloc hint whether the block should be allocated in intervening caches + */ +object Get { + def apply( + client_xact_id: UInt, + addr_block: UInt, + addr_beat: UInt, + alloc: Bool = Bool(true)): Acquire = { + Acquire( + is_builtin_type = Bool(true), + a_type = Acquire.getType, + client_xact_id = client_xact_id, + addr_block = addr_block, + addr_beat = addr_beat, + union = Cat(M_XRD, alloc)) + } +} + +/** Get a whole cache block of data from the outer memory hierarchy + * + * The client can hint whether the block should be allocated in the + * intervening levels of the hierarchy. + * + * @param client_xact_id client's transaction id + * @param addr_block address of the cache block + * @param alloc hint whether the block should be allocated in intervening caches + */ +object GetBlock { + def apply( + client_xact_id: UInt = UInt(0), + addr_block: UInt, + alloc: Bool = Bool(true)): Acquire = { + Acquire( + is_builtin_type = Bool(true), + a_type = Acquire.getBlockType, + client_xact_id = client_xact_id, + addr_block = addr_block, + union = Cat(M_XRD, alloc)) + } +} + +/** Prefetch a cache block into the next-outermost level of the memory hierarchy + * with read permissions. + * + * @param client_xact_id client's transaction id + * @param addr_block address of the cache block + */ +object GetPrefetch { + def apply( + client_xact_id: UInt, + addr_block: UInt): Acquire = { + Acquire( + is_builtin_type = Bool(true), + a_type = Acquire.prefetchType, + client_xact_id = client_xact_id, + addr_block = addr_block, + addr_beat = UInt(0), + union = Cat(M_XRD, Bool(true))) + } +} + +/** Put a single beat of data into the outer memory hierarchy + * + * The block will be allocated in the next-outermost level of the hierarchy. + * + * @param client_xact_id client's transaction id + * @param addr_block address of the cache block + * @param addr_beat sub-block address (which beat) + * @param data data being refilled to the original requestor + * @param wmask per-byte write mask for this beat + */ +object Put { + def apply( + client_xact_id: UInt, + addr_block: UInt, + addr_beat: UInt, + data: UInt, + wmask: UInt = Acquire.fullWriteMask): Acquire = { + Acquire( + is_builtin_type = Bool(true), + a_type = Acquire.putType, + addr_block = addr_block, + addr_beat = addr_beat, + client_xact_id = client_xact_id, + data = data, + union = Cat(wmask, Bool(true))) + } +} + +/** Put a whole cache block of data into the outer memory hierarchy + * + * If the write mask is not full, the block will be allocated in the + * next-outermost level of the hierarchy. If the write mask is full, the + * client can hint whether the block should be allocated or not. + * + * @param client_xact_id client's transaction id + * @param addr_block address of the cache block + * @param addr_beat sub-block address (which beat of several) + * @param data data being refilled to the original requestor + * @param wmask per-byte write mask for this beat + * @param alloc hint whether the block should be allocated in intervening caches + */ +object PutBlock { + def apply( + client_xact_id: UInt, + addr_block: UInt, + addr_beat: UInt, + data: UInt, + wmask: UInt): Acquire = { + Acquire( + is_builtin_type = Bool(true), + a_type = Acquire.putBlockType, + client_xact_id = client_xact_id, + addr_block = addr_block, + addr_beat = addr_beat, + data = data, + union = Cat(wmask, (wmask != Acquire.fullWriteMask))) + } + def apply( + client_xact_id: UInt, + addr_block: UInt, + addr_beat: UInt, + data: UInt, + alloc: Bool = Bool(true)): Acquire = { + Acquire( + is_builtin_type = Bool(true), + a_type = Acquire.putBlockType, + client_xact_id = client_xact_id, + addr_block = addr_block, + addr_beat = addr_beat, + data = data, + union = Cat(Acquire.fullWriteMask, alloc)) + } +} + +/** Prefetch a cache block into the next-outermost level of the memory hierarchy + * with write permissions. + * + * @param client_xact_id client's transaction id + * @param addr_block address of the cache block + */ +object PutPrefetch { + def apply( + client_xact_id: UInt, + addr_block: UInt): Acquire = { + Acquire( + is_builtin_type = Bool(true), + a_type = Acquire.prefetchType, + client_xact_id = client_xact_id, + addr_block = addr_block, + addr_beat = UInt(0), + union = Cat(M_XWR, Bool(true))) + } +} + +/** Perform an atomic memory operation in the next-outermost level of the memory hierarchy + * + * @param client_xact_id client's transaction id + * @param addr_block address of the cache block + * @param addr_beat sub-block address (within which beat) + * @param addr_byte sub-block address (which byte) + * @param atomic_opcode {swap, add, xor, and, min, max, minu, maxu} from [[uncore.MemoryOpConstants]] + * @param operand_size {byte, half, word, double} from [[uncore.MemoryOpConstants]] + * @param data source operand data + */ +object PutAtomic { + def apply( + client_xact_id: UInt, + addr_block: UInt, + addr_beat: UInt, + addr_byte: UInt, + atomic_opcode: UInt, + operand_size: UInt, + data: UInt): Acquire = { + Acquire( + is_builtin_type = Bool(true), + a_type = Acquire.putAtomicType, + client_xact_id = client_xact_id, + addr_block = addr_block, + addr_beat = addr_beat, + data = data, + union = Cat(addr_byte, operand_size, atomic_opcode, Bool(true))) + } +} + +/** The Probe channel is used to force clients to release data or cede permissions + * on a cache block. Clients respond to Probes with [[uncore.Release]] messages. + * The available types of Probes are customized by a particular + * [[uncore.CoherencePolicy]]. + */ +class Probe extends ManagerToClientChannel + with HasCacheBlockAddress { + val p_type = UInt(width = tlCoh.probeTypeWidth) + + def is(t: UInt) = p_type === t + def hasData(dummy: Int = 0) = Bool(false) + def hasMultibeatData(dummy: Int = 0) = Bool(false) +} + +/** [[uncore.Probe]] with an extra field stating its destination id */ +class ProbeToDst extends Probe with HasClientId + +/** Contains factories for [[uncore.Probe]] and [[uncore.ProbeToDst]] + * + * In general you should avoid using these factories directly and use + * [[uncore.ManagerMetadata.makeProbe(UInt,Acquire)*]] instead. + * + * @param dst id of client to which probe should be sent + * @param p_type custom probe type + * @param addr_block address of the cache block + */ +object Probe { + def apply(p_type: UInt, addr_block: UInt): Probe = { + val prb = new Probe + prb.p_type := p_type + prb.addr_block := addr_block + prb + } + def apply(dst: UInt, p_type: UInt, addr_block: UInt): ProbeToDst = { + val prb = new ProbeToDst + prb.client_id := dst + prb.p_type := p_type + prb.addr_block := addr_block + prb + } +} + +/** The Release channel is used to release data or permission back to the manager + * in response to [[uncore.Probe]] messages. It can also be used to voluntarily + * write back data, for example in the event that dirty data must be evicted on + * a cache miss. The available types of Release messages are always customized by + * a particular [[uncore.CoherencePolicy]]. Releases may contain data or may be + * simple acknowledgements. Voluntary Releases are acknowledged with [[uncore.Grant Grants]]. + */ +class Release extends ClientToManagerChannel + with HasCacheBlockAddress + with HasClientTransactionId + with HasTileLinkData { + val r_type = UInt(width = tlCoh.releaseTypeWidth) + val voluntary = Bool() + + // Helper funcs + def is(t: UInt) = r_type === t + def hasData(dummy: Int = 0) = tlCoh.releaseTypesWithData.contains(r_type) + //TODO: Assumes all releases write back full cache blocks: + def hasMultibeatData(dummy: Int = 0) = Bool(tlDataBeats > 1) && tlCoh.releaseTypesWithData.contains(r_type) + def isVoluntary(dummy: Int = 0) = voluntary + def requiresAck(dummy: Int = 0) = !Bool(tlNetworkPreservesPointToPointOrdering) +} + +/** [[uncore.Release]] with an extra field stating its source id */ +class ReleaseFromSrc extends Release with HasClientId + +/** Contains a [[uncore.Release]] factory + * + * In general you should avoid using this factory directly and use + * [[uncore.ClientMetadata.makeRelease]] instead. + * + * @param voluntary is this a voluntary writeback + * @param r_type type enum defined by coherence protocol + * @param client_xact_id client's transaction id + * @param addr_block address of the cache block + * @param addr_beat beat id of the data + * @param data data being written back + */ +object Release { + def apply( + voluntary: Bool, + r_type: UInt, + client_xact_id: UInt, + addr_block: UInt, + addr_beat: UInt = UInt(0), + data: UInt = UInt(0)): Release = { + val rel = new Release + rel.r_type := r_type + rel.client_xact_id := client_xact_id + rel.addr_block := addr_block + rel.addr_beat := addr_beat + rel.data := data + rel.voluntary := voluntary + rel + } +} + +/** The Grant channel is used to refill data or grant permissions requested of the + * manager agent via an [[uncore.Acquire]] message. It is also used to acknowledge + * the receipt of voluntary writeback from clients in the form of [[uncore.Release]] + * messages. There are built-in Grant messages used for Gets and Puts, and + * coherence policies may also define custom Grant types. Grants may contain data + * or may be simple acknowledgements. Grants are responded to with [[uncore.Finish]]. + */ +class Grant extends ManagerToClientChannel + with HasTileLinkData + with HasClientTransactionId + with HasManagerTransactionId { + val is_builtin_type = Bool() + val g_type = UInt(width = tlGrantTypeBits) + + // Helper funcs + def isBuiltInType(dummy: Int = 0): Bool = is_builtin_type + def isBuiltInType(t: UInt): Bool = is_builtin_type && g_type === t + def is(t: UInt):Bool = g_type === t + def hasData(dummy: Int = 0): Bool = Mux(isBuiltInType(), + Grant.typesWithData.contains(g_type), + tlCoh.grantTypesWithData.contains(g_type)) + def hasMultibeatData(dummy: Int = 0): Bool = + Bool(tlDataBeats > 1) && Mux(isBuiltInType(), + Grant.typesWithMultibeatData.contains(g_type), + tlCoh.grantTypesWithData.contains(g_type)) + def isVoluntary(dummy: Int = 0): Bool = isBuiltInType() && (g_type === Grant.voluntaryAckType) + def requiresAck(dummy: Int = 0): Bool = !Bool(tlNetworkPreservesPointToPointOrdering) && !isVoluntary() + def makeFinish(dummy: Int = 0): Finish = { + val f = Bundle(new Finish, { case TLMaxManagerXacts => tlMaxManagerXacts }) + f.manager_xact_id := this.manager_xact_id + f + } +} + +/** [[uncore.Grant]] with an extra field stating its destination */ +class GrantToDst extends Grant with HasClientId + +/** Contains definitions of the the built-in grant types and factories + * for [[uncore.Grant]] and [[uncore.GrantToDst]] + * + * In general you should avoid using these factories directly and use + * [[uncore.ManagerMetadata.makeGrant(uncore.AcquireFromSrc* makeGrant]] instead. + * + * @param dst id of client to which grant should be sent + * @param is_builtin_type built-in or custom type message? + * @param g_type built-in type enum or custom type enum + * @param client_xact_id client's transaction id + * @param manager_xact_id manager's transaction id + * @param addr_beat beat id of the data + * @param data data being refilled to the original requestor + */ +object Grant { + val nBuiltInTypes = 5 + def voluntaryAckType = UInt("b000") // For acking Releases + def prefetchAckType = UInt("b001") // For acking any kind of Prefetch + def putAckType = UInt("b011") // For acking any kind of non-prfetch Put + def getDataBeatType = UInt("b100") // Supplying a single beat of Get + def getDataBlockType = UInt("b101") // Supplying all beats of a GetBlock + def typesWithData = Vec(getDataBlockType, getDataBeatType) + def typesWithMultibeatData= Vec(getDataBlockType) + + def apply( + is_builtin_type: Bool, + g_type: UInt, + client_xact_id: UInt, + manager_xact_id: UInt, + addr_beat: UInt, + data: UInt): Grant = { + val gnt = new Grant + gnt.is_builtin_type := is_builtin_type + gnt.g_type := g_type + gnt.client_xact_id := client_xact_id + gnt.manager_xact_id := manager_xact_id + gnt.addr_beat := addr_beat + gnt.data := data + gnt + } + + def apply( + dst: UInt, + is_builtin_type: Bool, + g_type: UInt, + client_xact_id: UInt, + manager_xact_id: UInt, + addr_beat: UInt = UInt(0), + data: UInt = UInt(0)): GrantToDst = { + val gnt = new GrantToDst + gnt.client_id := dst + gnt.is_builtin_type := is_builtin_type + gnt.g_type := g_type + gnt.client_xact_id := client_xact_id + gnt.manager_xact_id := manager_xact_id + gnt.addr_beat := addr_beat + gnt.data := data + gnt + } +} + +/** The Finish channel is used to provide a global ordering of transactions + * in networks that do not guarantee point-to-point ordering of messages. + * A Finsish message is sent as acknowledgement of receipt of a [[uncore.Grant]]. + * When a Finish message is received, a manager knows it is safe to begin + * processing other transactions that touch the same cache block. + */ +class Finish extends ClientToManagerChannel with HasManagerTransactionId { + def hasData(dummy: Int = 0) = Bool(false) + def hasMultibeatData(dummy: Int = 0) = Bool(false) +} + +/** Complete IO definition for incoherent TileLink, including networking headers */ +class UncachedTileLinkIO extends TLBundle { + val acquire = new DecoupledIO(new LogicalNetworkIO(new Acquire)) + val grant = new DecoupledIO(new LogicalNetworkIO(new Grant)).flip + val finish = new DecoupledIO(new LogicalNetworkIO(new Finish)) +} + +/** Complete IO definition for coherent TileLink, including networking headers */ +class TileLinkIO extends UncachedTileLinkIO { + val probe = new DecoupledIO(new LogicalNetworkIO(new Probe)).flip + val release = new DecoupledIO(new LogicalNetworkIO(new Release)) +} + +/** This version of UncachedTileLinkIO does not contain network headers. + * It is intended for use within client agents. + * + * Headers are provided in the top-level that instantiates the clients and network, + * probably using a [[uncore.ClientTileLinkNetworkPort]] module. + * By eliding the header subbundles within the clients we can enable + * hierarchical P-and-R while minimizing unconnected port errors in GDS. + * + * Secondly, this version of the interface elides [[uncore.Finish]] messages, with the + * assumption that a [[uncore.FinishUnit]] has been coupled to the TileLinkIO port + * to deal with acking received [[uncore.Grant Grants]]. + */ +class ClientUncachedTileLinkIO extends TLBundle { + val acquire = new DecoupledIO(new Acquire) + val grant = new DecoupledIO(new Grant).flip +} + +/** This version of TileLinkIO does not contain network headers. + * It is intended for use within client agents. + */ +class ClientTileLinkIO extends ClientUncachedTileLinkIO { + val probe = new DecoupledIO(new Probe).flip + val release = new DecoupledIO(new Release) +} + +/** This version of TileLinkIO does not contain network headers, but + * every channel does include an extra client_id subbundle. + * It is intended for use within Management agents. + * + * Managers need to track where [[uncore.Acquire]] and [[uncore.Release]] messages + * originated so that they can send a [[uncore.Grant]] to the right place. + * Similarly they must be able to issues Probes to particular clients. + * However, we'd still prefer to have [[uncore.ManagerTileLinkNetworkPort]] fill in + * the header.src to enable hierarchical p-and-r of the managers. Additionally, + * coherent clients might be mapped to random network port ids, and we'll leave it to the + * [[uncore.ManagerTileLinkNetworkPort]] to apply the correct mapping. Managers do need to + * see Finished so they know when to allow new transactions on a cache + * block to proceed. + */ +class ManagerTileLinkIO extends TLBundle { + val acquire = new DecoupledIO(new AcquireFromSrc).flip + val grant = new DecoupledIO(new GrantToDst) + val finish = new DecoupledIO(new Finish).flip + val probe = new DecoupledIO(new ProbeToDst) + val release = new DecoupledIO(new ReleaseFromSrc).flip +} + +/** Utilities for safely wrapping a *UncachedTileLink by pinning probe.ready and release.valid low */ +object TileLinkIOWrapper { + def apply(utl: ClientUncachedTileLinkIO, p: Parameters): ClientTileLinkIO = { + val conv = Module(new ClientTileLinkIOWrapper)(p) + conv.io.in <> utl + conv.io.out + } + def apply(utl: ClientUncachedTileLinkIO): ClientTileLinkIO = { + val conv = Module(new ClientTileLinkIOWrapper) + conv.io.in <> utl + conv.io.out + } + def apply(tl: ClientTileLinkIO): ClientTileLinkIO = tl + def apply(utl: UncachedTileLinkIO, p: Parameters): TileLinkIO = { + val conv = Module(new TileLinkIOWrapper)(p) + conv.io.in <> utl + conv.io.out + } + def apply(utl: UncachedTileLinkIO): TileLinkIO = { + val conv = Module(new TileLinkIOWrapper) + conv.io.in <> utl + conv.io.out + } + def apply(tl: TileLinkIO): TileLinkIO = tl +} + +class TileLinkIOWrapper extends TLModule { + val io = new Bundle { + val in = new UncachedTileLinkIO().flip + val out = new TileLinkIO + } + io.out.acquire <> io.in.acquire + io.out.grant <> io.in.grant + io.out.finish <> io.in.finish + io.out.probe.ready := Bool(true) + io.out.release.valid := Bool(false) +} + +class ClientTileLinkIOWrapper extends TLModule { + val io = new Bundle { + val in = new ClientUncachedTileLinkIO().flip + val out = new ClientTileLinkIO + } + io.out.acquire <> io.in.acquire + io.out.grant <> io.in.grant + io.out.probe.ready := Bool(true) + io.out.release.valid := Bool(false) +} + +/** Used to track metadata for transactions where multiple secondary misses have been merged + * and handled by a single transaction tracker. + */ +class SecondaryMissInfo extends TLBundle // TODO: add a_type to merge e.g. Get+GetBlocks, and/or HasClientId + with HasTileLinkBeatId + with HasClientTransactionId + +/** A helper module that automatically issues [[uncore.Finish]] messages in repsonse + * to [[uncore.Grant]] that it receives from a manager and forwards to a client + */ +class FinishUnit(srcId: Int = 0, outstanding: Int = 2) extends TLModule with HasDataBeatCounters { + val io = new Bundle { + val grant = Decoupled(new LogicalNetworkIO(new Grant)).flip + val refill = Decoupled(new Grant) + val finish = Decoupled(new LogicalNetworkIO(new Finish)) + val ready = Bool(OUTPUT) + } + + val g = io.grant.bits.payload + + if(tlNetworkPreservesPointToPointOrdering) { + io.finish.valid := Bool(false) + io.refill.valid := io.grant.valid + io.refill.bits := g + io.grant.ready := io.refill.ready + io.ready := Bool(true) + } else { + // We only want to send Finishes after we have collected all beats of + // a multibeat Grant. But Grants from multiple managers or transactions may + // get interleaved, so we could need a counter for each. + val done = if(tlNetworkDoesNotInterleaveBeats) { + connectIncomingDataBeatCounterWithHeader(io.grant) + } else { + val entries = 1 << tlClientXactIdBits + def getId(g: LogicalNetworkIO[Grant]) = g.payload.client_xact_id + assert(getId(io.grant.bits) <= UInt(entries), "Not enough grant beat counters, only " + entries + " entries.") + connectIncomingDataBeatCountersWithHeader(io.grant, entries, getId).reduce(_||_) + } + val q = Module(new FinishQueue(outstanding)) + q.io.enq.valid := io.grant.fire() && g.requiresAck() && (!g.hasMultibeatData() || done) + q.io.enq.bits.fin := g.makeFinish() + q.io.enq.bits.dst := io.grant.bits.header.src + + io.finish.bits.header.src := UInt(srcId) + io.finish.bits.header.dst := q.io.deq.bits.dst + io.finish.bits.payload := q.io.deq.bits.fin + io.finish.valid := q.io.deq.valid + q.io.deq.ready := io.finish.ready + + io.refill.valid := io.grant.valid + io.refill.bits := g + io.grant.ready := (q.io.enq.ready || !g.requiresAck()) && io.refill.ready + io.ready := q.io.enq.ready + } +} + +class FinishQueueEntry extends TLBundle { + val fin = new Finish + val dst = UInt(width = log2Up(params(LNEndpoints))) +} + +class FinishQueue(entries: Int) extends Queue(new FinishQueueEntry, entries) + +/** A port to convert [[uncore.ClientTileLinkIO]].flip into [[uncore.TileLinkIO]] + * + * Creates network headers for [[uncore.Acquire]] and [[uncore.Release]] messages, + * calculating header.dst and filling in header.src. + * Strips headers from [[uncore.Probe Probes]]. + * Responds to [[uncore.Grant]] by automatically issuing [[uncore.Finish]] to the granting managers. + * + * @param clientId network port id of this agent + * @param addrConvert how a physical address maps to a destination manager port id + */ +class ClientTileLinkNetworkPort(clientId: Int, addrConvert: UInt => UInt) extends TLModule { + val io = new Bundle { + val client = new ClientTileLinkIO().flip + val network = new TileLinkIO + } + + val finisher = Module(new FinishUnit(clientId)) + finisher.io.grant <> io.network.grant + io.network.finish <> finisher.io.finish + + val acq_with_header = ClientTileLinkHeaderCreator(io.client.acquire, clientId, addrConvert) + val rel_with_header = ClientTileLinkHeaderCreator(io.client.release, clientId, addrConvert) + val prb_without_header = DecoupledLogicalNetworkIOUnwrapper(io.network.probe) + val gnt_without_header = finisher.io.refill + + io.network.acquire.bits := acq_with_header.bits + io.network.acquire.valid := acq_with_header.valid && finisher.io.ready + acq_with_header.ready := io.network.acquire.ready && finisher.io.ready + io.network.release <> rel_with_header + io.client.probe <> prb_without_header + io.client.grant <> gnt_without_header +} + +object ClientTileLinkHeaderCreator { + def apply[T <: ClientToManagerChannel with HasCacheBlockAddress]( + in: DecoupledIO[T], + clientId: Int, + addrConvert: UInt => UInt): DecoupledIO[LogicalNetworkIO[T]] = { + val out = new DecoupledIO(new LogicalNetworkIO(in.bits.clone)).asDirectionless + out.bits.payload := in.bits + out.bits.header.src := UInt(clientId) + out.bits.header.dst := addrConvert(in.bits.addr_block) + out.valid := in.valid + in.ready := out.ready + out + } +} + +/** A port to convert [[uncore.ManagerTileLinkIO]].flip into [[uncore.TileLinkIO]].flip + * + * Creates network headers for [[uncore.Probe]] and [[uncore.Grant]] messagess, + * calculating header.dst and filling in header.src. + * Strips headers from [[uncore.Acquire]], [[uncore.Release]] and [[uncore.Finish]], + * but supplies client_id instead. + * + * @param managerId the network port id of this agent + * @param idConvert how a sharer id maps to a destination client port id + */ +class ManagerTileLinkNetworkPort(managerId: Int, idConvert: UInt => UInt) extends TLModule { + val io = new Bundle { + val manager = new ManagerTileLinkIO().flip + val network = new TileLinkIO().flip + } + io.network.grant <> ManagerTileLinkHeaderCreator(io.manager.grant, managerId, (u: UInt) => u) + io.network.probe <> ManagerTileLinkHeaderCreator(io.manager.probe, managerId, idConvert) + io.manager.acquire.bits.client_id := io.network.acquire.bits.header.src + io.manager.acquire <> DecoupledLogicalNetworkIOUnwrapper(io.network.acquire) + io.manager.release.bits.client_id := io.network.release.bits.header.src + io.manager.release <> DecoupledLogicalNetworkIOUnwrapper(io.network.release) + io.manager.finish <> DecoupledLogicalNetworkIOUnwrapper(io.network.finish) +} + +object ManagerTileLinkHeaderCreator { + def apply[T <: ManagerToClientChannel with HasClientId]( + in: DecoupledIO[T], + managerId: Int, + idConvert: UInt => UInt): DecoupledIO[LogicalNetworkIO[T]] = { + val out = new DecoupledIO(new LogicalNetworkIO(in.bits.clone)).asDirectionless + out.bits.payload := in.bits + out.bits.header.src := UInt(managerId) + out.bits.header.dst := idConvert(in.bits.client_id) + out.valid := in.valid + in.ready := out.ready + out + } +} + +/** Struct for describing per-channel queue depths */ +case class TileLinkDepths(acq: Int, prb: Int, rel: Int, gnt: Int, fin: Int) + +/** Optionally enqueues each [[uncore.TileLinkChannel]] individually */ +class TileLinkEnqueuer(depths: TileLinkDepths) extends Module { + val io = new Bundle { + val client = new TileLinkIO().flip + val manager = new TileLinkIO + } + io.manager.acquire <> (if(depths.acq > 0) Queue(io.client.acquire, depths.acq) else io.client.acquire) + io.client.probe <> (if(depths.prb > 0) Queue(io.manager.probe, depths.prb) else io.manager.probe) + io.manager.release <> (if(depths.rel > 0) Queue(io.client.release, depths.rel) else io.client.release) + io.client.grant <> (if(depths.gnt > 0) Queue(io.manager.grant, depths.gnt) else io.manager.grant) + io.manager.finish <> (if(depths.fin > 0) Queue(io.client.finish, depths.fin) else io.client.finish) +} + +object TileLinkEnqueuer { + def apply(in: TileLinkIO, depths: TileLinkDepths)(p: Parameters): TileLinkIO = { + val t = Module(new TileLinkEnqueuer(depths))(p) + t.io.client <> in + t.io.manager + } + def apply(in: TileLinkIO, depth: Int)(p: Parameters): TileLinkIO = { + apply(in, TileLinkDepths(depth, depth, depth, depth, depth))(p) + } +} + +/** Utility functions for constructing TileLinkIO arbiters */ +trait TileLinkArbiterLike extends TileLinkParameters { + // Some shorthand type variables + type ManagerSourcedWithId = ManagerToClientChannel with HasClientTransactionId + type ClientSourcedWithId = ClientToManagerChannel with HasClientTransactionId + type ClientSourcedWithIdAndData = ClientToManagerChannel with HasClientTransactionId with HasTileLinkData + + val arbN: Int // The number of ports on the client side + + // These abstract funcs are filled in depending on whether the arbiter mucks with the + // outgoing client ids to track sourcing and then needs to revert them on the way back + def clientSourcedClientXactId(in: ClientSourcedWithId, id: Int): Bits + def managerSourcedClientXactId(in: ManagerSourcedWithId): Bits + def arbIdx(in: ManagerSourcedWithId): UInt + + // The following functions are all wiring helpers for each of the different types of TileLink channels + + def hookupClientSource[M <: ClientSourcedWithIdAndData]( + clts: Seq[DecoupledIO[LogicalNetworkIO[M]]], + mngr: DecoupledIO[LogicalNetworkIO[M]]) { + def hasData(m: LogicalNetworkIO[M]) = m.payload.hasMultibeatData() + val arb = Module(new LockingRRArbiter(mngr.bits.clone, arbN, tlDataBeats, Some(hasData _))) + clts.zipWithIndex.zip(arb.io.in).map{ case ((req, id), arb) => { + arb.valid := req.valid + arb.bits := req.bits + arb.bits.payload.client_xact_id := clientSourcedClientXactId(req.bits.payload, id) + req.ready := arb.ready + }} + arb.io.out <> mngr + } + + def hookupClientSourceHeaderless[M <: ClientSourcedWithIdAndData]( + clts: Seq[DecoupledIO[M]], + mngr: DecoupledIO[M]) { + def hasData(m: M) = m.hasMultibeatData() + val arb = Module(new LockingRRArbiter(mngr.bits.clone, arbN, tlDataBeats, Some(hasData _))) + clts.zipWithIndex.zip(arb.io.in).map{ case ((req, id), arb) => { + arb.valid := req.valid + arb.bits := req.bits + arb.bits.client_xact_id := clientSourcedClientXactId(req.bits, id) + req.ready := arb.ready + }} + arb.io.out <> mngr + } + + def hookupManagerSourceWithHeader[M <: ManagerToClientChannel]( + clts: Seq[DecoupledIO[LogicalNetworkIO[M]]], + mngr: DecoupledIO[LogicalNetworkIO[M]]) { + mngr.ready := Bool(false) + for (i <- 0 until arbN) { + clts(i).valid := Bool(false) + when (mngr.bits.header.dst === UInt(i)) { + clts(i).valid := mngr.valid + mngr.ready := clts(i).ready + } + clts(i).bits := mngr.bits + } + } + + def hookupManagerSourceWithId[M <: ManagerSourcedWithId]( + clts: Seq[DecoupledIO[LogicalNetworkIO[M]]], + mngr: DecoupledIO[LogicalNetworkIO[M]]) { + mngr.ready := Bool(false) + for (i <- 0 until arbN) { + clts(i).valid := Bool(false) + when (arbIdx(mngr.bits.payload) === UInt(i)) { + clts(i).valid := mngr.valid + mngr.ready := clts(i).ready + } + clts(i).bits := mngr.bits + clts(i).bits.payload.client_xact_id := managerSourcedClientXactId(mngr.bits.payload) + } + } + + def hookupManagerSourceHeaderlessWithId[M <: ManagerSourcedWithId]( + clts: Seq[DecoupledIO[M]], + mngr: DecoupledIO[M]) { + mngr.ready := Bool(false) + for (i <- 0 until arbN) { + clts(i).valid := Bool(false) + when (arbIdx(mngr.bits) === UInt(i)) { + clts(i).valid := mngr.valid + mngr.ready := clts(i).ready + } + clts(i).bits := mngr.bits + clts(i).bits.client_xact_id := managerSourcedClientXactId(mngr.bits) + } + } + + def hookupManagerSourceBroadcast[M <: Data](clts: Seq[DecoupledIO[M]], mngr: DecoupledIO[M]) { + clts.map{ _.valid := mngr.valid } + clts.map{ _.bits := mngr.bits } + mngr.ready := clts.map(_.ready).reduce(_&&_) + } + + def hookupFinish[M <: LogicalNetworkIO[Finish]]( clts: Seq[DecoupledIO[M]], mngr: DecoupledIO[M]) { + val arb = Module(new RRArbiter(mngr.bits.clone, arbN)) + arb.io.in <> clts + arb.io.out <> mngr + } +} + +/** Abstract base case for any Arbiters that have UncachedTileLinkIOs */ +abstract class UncachedTileLinkIOArbiter(val arbN: Int) extends Module with TileLinkArbiterLike { + val io = new Bundle { + val in = Vec.fill(arbN){new UncachedTileLinkIO}.flip + val out = new UncachedTileLinkIO + } + hookupClientSource(io.in.map(_.acquire), io.out.acquire) + hookupFinish(io.in.map(_.finish), io.out.finish) + hookupManagerSourceWithId(io.in.map(_.grant), io.out.grant) +} + +/** Abstract base case for any Arbiters that have cached TileLinkIOs */ +abstract class TileLinkIOArbiter(val arbN: Int) extends Module with TileLinkArbiterLike { + val io = new Bundle { + val in = Vec.fill(arbN){new TileLinkIO}.flip + val out = new TileLinkIO + } + hookupClientSource(io.in.map(_.acquire), io.out.acquire) + hookupClientSource(io.in.map(_.release), io.out.release) + hookupFinish(io.in.map(_.finish), io.out.finish) + hookupManagerSourceBroadcast(io.in.map(_.probe), io.out.probe) + hookupManagerSourceWithId(io.in.map(_.grant), io.out.grant) +} + +/** Appends the port index of the arbiter to the client_xact_id */ +trait AppendsArbiterId extends TileLinkArbiterLike { + def clientSourcedClientXactId(in: ClientSourcedWithId, id: Int) = + Cat(in.client_xact_id, UInt(id, log2Up(arbN))) + def managerSourcedClientXactId(in: ManagerSourcedWithId) = + in.client_xact_id >> UInt(log2Up(arbN)) + def arbIdx(in: ManagerSourcedWithId) = in.client_xact_id(log2Up(arbN)-1,0).toUInt +} + +/** Uses the client_xact_id as is (assumes it has been set to port index) */ +trait PassesId extends TileLinkArbiterLike { + def clientSourcedClientXactId(in: ClientSourcedWithId, id: Int) = in.client_xact_id + def managerSourcedClientXactId(in: ManagerSourcedWithId) = in.client_xact_id + def arbIdx(in: ManagerSourcedWithId) = in.client_xact_id +} + +/** Overwrites some default client_xact_id with the port idx */ +trait UsesNewId extends TileLinkArbiterLike { + def clientSourcedClientXactId(in: ClientSourcedWithId, id: Int) = UInt(id, log2Up(arbN)) + def managerSourcedClientXactId(in: ManagerSourcedWithId) = UInt(0) + def arbIdx(in: ManagerSourcedWithId) = in.client_xact_id +} + +// Now we can mix-in thevarious id-generation traits to make concrete arbiter classes +class UncachedTileLinkIOArbiterThatAppendsArbiterId(val n: Int) extends UncachedTileLinkIOArbiter(n) with AppendsArbiterId +class UncachedTileLinkIOArbiterThatPassesId(val n: Int) extends UncachedTileLinkIOArbiter(n) with PassesId +class UncachedTileLinkIOArbiterThatUsesNewId(val n: Int) extends UncachedTileLinkIOArbiter(n) with UsesNewId +class TileLinkIOArbiterThatAppendsArbiterId(val n: Int) extends TileLinkIOArbiter(n) with AppendsArbiterId +class TileLinkIOArbiterThatPassesId(val n: Int) extends TileLinkIOArbiter(n) with PassesId +class TileLinkIOArbiterThatUsesNewId(val n: Int) extends TileLinkIOArbiter(n) with UsesNewId + +/** Concrete uncached client-side arbiter that appends the arbiter's port id to client_xact_id */ +class ClientUncachedTileLinkIOArbiter(val arbN: Int) extends Module with TileLinkArbiterLike with AppendsArbiterId { + val io = new Bundle { + val in = Vec.fill(arbN){new ClientUncachedTileLinkIO}.flip + val out = new ClientUncachedTileLinkIO + } + hookupClientSourceHeaderless(io.in.map(_.acquire), io.out.acquire) + hookupManagerSourceHeaderlessWithId(io.in.map(_.grant), io.out.grant) +} + +/** Concrete client-side arbiter that appends the arbiter's port id to client_xact_id */ +class ClientTileLinkIOArbiter(val arbN: Int) extends Module with TileLinkArbiterLike with AppendsArbiterId { + val io = new Bundle { + val in = Vec.fill(arbN){new ClientTileLinkIO}.flip + val out = new ClientTileLinkIO + } + hookupClientSourceHeaderless(io.in.map(_.acquire), io.out.acquire) + hookupClientSourceHeaderless(io.in.map(_.release), io.out.release) + hookupManagerSourceBroadcast(io.in.map(_.probe), io.out.probe) + hookupManagerSourceHeaderlessWithId(io.in.map(_.grant), io.out.grant) +} + +/** Utility trait containing wiring functions to keep track of how many data beats have + * been sent or recieved over a particular [[uncore.TileLinkChannel]] or pair of channels. + * + * Won't count message types that don't have data. + * Used in [[uncore.XactTracker]] and [[uncore.FinishUnit]]. + */ +trait HasDataBeatCounters { + type HasBeat = TileLinkChannel with HasTileLinkBeatId + + /** Returns the current count on this channel and when a message is done + * @param inc increment the counter (usually .valid or .fire()) + * @param data the actual channel data + * @param beat count to return for single-beat messages + */ + def connectDataBeatCounter[S <: TileLinkChannel](inc: Bool, data: S, beat: UInt) = { + val multi = data.hasMultibeatData() + val (multi_cnt, multi_done) = Counter(inc && multi, data.tlDataBeats) + val cnt = Mux(multi, multi_cnt, beat) + val done = Mux(multi, multi_done, inc) + (cnt, done) + } + + /** Counter for beats on outgoing [[chisel.DecoupledIO]] */ + def connectOutgoingDataBeatCounter[T <: TileLinkChannel](in: DecoupledIO[T], beat: UInt = UInt(0)): (UInt, Bool) = + connectDataBeatCounter(in.fire(), in.bits, beat) + + /** Returns done but not cnt. Use the addr_beat subbundle instead of cnt for beats on + * incoming channels in case of network reordering. + */ + def connectIncomingDataBeatCounter[T <: TileLinkChannel](in: DecoupledIO[T]): Bool = + connectDataBeatCounter(in.fire(), in.bits, UInt(0))._2 + + /** Counter for beats on incoming DecoupledIO[LogicalNetworkIO[]]s returns done */ + def connectIncomingDataBeatCounterWithHeader[T <: TileLinkChannel](in: DecoupledIO[LogicalNetworkIO[T]]): Bool = + connectDataBeatCounter(in.fire(), in.bits.payload, UInt(0))._2 + + /** If the network might interleave beats from different messages, we need a Vec of counters, + * one for every outstanding message id that might be interleaved. + * + * @param getId mapping from Message to counter id + */ + def connectIncomingDataBeatCountersWithHeader[T <: TileLinkChannel with HasClientTransactionId]( + in: DecoupledIO[LogicalNetworkIO[T]], + entries: Int, + getId: LogicalNetworkIO[T] => UInt): Vec[Bool] = { + Vec((0 until entries).map { i => + connectDataBeatCounter(in.fire() && getId(in.bits) === UInt(i), in.bits.payload, UInt(0))._2 + }) + } + + /** Provides counters on two channels, as well a meta-counter that tracks how many + * messages have been sent over the up channel but not yet responded to over the down channel + * + * @param max max number of outstanding ups with no down + * @param up outgoing channel + * @param down incoming channel + * @param beat overrides cnts on single-beat messages + * @param track whether up's message should be tracked + * @return a tuple containing whether their are outstanding messages, up's count, + * up's done, down's count, down's done + */ + def connectTwoWayBeatCounter[T <: TileLinkChannel, S <: TileLinkChannel]( + max: Int, + up: DecoupledIO[T], + down: DecoupledIO[S], + beat: UInt = UInt(0), + track: T => Bool = (t: T) => Bool(true)): (Bool, UInt, Bool, UInt, Bool) = { + val cnt = Reg(init = UInt(0, width = log2Up(max+1))) + val (up_idx, up_done) = connectDataBeatCounter(up.fire(), up.bits, beat) + val (down_idx, down_done) = connectDataBeatCounter(down.fire(), down.bits, beat) + val do_inc = up_done && track(up.bits) + val do_dec = down_done + cnt := Mux(do_dec, + Mux(do_inc, cnt, cnt - UInt(1)), + Mux(do_inc, cnt + UInt(1), cnt)) + (cnt > UInt(0), up_idx, up_done, down_idx, down_done) + } +} diff --git a/uncore/src/main/scala/uncore.scala b/uncore/src/main/scala/uncore.scala new file mode 100644 index 00000000..d7573aec --- /dev/null +++ b/uncore/src/main/scala/uncore.scala @@ -0,0 +1,129 @@ +// See LICENSE for license details. + +package uncore +import Chisel._ + +case object NReleaseTransactors extends Field[Int] +case object NProbeTransactors extends Field[Int] +case object NAcquireTransactors extends Field[Int] + +trait CoherenceAgentParameters extends UsesParameters { + val nReleaseTransactors = 1 + val nAcquireTransactors = params(NAcquireTransactors) + val nTransactors = nReleaseTransactors + nAcquireTransactors + def outerTLParams = params.alterPartial({ case TLId => params(OuterTLId)}) + val outerDataBeats = outerTLParams(TLDataBeats) + val outerDataBits = outerTLParams(TLDataBits) + val outerBeatAddrBits = log2Up(outerDataBeats) + val outerByteAddrBits = log2Up(outerDataBits/8) + def innerTLParams = params.alterPartial({case TLId => params(InnerTLId)}) + val innerDataBeats = innerTLParams(TLDataBeats) + val innerDataBits = innerTLParams(TLDataBits) + val innerBeatAddrBits = log2Up(innerDataBeats) + val innerByteAddrBits = log2Up(innerDataBits/8) + require(outerDataBeats == innerDataBeats) //TODO: must fix all xact_data Vecs to remove this requirement +} + +abstract class CoherenceAgentBundle extends Bundle with CoherenceAgentParameters +abstract class CoherenceAgentModule extends Module with CoherenceAgentParameters + +trait HasCoherenceAgentWiringHelpers { + def doOutputArbitration[T <: TileLinkChannel]( + out: DecoupledIO[T], + ins: Seq[DecoupledIO[T]]) { + def lock(o: T) = o.hasMultibeatData() + val arb = Module(new LockingRRArbiter(out.bits.clone, ins.size, out.bits.tlDataBeats, lock _)) + out <> arb.io.out + arb.io.in <> ins + } + + def doInputRouting[T <: HasManagerTransactionId]( + in: DecoupledIO[T], + outs: Seq[DecoupledIO[T]]) { + val idx = in.bits.manager_xact_id + outs.map(_.bits := in.bits) + outs.zipWithIndex.map { case (o,i) => o.valid := in.valid && idx === UInt(i) } + in.ready := Vec(outs.map(_.ready)).read(idx) + } +} + +trait HasInnerTLIO extends CoherenceAgentBundle { + val inner = Bundle(new ManagerTileLinkIO)(innerTLParams) + val incoherent = Vec.fill(inner.tlNCachingClients){Bool()}.asInput + def iacq(dummy: Int = 0) = inner.acquire.bits + def iprb(dummy: Int = 0) = inner.probe.bits + def irel(dummy: Int = 0) = inner.release.bits + def ignt(dummy: Int = 0) = inner.grant.bits + def ifin(dummy: Int = 0) = inner.finish.bits +} + +trait HasUncachedOuterTLIO extends CoherenceAgentBundle { + val outer = Bundle(new ClientUncachedTileLinkIO)(outerTLParams) + def oacq(dummy: Int = 0) = outer.acquire.bits + def ognt(dummy: Int = 0) = outer.grant.bits +} + +trait HasCachedOuterTLIO extends CoherenceAgentBundle { + val outer = Bundle(new ClientTileLinkIO)(outerTLParams) + def oacq(dummy: Int = 0) = outer.acquire.bits + def oprb(dummy: Int = 0) = outer.probe.bits + def orel(dummy: Int = 0) = outer.release.bits + def ognt(dummy: Int = 0) = outer.grant.bits +} + +class ManagerTLIO extends HasInnerTLIO with HasUncachedOuterTLIO + +abstract class CoherenceAgent extends CoherenceAgentModule { + def innerTL: ManagerTileLinkIO + def outerTL: ClientTileLinkIO + def incoherent: Vec[Bool] +} + +abstract class ManagerCoherenceAgent extends CoherenceAgent + with HasCoherenceAgentWiringHelpers { + val io = new ManagerTLIO + def innerTL = io.inner + def outerTL = TileLinkIOWrapper(io.outer, outerTLParams) + def incoherent = io.incoherent +} + +class HierarchicalTLIO extends HasInnerTLIO with HasCachedOuterTLIO + +abstract class HierarchicalCoherenceAgent extends CoherenceAgent { + val io = new HierarchicalTLIO + def innerTL = io.inner + def outerTL = io.outer + def incoherent = io.incoherent +} + +trait HasTrackerConflictIO extends Bundle { + val has_acquire_conflict = Bool(OUTPUT) + val has_acquire_match = Bool(OUTPUT) + val has_release_match = Bool(OUTPUT) +} + +class ManagerXactTrackerIO extends ManagerTLIO with HasTrackerConflictIO +class HierarchicalXactTrackerIO extends HierarchicalTLIO with HasTrackerConflictIO + +abstract class XactTracker extends CoherenceAgentModule with HasDataBeatCounters { + def addPendingBitWhenBeat[T <: HasBeat](inc: Bool, in: T): UInt = + Fill(in.tlDataBeats, inc) & UIntToOH(in.addr_beat) + def dropPendingBitWhenBeat[T <: HasBeat](dec: Bool, in: T): UInt = + ~Fill(in.tlDataBeats, dec) | ~UIntToOH(in.addr_beat) + + def addPendingBitWhenBeatHasData[T <: HasBeat](in: DecoupledIO[T]): UInt = + addPendingBitWhenBeat(in.fire() && in.bits.hasData(), in.bits) + + def addPendingBitWhenBeatIsGetOrAtomic(in: DecoupledIO[AcquireFromSrc]): UInt = { + val a = in.bits + val isGetOrAtomic = a.isBuiltInType() && + (Vec(Acquire.getType, Acquire.getBlockType, Acquire.putAtomicType).contains(a.a_type)) + addPendingBitWhenBeat(in.fire() && isGetOrAtomic, a) + } + + def dropPendingBitWhenBeatHasData[T <: HasBeat](in: DecoupledIO[T]): UInt = + dropPendingBitWhenBeat(in.fire() && in.bits.hasData(), in.bits) + + def dropPendingBitAtDest(in: DecoupledIO[ProbeToDst]): UInt = + ~Fill(in.bits.tlNCachingClients, in.fire()) | ~UIntToOH(in.bits.client_id) +} diff --git a/uncore/src/main/scala/util.scala b/uncore/src/main/scala/util.scala new file mode 100644 index 00000000..65c5d6cd --- /dev/null +++ b/uncore/src/main/scala/util.scala @@ -0,0 +1,106 @@ +// See LICENSE for license details. + +package uncore + +import Chisel._ +import scala.math._ + +class Unsigned(x: Int) { + require(x >= 0) + def clog2: Int = { require(x > 0); ceil(log(x)/log(2)).toInt } + def log2: Int = { require(x > 0); floor(log(x)/log(2)).toInt } + def isPow2: Boolean = x > 0 && (x & (x-1)) == 0 + def nextPow2: Int = if (x == 0) 1 else 1 << clog2 +} + +object MuxBundle { + def apply[T <: Data] (default: T, mapping: Seq[(Bool, T)]): T = { + mapping.reverse.foldLeft(default)((b, a) => Mux(a._1, a._2, b)) + } +} + +// Produces 0-width value when counting to 1 +class ZCounter(val n: Int) { + val value = Reg(init=UInt(0, log2Ceil(n))) + def inc(): Bool = { + if (n == 1) Bool(true) + else { + val wrap = value === UInt(n-1) + value := Mux(Bool(!isPow2(n)) && wrap, UInt(0), value + UInt(1)) + wrap + } + } +} + +object ZCounter { + def apply(n: Int) = new ZCounter(n) + def apply(cond: Bool, n: Int): (UInt, Bool) = { + val c = new ZCounter(n) + var wrap: Bool = null + when (cond) { wrap = c.inc() } + (c.value, cond && wrap) + } +} + +class FlowThroughSerializer[T <: HasTileLinkData](gen: T, n: Int) extends Module { + val io = new Bundle { + val in = Decoupled(gen.clone).flip + val out = Decoupled(gen.clone) + val cnt = UInt(OUTPUT, log2Up(n)) + val done = Bool(OUTPUT) + } + val narrowWidth = io.in.bits.data.getWidth / n + require(io.in.bits.data.getWidth % narrowWidth == 0) + + if(n == 1) { + io.in <> io.out + io.cnt := UInt(width = 0) + io.done := Bool(true) + } else { + val cnt = Reg(init=UInt(0, width = log2Up(n))) + val wrap = cnt === UInt(n-1) + val rbits = Reg(io.in.bits.clone) + val active = Reg(init=Bool(false)) + + val shifter = Vec.fill(n){Bits(width = narrowWidth)} + (0 until n).foreach { + i => shifter(i) := rbits.data((i+1)*narrowWidth-1,i*narrowWidth) + } + + io.done := Bool(false) + io.cnt := cnt + io.in.ready := !active + io.out.valid := active || io.in.valid + io.out.bits := io.in.bits + when(!active && io.in.valid) { + when(io.in.bits.hasData()) { + cnt := Mux(io.out.ready, UInt(1), UInt(0)) + rbits := io.in.bits + active := Bool(true) + } + io.done := !io.in.bits.hasData() + } + when(active) { + io.out.bits := rbits + io.out.bits.data := shifter(cnt) + when(io.out.ready) { + cnt := cnt + UInt(1) + when(wrap) { + cnt := UInt(0) + io.done := Bool(true) + active := Bool(false) + } + } + } + } +} + +object FlowThroughSerializer { + def apply[T <: HasTileLinkData](in: DecoupledIO[T], n: Int): DecoupledIO[T] = { + val fs = Module(new FlowThroughSerializer(in.bits, n)) + fs.io.in.valid := in.valid + fs.io.in.bits := in.bits + in.ready := fs.io.in.ready + fs.io.out + } +} From 8832b454ce9f4d1d0f77d9737484f492ff7fe689 Mon Sep 17 00:00:00 2001 From: Henry Cook Date: Wed, 29 Apr 2015 15:34:56 -0700 Subject: [PATCH 2/2] add plugins to make scala doc site and publish to ghpages --- uncore/build.sbt | 2 ++ uncore/project/plugins.sbt | 5 +++++ 2 files changed, 7 insertions(+) create mode 100644 uncore/project/plugins.sbt diff --git a/uncore/build.sbt b/uncore/build.sbt index d78d02ca..35b7dcac 100644 --- a/uncore/build.sbt +++ b/uncore/build.sbt @@ -8,6 +8,8 @@ scalaVersion := "2.10.2" site.settings +site.includeScaladoc() + ghpages.settings git.remoteRepo := "git@github.com:ucb-bar/uncore.git" diff --git a/uncore/project/plugins.sbt b/uncore/project/plugins.sbt new file mode 100644 index 00000000..4f4825c4 --- /dev/null +++ b/uncore/project/plugins.sbt @@ -0,0 +1,5 @@ +resolvers += "jgit-repo" at "http://download.eclipse.org/jgit/maven" + +addSbtPlugin("com.typesafe.sbt" % "sbt-ghpages" % "0.5.3") + +addSbtPlugin("com.typesafe.sbt" % "sbt-site" % "0.8.1")