Merge pull request #536 from ucb-bar/diplomacy-star-nodes
diplomacy: add :*= and :=* to support flexible # of edges
This commit is contained in:
		| @@ -11,17 +11,14 @@ import util._ | |||||||
|  |  | ||||||
| class DefaultCoreplex(implicit p: Parameters) extends BaseCoreplex | class DefaultCoreplex(implicit p: Parameters) extends BaseCoreplex | ||||||
|     with CoreplexRISCVPlatform |     with CoreplexRISCVPlatform | ||||||
|     with HasL2MasterPort |  | ||||||
|     with HasRocketTiles { |     with HasRocketTiles { | ||||||
|   override lazy val module = new DefaultCoreplexModule(this, () => new DefaultCoreplexBundle(this)) |   override lazy val module = new DefaultCoreplexModule(this, () => new DefaultCoreplexBundle(this)) | ||||||
| } | } | ||||||
|  |  | ||||||
| class DefaultCoreplexBundle[+L <: DefaultCoreplex](_outer: L) extends BaseCoreplexBundle(_outer) | class DefaultCoreplexBundle[+L <: DefaultCoreplex](_outer: L) extends BaseCoreplexBundle(_outer) | ||||||
|     with CoreplexRISCVPlatformBundle |     with CoreplexRISCVPlatformBundle | ||||||
|     with HasL2MasterPortBundle |  | ||||||
|     with HasRocketTilesBundle |     with HasRocketTilesBundle | ||||||
|  |  | ||||||
| class DefaultCoreplexModule[+L <: DefaultCoreplex, +B <: DefaultCoreplexBundle[L]](_outer: L, _io: () => B) extends BaseCoreplexModule(_outer, _io) | class DefaultCoreplexModule[+L <: DefaultCoreplex, +B <: DefaultCoreplexBundle[L]](_outer: L, _io: () => B) extends BaseCoreplexModule(_outer, _io) | ||||||
|     with CoreplexRISCVPlatformModule |     with CoreplexRISCVPlatformModule | ||||||
|     with HasL2MasterPortModule |  | ||||||
|     with HasRocketTilesModule |     with HasRocketTilesModule | ||||||
|   | |||||||
| @@ -24,9 +24,13 @@ trait CoreplexNetwork extends HasCoreplexParameters { | |||||||
|  |  | ||||||
|   val mmio = TLOutputNode() |   val mmio = TLOutputNode() | ||||||
|   val mmioInt = IntInputNode() |   val mmioInt = IntInputNode() | ||||||
|  |   val l2in = TLInputNode() | ||||||
|  |  | ||||||
|   intBar.intnode := mmioInt |   intBar.intnode := mmioInt | ||||||
|  |  | ||||||
|  |   // Allows a variable number of inputs from outside to the Xbar | ||||||
|  |   l1tol2.node :=* l2in | ||||||
|  |  | ||||||
|   cbus.node := |   cbus.node := | ||||||
|     TLBuffer()( |     TLBuffer()( | ||||||
|     TLAtomicAutomata(arithmetic = true)( // disable once TLB uses TL2 metadata |     TLAtomicAutomata(arithmetic = true)( // disable once TLB uses TL2 metadata | ||||||
| @@ -43,6 +47,7 @@ trait CoreplexNetworkBundle extends HasCoreplexParameters { | |||||||
|  |  | ||||||
|   val mmio = outer.mmio.bundleOut |   val mmio = outer.mmio.bundleOut | ||||||
|   val interrupts = outer.mmioInt.bundleIn |   val interrupts = outer.mmioInt.bundleIn | ||||||
|  |   val l2in = outer.l2in.bundleIn | ||||||
| } | } | ||||||
|  |  | ||||||
| trait CoreplexNetworkModule extends HasCoreplexParameters { | trait CoreplexNetworkModule extends HasCoreplexParameters { | ||||||
| @@ -93,21 +98,3 @@ trait BankedL2CoherenceManagersModule extends CoreplexNetworkModule { | |||||||
|   val outer: BankedL2CoherenceManagers |   val outer: BankedL2CoherenceManagers | ||||||
|   val io: BankedL2CoherenceManagersBundle |   val io: BankedL2CoherenceManagersBundle | ||||||
| } | } | ||||||
|  |  | ||||||
| ///// |  | ||||||
|  |  | ||||||
| trait HasL2MasterPort extends CoreplexNetwork { |  | ||||||
|   val module: HasL2MasterPortModule |  | ||||||
|   val l2in = TLInputNode() |  | ||||||
|   l1tol2.node := TLBuffer()(l2in) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| trait HasL2MasterPortBundle extends CoreplexNetworkBundle { |  | ||||||
|   val outer: HasL2MasterPort |  | ||||||
|   val l2in = outer.l2in.bundleIn |  | ||||||
| } |  | ||||||
|  |  | ||||||
| trait HasL2MasterPortModule extends CoreplexNetworkModule { |  | ||||||
|   val outer: HasL2MasterPort |  | ||||||
|   val io: HasL2MasterPortBundle |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -16,7 +16,9 @@ trait InwardNodeImp[DI, UI, EI, BI <: Data] | |||||||
|   def edgeI(pd: DI, pu: UI): EI |   def edgeI(pd: DI, pu: UI): EI | ||||||
|   def bundleI(ei: Seq[EI]): Vec[BI] |   def bundleI(ei: Seq[EI]): Vec[BI] | ||||||
|   def colour: String |   def colour: String | ||||||
|   def connect(bo: => BI, bi: => BI, e: => EI)(implicit p: Parameters, sourceInfo: SourceInfo): (Option[LazyModule], () => Unit) |   def connect(bindings: () => Seq[(EI, BI, BI)])(implicit p: Parameters, sourceInfo: SourceInfo): (Option[LazyModule], () => Unit) = { | ||||||
|  |     (None, () => bindings().foreach { case (_, i, o) => i <> o }) | ||||||
|  |   } | ||||||
|  |  | ||||||
|   // optional methods to track node graph |   // optional methods to track node graph | ||||||
|   def mixI(pu: UI, node: InwardNode[DI, UI, BI]): UI = pu // insert node into parameters |   def mixI(pu: UI, node: InwardNode[DI, UI, BI]): UI = pu // insert node into parameters | ||||||
| @@ -71,8 +73,17 @@ trait InwardNodeHandle[DI, UI, BI <: Data] | |||||||
|   val inward: InwardNode[DI, UI, BI] |   val inward: InwardNode[DI, UI, BI] | ||||||
|   def := (h: OutwardNodeHandle[DI, UI, BI])(implicit p: Parameters, sourceInfo: SourceInfo): Option[LazyModule] = |   def := (h: OutwardNodeHandle[DI, UI, BI])(implicit p: Parameters, sourceInfo: SourceInfo): Option[LazyModule] = | ||||||
|     inward.:=(h)(p, sourceInfo) |     inward.:=(h)(p, sourceInfo) | ||||||
|  |   def :*= (h: OutwardNodeHandle[DI, UI, BI])(implicit p: Parameters, sourceInfo: SourceInfo): Option[LazyModule] = | ||||||
|  |     inward.:*=(h)(p, sourceInfo) | ||||||
|  |   def :=* (h: OutwardNodeHandle[DI, UI, BI])(implicit p: Parameters, sourceInfo: SourceInfo): Option[LazyModule] = | ||||||
|  |     inward.:=*(h)(p, sourceInfo) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | sealed trait NodeBinding | ||||||
|  | case object BIND_ONCE  extends NodeBinding | ||||||
|  | case object BIND_QUERY extends NodeBinding | ||||||
|  | case object BIND_STAR  extends NodeBinding | ||||||
|  |  | ||||||
| trait InwardNode[DI, UI, BI <: Data] extends BaseNode with InwardNodeHandle[DI, UI, BI] | trait InwardNode[DI, UI, BI <: Data] extends BaseNode with InwardNodeHandle[DI, UI, BI] | ||||||
| { | { | ||||||
|   val inward = this |   val inward = this | ||||||
| @@ -81,21 +92,22 @@ trait InwardNode[DI, UI, BI <: Data] extends BaseNode with InwardNodeHandle[DI, | |||||||
|   require (!numPI.isEmpty, s"No number of inputs would be acceptable to ${name}${lazyModule.line}") |   require (!numPI.isEmpty, s"No number of inputs would be acceptable to ${name}${lazyModule.line}") | ||||||
|   require (numPI.start >= 0, s"${name} accepts a negative number of inputs${lazyModule.line}") |   require (numPI.start >= 0, s"${name} accepts a negative number of inputs${lazyModule.line}") | ||||||
|  |  | ||||||
|   private val accPI = ListBuffer[(Int, OutwardNode[DI, UI, BI])]() |   private val accPI = ListBuffer[(Int, OutwardNode[DI, UI, BI], NodeBinding)]() | ||||||
|   private var iRealized = false |   private var iRealized = false | ||||||
|  |  | ||||||
|   protected[diplomacy] def iPushed = accPI.size |   protected[diplomacy] def iPushed = accPI.size | ||||||
|   protected[diplomacy] def iPush(index: Int, node: OutwardNode[DI, UI, BI])(implicit sourceInfo: SourceInfo) { |   protected[diplomacy] def iPush(index: Int, node: OutwardNode[DI, UI, BI], binding: NodeBinding)(implicit sourceInfo: SourceInfo) { | ||||||
|     val info = sourceLine(sourceInfo, " at ", "") |     val info = sourceLine(sourceInfo, " at ", "") | ||||||
|     val noIs = numPI.size == 1 && numPI.contains(0) |     val noIs = numPI.size == 1 && numPI.contains(0) | ||||||
|     require (!noIs, s"${name}${lazyModule.line} was incorrectly connected as a sink" + info) |     require (!noIs, s"${name}${lazyModule.line} was incorrectly connected as a sink" + info) | ||||||
|     require (!iRealized, s"${name}${lazyModule.line} was incorrectly connected as a sink after it's .module was used" + info) |     require (!iRealized, s"${name}${lazyModule.line} was incorrectly connected as a sink after it's .module was used" + info) | ||||||
|     accPI += ((index, node)) |     accPI += ((index, node, binding)) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   private def reqI() = require(numPI.contains(accPI.size), s"${name} has ${accPI.size} inputs, expected ${numPI}${lazyModule.line}") |   protected[diplomacy] lazy val iBindings = { iRealized = true; accPI.result() } | ||||||
|   protected[diplomacy] lazy val iPorts = { iRealized = true; reqI(); accPI.result() } |  | ||||||
|  |  | ||||||
|  |   protected[diplomacy] val iStar: Int | ||||||
|  |   protected[diplomacy] val iPortMapping: Seq[(Int, Int)] | ||||||
|   protected[diplomacy] val iParams: Seq[UI] |   protected[diplomacy] val iParams: Seq[UI] | ||||||
|   val bundleIn: Vec[BI] |   val bundleIn: Vec[BI] | ||||||
| } | } | ||||||
| @@ -113,48 +125,84 @@ trait OutwardNode[DO, UO, BO <: Data] extends BaseNode with OutwardNodeHandle[DO | |||||||
|   require (!numPO.isEmpty, s"No number of outputs would be acceptable to ${name}${lazyModule.line}") |   require (!numPO.isEmpty, s"No number of outputs would be acceptable to ${name}${lazyModule.line}") | ||||||
|   require (numPO.start >= 0, s"${name} accepts a negative number of outputs${lazyModule.line}") |   require (numPO.start >= 0, s"${name} accepts a negative number of outputs${lazyModule.line}") | ||||||
|  |  | ||||||
|   private val accPO = ListBuffer[(Int, InwardNode [DO, UO, BO])]() |   private val accPO = ListBuffer[(Int, InwardNode [DO, UO, BO], NodeBinding)]() | ||||||
|   private var oRealized = false |   private var oRealized = false | ||||||
|  |  | ||||||
|   protected[diplomacy] def oPushed = accPO.size |   protected[diplomacy] def oPushed = accPO.size | ||||||
|   protected[diplomacy] def oPush(index: Int, node: InwardNode [DO, UO, BO])(implicit sourceInfo: SourceInfo) { |   protected[diplomacy] def oPush(index: Int, node: InwardNode [DO, UO, BO], binding: NodeBinding)(implicit sourceInfo: SourceInfo) { | ||||||
|     val info = sourceLine(sourceInfo, " at ", "") |     val info = sourceLine(sourceInfo, " at ", "") | ||||||
|     val noOs = numPO.size == 1 && numPO.contains(0) |     val noOs = numPO.size == 1 && numPO.contains(0) | ||||||
|     require (!noOs, s"${name}${lazyModule.line} was incorrectly connected as a source" + info) |     require (!noOs, s"${name}${lazyModule.line} was incorrectly connected as a source" + info) | ||||||
|     require (!oRealized, s"${name}${lazyModule.line} was incorrectly connected as a source after it's .module was used" + info) |     require (!oRealized, s"${name}${lazyModule.line} was incorrectly connected as a source after it's .module was used" + info) | ||||||
|     accPO += ((index, node)) |     accPO += ((index, node, binding)) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   private def reqO() = require(numPO.contains(accPO.size), s"${name} has ${accPO.size} outputs, expected ${numPO}${lazyModule.line}") |   protected[diplomacy] lazy val oBindings = { oRealized = true; accPO.result() } | ||||||
|   protected[diplomacy] lazy val oPorts = { oRealized = true; reqO(); accPO.result() } |  | ||||||
|  |  | ||||||
|  |   protected[diplomacy] val oStar: Int | ||||||
|  |   protected[diplomacy] val oPortMapping: Seq[(Int, Int)] | ||||||
|   protected[diplomacy] val oParams: Seq[DO] |   protected[diplomacy] val oParams: Seq[DO] | ||||||
|   val bundleOut: Vec[BO] |   val bundleOut: Vec[BO] | ||||||
| } | } | ||||||
|  |  | ||||||
| class MixedNode[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data]( | abstract class MixedNode[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data]( | ||||||
|   inner: InwardNodeImp [DI, UI, EI, BI], |   inner: InwardNodeImp [DI, UI, EI, BI], | ||||||
|   outer: OutwardNodeImp[DO, UO, EO, BO])( |   outer: OutwardNodeImp[DO, UO, EO, BO])( | ||||||
|   private val dFn: (Int, Seq[DI]) => Seq[DO], |  | ||||||
|   private val uFn: (Int, Seq[UO]) => Seq[UI], |  | ||||||
|   protected[diplomacy] val numPO: Range.Inclusive, |   protected[diplomacy] val numPO: Range.Inclusive, | ||||||
|   protected[diplomacy] val numPI: Range.Inclusive) |   protected[diplomacy] val numPI: Range.Inclusive) | ||||||
|   extends BaseNode with InwardNode[DI, UI, BI] with OutwardNode[DO, UO, BO] |   extends BaseNode with InwardNode[DI, UI, BI] with OutwardNode[DO, UO, BO] | ||||||
| { | { | ||||||
|   // meta-data for printing the node graph |   protected[diplomacy] def resolveStarO(i: Int, o: Int): Int | ||||||
|   protected[diplomacy] def colour  = inner.colour |   protected[diplomacy] def resolveStarI(i: Int, o: Int): Int | ||||||
|   protected[diplomacy] def outputs = oPorts.map(_._2) zip edgesOut.map(e => outer.labelO(e)) |   protected[diplomacy] def mapParamsD(n: Int, p: Seq[DI]): Seq[DO] | ||||||
|   protected[diplomacy] def inputs  = iPorts.map(_._2) zip edgesIn .map(e => inner.labelI(e)) |   protected[diplomacy] def mapParamsU(n: Int, p: Seq[UO]): Seq[UI] | ||||||
|  |  | ||||||
|  |   protected[diplomacy] lazy val (oPortMapping, iPortMapping, oStar, iStar) = { | ||||||
|  |     val oStars = oBindings.filter { case (_,_,b) => b == BIND_STAR }.size | ||||||
|  |     val iStars = iBindings.filter { case (_,_,b) => b == BIND_STAR }.size | ||||||
|  |     require (oStars + iStars <= 1, s"${name} appears beside a :*= ${iStars} times and a :=* ${oStars} times; at most once is allowed${lazyModule.line}") | ||||||
|  |     val oKnown = oBindings.map { case (_, n, b) => b match { | ||||||
|  |       case BIND_ONCE  => 1 | ||||||
|  |       case BIND_QUERY => n.iStar | ||||||
|  |       case BIND_STAR  => 0 }}.foldLeft(0)(_+_) | ||||||
|  |     val iKnown = iBindings.map { case (_, n, b) => b match { | ||||||
|  |       case BIND_ONCE  => 1 | ||||||
|  |       case BIND_QUERY => n.oStar | ||||||
|  |       case BIND_STAR  => 0 }}.foldLeft(0)(_+_) | ||||||
|  |     val oStar = if (oStars > 0) resolveStarO(iKnown, oKnown) else 0 | ||||||
|  |     val iStar = if (iStars > 0) resolveStarI(iKnown, oKnown) else 0 | ||||||
|  |     val oSum = oBindings.map { case (_, n, b) => b match { | ||||||
|  |       case BIND_ONCE  => 1 | ||||||
|  |       case BIND_QUERY => n.iStar | ||||||
|  |       case BIND_STAR  => oStar }}.scanLeft(0)(_+_) | ||||||
|  |     val iSum = iBindings.map { case (_, n, b) => b match { | ||||||
|  |       case BIND_ONCE  => 1 | ||||||
|  |       case BIND_QUERY => n.oStar | ||||||
|  |       case BIND_STAR  => iStar }}.scanLeft(0)(_+_) | ||||||
|  |     val oTotal = oSum.lastOption.getOrElse(0) | ||||||
|  |     val iTotal = iSum.lastOption.getOrElse(0) | ||||||
|  |     require(numPO.contains(oTotal), s"${name} has ${oTotal} outputs, expected ${numPO}${lazyModule.line}") | ||||||
|  |     require(numPI.contains(iTotal), s"${name} has ${iTotal} inputs, expected ${numPI}${lazyModule.line}") | ||||||
|  |     (oSum.init zip oSum.tail, iSum.init zip iSum.tail, oStar, iStar) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   lazy val oPorts = oBindings.flatMap { case (i, n, _) => | ||||||
|  |     val (start, end) = n.iPortMapping(i) | ||||||
|  |     (start until end) map { j => (j, n) } | ||||||
|  |   } | ||||||
|  |   lazy val iPorts = iBindings.flatMap { case (i, n, _) => | ||||||
|  |     val (start, end) = n.oPortMapping(i) | ||||||
|  |     (start until end) map { j => (j, n) } | ||||||
|  |   } | ||||||
|  |  | ||||||
|   private def reqE(o: Int, i: Int) = require(i == o, s"${name} has ${i} inputs and ${o} outputs; they must match${lazyModule.line}") |  | ||||||
|   protected[diplomacy] lazy val oParams: Seq[DO] = { |   protected[diplomacy] lazy val oParams: Seq[DO] = { | ||||||
|     val o = dFn(oPorts.size, iPorts.map { case (i, n) => n.oParams(i) }) |     val o = mapParamsD(oPorts.size, iPorts.map { case (i, n) => n.oParams(i) }) | ||||||
|     reqE(oPorts.size, o.size) |     require (o.size == oPorts.size, s"Bug in diplomacy; ${name} has ${o.size} != ${oPorts.size} down/up outer parameters${lazyModule.line}") | ||||||
|     o.map(outer.mixO(_, this)) |     o.map(outer.mixO(_, this)) | ||||||
|   } |   } | ||||||
|   protected[diplomacy] lazy val iParams: Seq[UI] = { |   protected[diplomacy] lazy val iParams: Seq[UI] = { | ||||||
|     val i = uFn(iPorts.size, oPorts.map { case (o, n) => n.iParams(o) }) |     val i = mapParamsU(iPorts.size, oPorts.map { case (o, n) => n.iParams(o) }) | ||||||
|     reqE(i.size, iPorts.size) |     require (i.size == iPorts.size, s"Bug in diplomacy; ${name} has ${i.size} != ${iPorts.size} up/down inner parameters${lazyModule.line}") | ||||||
|     i.map(inner.mixI(_, this)) |     i.map(inner.mixI(_, this)) | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -175,87 +223,174 @@ class MixedNode[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data]( | |||||||
|   lazy val bundleIn  = wireI(flipI(inner.bundleI(edgesIn))) |   lazy val bundleIn  = wireI(flipI(inner.bundleI(edgesIn))) | ||||||
|  |  | ||||||
|   // connects the outward part of a node with the inward part of this node |   // connects the outward part of a node with the inward part of this node | ||||||
|   override def := (h: OutwardNodeHandle[DI, UI, BI])(implicit p: Parameters, sourceInfo: SourceInfo): Option[LazyModule] = { |   private def bind(h: OutwardNodeHandle[DI, UI, BI], binding: NodeBinding)(implicit p: Parameters, sourceInfo: SourceInfo): Option[LazyModule] = { | ||||||
|     val x = this // x := y |     val x = this // x := y | ||||||
|     val y = h.outward |     val y = h.outward | ||||||
|     val info = sourceLine(sourceInfo, " at ", "") |     val info = sourceLine(sourceInfo, " at ", "") | ||||||
|     require (!LazyModule.stack.isEmpty, s"${y.name} cannot be connected to ${x.name} outside of LazyModule scope" + info) |     require (!LazyModule.stack.isEmpty, s"${y.name} cannot be connected to ${x.name} outside of LazyModule scope" + info) | ||||||
|     val i = x.iPushed |     val i = x.iPushed | ||||||
|     val o = y.oPushed |     val o = y.oPushed | ||||||
|     y.oPush(i, x) |     y.oPush(i, x, binding match { | ||||||
|     x.iPush(o, y) |       case BIND_ONCE  => BIND_ONCE | ||||||
|     val (out, binding) = inner.connect(y.bundleOut(o), x.bundleIn(i), x.edgesIn(i)) |       case BIND_STAR  => BIND_QUERY | ||||||
|     LazyModule.stack.head.bindings = binding :: LazyModule.stack.head.bindings |       case BIND_QUERY => BIND_STAR }) | ||||||
|  |     x.iPush(o, y, binding) | ||||||
|  |     def bindings() = { | ||||||
|  |       val (iStart, iEnd) = x.iPortMapping(i) | ||||||
|  |       val (oStart, oEnd) = y.oPortMapping(o) | ||||||
|  |       require (iEnd - iStart == oEnd - oStart, s"Bug in diplomacy; ${iEnd-iStart} != ${oEnd-oStart} means port resolution failed") | ||||||
|  |       Seq.tabulate(iEnd - iStart) { j => | ||||||
|  |         (x.edgesIn(iStart+j), x.bundleIn(iStart+j), y.bundleOut(oStart+j)) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     val (out, newbinding) = inner.connect(bindings _) | ||||||
|  |     LazyModule.stack.head.bindings = newbinding :: LazyModule.stack.head.bindings | ||||||
|     out |     out | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   override def :=  (h: OutwardNodeHandle[DI, UI, BI])(implicit p: Parameters, sourceInfo: SourceInfo): Option[LazyModule] = bind(h, BIND_ONCE) | ||||||
|  |   override def :*= (h: OutwardNodeHandle[DI, UI, BI])(implicit p: Parameters, sourceInfo: SourceInfo): Option[LazyModule] = bind(h, BIND_STAR) | ||||||
|  |   override def :=* (h: OutwardNodeHandle[DI, UI, BI])(implicit p: Parameters, sourceInfo: SourceInfo): Option[LazyModule] = bind(h, BIND_QUERY) | ||||||
|  |  | ||||||
|  |   // meta-data for printing the node graph | ||||||
|  |   protected[diplomacy] def colour  = inner.colour | ||||||
|  |   protected[diplomacy] def outputs = oPorts.map(_._2) zip edgesOut.map(e => outer.labelO(e)) | ||||||
|  |   protected[diplomacy] def inputs  = iPorts.map(_._2) zip edgesIn .map(e => inner.labelI(e)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class MixedAdapterNode[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data]( | ||||||
|  |   inner: InwardNodeImp [DI, UI, EI, BI], | ||||||
|  |   outer: OutwardNodeImp[DO, UO, EO, BO])( | ||||||
|  |   dFn: DI => DO, | ||||||
|  |   uFn: UO => UI, | ||||||
|  |   num: Range.Inclusive = 0 to 999) | ||||||
|  |   extends MixedNode(inner, outer)(num, num) | ||||||
|  | { | ||||||
|  |   protected[diplomacy] def resolveStarO(i: Int, o: Int): Int = { | ||||||
|  |     require (i >= o, s"${name} has ${o} outputs and ${i} inputs; cannot assign ${i-o} edges to resolve :=*${lazyModule.line}") | ||||||
|  |     i - o | ||||||
|  |   } | ||||||
|  |   protected[diplomacy] def resolveStarI(i: Int, o: Int): Int = { | ||||||
|  |     require (o >= i, s"${name} has ${o} outputs and ${i} inputs; cannot assign ${o-i} edges to resolve :*=${lazyModule.line}") | ||||||
|  |     o - i | ||||||
|  |   } | ||||||
|  |   protected[diplomacy] def mapParamsD(n: Int, p: Seq[DI]): Seq[DO] = { | ||||||
|  |     require(n == p.size, s"${name} has ${p.size} inputs and ${n} outputs; they must match${lazyModule.line}") | ||||||
|  |     p.map(dFn) | ||||||
|  |   } | ||||||
|  |   protected[diplomacy] def mapParamsU(n: Int, p: Seq[UO]): Seq[UI] = { | ||||||
|  |     require(n == p.size, s"${name} has ${n} inputs and ${p.size} outputs; they must match${lazyModule.line}") | ||||||
|  |     p.map(uFn) | ||||||
|  |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| class SimpleNode[D, U, EO, EI, B <: Data](imp: NodeImp[D, U, EO, EI, B])( | class MixedNexusNode[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data]( | ||||||
|   oFn: (Int, Seq[D]) => Seq[D], |   inner: InwardNodeImp [DI, UI, EI, BI], | ||||||
|   iFn: (Int, Seq[U]) => Seq[U], |   outer: OutwardNodeImp[DO, UO, EO, BO])( | ||||||
|   numPO: Range.Inclusive, |   dFn: Seq[DI] => DO, | ||||||
|   numPI: Range.Inclusive) |   uFn: Seq[UO] => UI, | ||||||
|     extends MixedNode[D, U, EI, B, D, U, EO, B](imp, imp)(oFn, iFn, numPO, numPI) |   numPO: Range.Inclusive = 1 to 999, | ||||||
|  |   numPI: Range.Inclusive = 1 to 999) | ||||||
|  |   extends MixedNode(inner, outer)(numPO, numPI) | ||||||
|  | { | ||||||
|  |   require (numPO.end >= 1, s"${name} does not accept outputs${lazyModule.line}") | ||||||
|  |   require (numPI.end >= 1, s"${name} does not accept inputs${lazyModule.line}") | ||||||
|  |   protected[diplomacy] def resolveStarO(i: Int, o: Int): Int = { | ||||||
|  |     require (false, "${name} cannot resolve :=*${lazyModule.line}") | ||||||
|  |     0 | ||||||
|  |   } | ||||||
|  |   protected[diplomacy] def resolveStarI(i: Int, o: Int): Int = { | ||||||
|  |     require (false, s"${name} cannot resolve :*=${lazyModule.line}") | ||||||
|  |     0 | ||||||
|  |   } | ||||||
|  |   protected[diplomacy] def mapParamsD(n: Int, p: Seq[DI]): Seq[DO] = Seq.fill(n) { dFn(p) } | ||||||
|  |   protected[diplomacy] def mapParamsU(n: Int, p: Seq[UO]): Seq[UI] = Seq.fill(n) { uFn(p) } | ||||||
|  | } | ||||||
|  |  | ||||||
| class IdentityNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B]) | class AdapterNode[D, U, EO, EI, B <: Data](imp: NodeImp[D, U, EO, EI, B])( | ||||||
|   extends SimpleNode(imp)({case (_, s) => s}, {case (_, s) => s}, 0 to 999, 0 to 999) |   dFn: D => D, | ||||||
|  |   uFn: U => U, | ||||||
|  |   num: Range.Inclusive = 0 to 999) | ||||||
|  |     extends MixedAdapterNode[D, U, EI, B, D, U, EO, B](imp, imp)(dFn, uFn, num) | ||||||
|  |  | ||||||
| class OutputNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B]) extends IdentityNode(imp) | class NexusNode[D, U, EO, EI, B <: Data](imp: NodeImp[D, U, EO, EI, B])( | ||||||
|  |   dFn: Seq[D] => D, | ||||||
|  |   uFn: Seq[U] => U, | ||||||
|  |   numPO: Range.Inclusive = 1 to 999, | ||||||
|  |   numPI: Range.Inclusive = 1 to 999) | ||||||
|  |     extends MixedNexusNode[D, U, EI, B, D, U, EO, B](imp, imp)(dFn, uFn, numPO, numPI) | ||||||
|  |  | ||||||
|  | class IdentityNode[D, U, EO, EI, B <: Data](imp: NodeImp[D, U, EO, EI, B]) | ||||||
|  |   extends AdapterNode(imp)({s => s}, {s => s}) | ||||||
|  |  | ||||||
|  | class OutputNode[D, U, EO, EI, B <: Data](imp: NodeImp[D, U, EO, EI, B]) extends IdentityNode(imp) | ||||||
| { | { | ||||||
|   override lazy val bundleIn = bundleOut |   override lazy val bundleIn = bundleOut | ||||||
| } | } | ||||||
|  |  | ||||||
| class InputNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B]) extends IdentityNode(imp) | class InputNode[D, U, EO, EI, B <: Data](imp: NodeImp[D, U, EO, EI, B]) extends IdentityNode(imp) | ||||||
| { | { | ||||||
|   override lazy val bundleOut = bundleIn |   override lazy val bundleOut = bundleIn | ||||||
| } | } | ||||||
|  |  | ||||||
| class SourceNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B])(po: PO, num: Range.Inclusive = 1 to 1) | class SourceNode[D, U, EO, EI, B <: Data](imp: NodeImp[D, U, EO, EI, B])(po: Seq[D]) | ||||||
|   extends SimpleNode(imp)({case (n, Seq()) => Seq.fill(n)(po)}, {case (0, _) => Seq()}, num, 0 to 0) |   extends MixedNode(imp, imp)(po.size to po.size, 0 to 0) | ||||||
| { | { | ||||||
|   require (num.end >= 1, s"${name} is a source which does not accept outputs${lazyModule.line}") |   protected[diplomacy] def resolveStarO(i: Int, o: Int): Int = { | ||||||
|  |     require (po.size >= o, s"${name} has ${o} outputs out of ${po.size}; cannot assign ${po.size - o} edges to resolve :=*${lazyModule.line}") | ||||||
|  |     po.size - o | ||||||
|  |   } | ||||||
|  |   protected[diplomacy] def resolveStarI(i: Int, o: Int): Int = { | ||||||
|  |     require (false, s"${name} cannot resolve :*=${lazyModule.line}") | ||||||
|  |     0 | ||||||
|  |   } | ||||||
|  |   protected[diplomacy] def mapParamsD(n: Int, p: Seq[D]): Seq[D] = po | ||||||
|  |   protected[diplomacy] def mapParamsU(n: Int, p: Seq[U]): Seq[U] = Seq() | ||||||
|  |  | ||||||
|   override lazy val bundleIn = { require(false, s"${name} has no bundleIn; try bundleOut?"); bundleOut } |   override lazy val bundleIn = { require(false, s"${name} has no bundleIn; try bundleOut?"); bundleOut } | ||||||
| } | } | ||||||
|  |  | ||||||
| class SinkNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B])(pi: PI, num: Range.Inclusive = 1 to 1) | class SinkNode[D, U, EO, EI, B <: Data](imp: NodeImp[D, U, EO, EI, B])(pi: Seq[U]) | ||||||
|   extends SimpleNode(imp)({case (0, _) => Seq()}, {case (n, Seq()) => Seq.fill(n)(pi)}, 0 to 0, num) |   extends MixedNode(imp, imp)(0 to 0, pi.size to pi.size) | ||||||
| { | { | ||||||
|   require (num.end >= 1, s"${name} is a sink which does not accept inputs${lazyModule.line}") |   protected[diplomacy] def resolveStarO(i: Int, o: Int): Int = { | ||||||
|  |     require (false, s"${name} cannot resolve :=*${lazyModule.line}") | ||||||
|  |     0 | ||||||
|  |   } | ||||||
|  |   protected[diplomacy] def resolveStarI(i: Int, o: Int): Int = { | ||||||
|  |     require (pi.size >= i, s"${name} has ${i} inputs out of ${pi.size}; cannot assign ${pi.size - i} edges to resolve :*=${lazyModule.line}") | ||||||
|  |     pi.size - i | ||||||
|  |   } | ||||||
|  |   protected[diplomacy] def mapParamsD(n: Int, p: Seq[D]): Seq[D] = Seq() | ||||||
|  |   protected[diplomacy] def mapParamsU(n: Int, p: Seq[U]): Seq[U] = pi | ||||||
|  |  | ||||||
|   override lazy val bundleOut = { require(false, s"${name} has no bundleOut; try bundleIn?"); bundleIn } |   override lazy val bundleOut = { require(false, s"${name} has no bundleOut; try bundleIn?"); bundleIn } | ||||||
| } | } | ||||||
|  |  | ||||||
| class BlindOutputNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B])(pi: Seq[PI]) | class BlindOutputNode[D, U, EO, EI, B <: Data](imp: NodeImp[D, U, EO, EI, B])(pi: Seq[U]) | ||||||
|   extends SimpleNode(imp)({case (0, _) => Seq()}, {case (_, Seq()) => pi}, 0 to 0, pi.size to pi.size) |   extends SinkNode(imp)(pi) | ||||||
| { | { | ||||||
|   override val flip = true |   override val flip = true | ||||||
|   override lazy val bundleOut = bundleIn |   override lazy val bundleOut = bundleIn | ||||||
| } | } | ||||||
|  |  | ||||||
| class BlindInputNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B])(po: Seq[PO]) | class BlindInputNode[D, U, EO, EI, B <: Data](imp: NodeImp[D, U, EO, EI, B])(po: Seq[D]) | ||||||
|   extends SimpleNode(imp)({case (_, Seq()) => po}, {case (0, _) => Seq()}, po.size to po.size, 0 to 0) |   extends SourceNode(imp)(po) | ||||||
| { | { | ||||||
|   override val flip = true |   override val flip = true | ||||||
|   override lazy val bundleIn = bundleOut |   override lazy val bundleIn = bundleOut | ||||||
| } | } | ||||||
|  |  | ||||||
| class InternalOutputNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B])(pi: Seq[PI]) | class InternalOutputNode[D, U, EO, EI, B <: Data](imp: NodeImp[D, U, EO, EI, B])(pi: Seq[U]) | ||||||
|   extends SimpleNode(imp)({case (0, _) => Seq()}, {case (_, Seq()) => pi}, 0 to 0, pi.size to pi.size) |   extends SinkNode(imp)(pi) | ||||||
| { | { | ||||||
|   override val wire = true |   override val wire = true | ||||||
|   override lazy val bundleOut = bundleIn |   override lazy val bundleOut = bundleIn | ||||||
| } | } | ||||||
|  |  | ||||||
| class InternalInputNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B])(po: Seq[PO]) | class InternalInputNode[D, U, EO, EI, B <: Data](imp: NodeImp[D, U, EO, EI, B])(po: Seq[D]) | ||||||
|   extends SimpleNode(imp)({case (_, Seq()) => po}, {case (0, _) => Seq()}, po.size to po.size, 0 to 0) |   extends SourceNode(imp)(po) | ||||||
| { | { | ||||||
|   override val wire = true |   override val wire = true | ||||||
|   override lazy val bundleIn = bundleOut |   override lazy val bundleIn = bundleOut | ||||||
| } | } | ||||||
|  |  | ||||||
| class InteriorNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B]) |  | ||||||
|   (oFn: Seq[PO] => PO, iFn: Seq[PI] => PI, numPO: Range.Inclusive, numPI: Range.Inclusive) |  | ||||||
|   extends SimpleNode(imp)({case (n,s) => Seq.fill(n)(oFn(s))}, {case (n,s) => Seq.fill(n)(iFn(s))}, numPO, numPI) |  | ||||||
| { |  | ||||||
|   require (numPO.end >= 1, s"${name} is an adapter which does not accept outputs${lazyModule.line}") |  | ||||||
|   require (numPI.end >= 1, s"${name} is an adapter which does not accept inputs${lazyModule.line}") |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -13,7 +13,7 @@ import uncore.util._ | |||||||
|  |  | ||||||
| class ScratchpadSlavePort(implicit p: Parameters) extends LazyModule { | class ScratchpadSlavePort(implicit p: Parameters) extends LazyModule { | ||||||
|   val coreDataBytes = p(XLen)/8 |   val coreDataBytes = p(XLen)/8 | ||||||
|   val node = TLManagerNode(TLManagerPortParameters( |   val node = TLManagerNode(Seq(TLManagerPortParameters( | ||||||
|     Seq(TLManagerParameters( |     Seq(TLManagerParameters( | ||||||
|       address            = List(AddressSet(0x80000000L, BigInt(p(DataScratchpadSize)-1))), |       address            = List(AddressSet(0x80000000L, BigInt(p(DataScratchpadSize)-1))), | ||||||
|       regionType         = RegionType.UNCACHED, |       regionType         = RegionType.UNCACHED, | ||||||
| @@ -25,7 +25,7 @@ class ScratchpadSlavePort(implicit p: Parameters) extends LazyModule { | |||||||
|       supportsGet        = TransferSizes(1, coreDataBytes), |       supportsGet        = TransferSizes(1, coreDataBytes), | ||||||
|       fifoId             = Some(0))), // requests handled in FIFO order |       fifoId             = Some(0))), // requests handled in FIFO order | ||||||
|     beatBytes = coreDataBytes, |     beatBytes = coreDataBytes, | ||||||
|     minLatency = 1)) |     minLatency = 1))) | ||||||
|  |  | ||||||
|   lazy val module = new LazyModuleImp(this) { |   lazy val module = new LazyModuleImp(this) { | ||||||
|     val io = new Bundle { |     val io = new Bundle { | ||||||
|   | |||||||
| @@ -34,6 +34,7 @@ trait TopNetwork extends HasPeripheryParameters { | |||||||
|   val socBus = LazyModule(new TLXbar) |   val socBus = LazyModule(new TLXbar) | ||||||
|   val peripheryBus = LazyModule(new TLXbar) |   val peripheryBus = LazyModule(new TLXbar) | ||||||
|   val intBus = LazyModule(new IntXbar) |   val intBus = LazyModule(new IntXbar) | ||||||
|  |   val l2 = LazyModule(new TLBuffer) | ||||||
|  |  | ||||||
|   peripheryBus.node := |   peripheryBus.node := | ||||||
|     TLBuffer()( |     TLBuffer()( | ||||||
| @@ -62,13 +63,3 @@ class BaseTopBundle[+L <: BaseTop](_outer: L) extends BareTopBundle(_outer) | |||||||
|  |  | ||||||
| class BaseTopModule[+L <: BaseTop, +B <: BaseTopBundle[L]](_outer: L, _io: () => B) extends BareTopModule(_outer, _io) | class BaseTopModule[+L <: BaseTop, +B <: BaseTopBundle[L]](_outer: L, _io: () => B) extends BareTopModule(_outer, _io) | ||||||
|     with TopNetworkModule |     with TopNetworkModule | ||||||
|  |  | ||||||
| trait L2Crossbar extends TopNetwork { |  | ||||||
|   val l2 = LazyModule(new TLXbar) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| trait L2CrossbarBundle extends TopNetworkBundle { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| trait L2CrossbarModule extends TopNetworkModule { |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -165,7 +165,7 @@ trait PeripheryMasterAXI4MMIOModule { | |||||||
| ///// | ///// | ||||||
|  |  | ||||||
| // PeripherySlaveAXI4 is an example, make your own cake pattern like this one. | // PeripherySlaveAXI4 is an example, make your own cake pattern like this one. | ||||||
| trait PeripherySlaveAXI4 extends L2Crossbar { | trait PeripherySlaveAXI4 extends TopNetwork { | ||||||
|   private val config = p(ExtIn) |   private val config = p(ExtIn) | ||||||
|   val l2_axi4 = AXI4BlindInputNode(Seq(AXI4MasterPortParameters( |   val l2_axi4 = AXI4BlindInputNode(Seq(AXI4MasterPortParameters( | ||||||
|     masters = Seq(AXI4MasterParameters( |     masters = Seq(AXI4MasterParameters( | ||||||
| @@ -179,12 +179,12 @@ trait PeripherySlaveAXI4 extends L2Crossbar { | |||||||
|     l2_axi4)))) |     l2_axi4)))) | ||||||
| } | } | ||||||
|  |  | ||||||
| trait PeripherySlaveAXI4Bundle extends L2CrossbarBundle { | trait PeripherySlaveAXI4Bundle extends TopNetworkBundle { | ||||||
|   val outer: PeripherySlaveAXI4 |   val outer: PeripherySlaveAXI4 | ||||||
|   val l2_axi4 = outer.l2_axi4.bundleIn |   val l2_axi4 = outer.l2_axi4.bundleIn | ||||||
| } | } | ||||||
|  |  | ||||||
| trait PeripherySlaveAXI4Module extends L2CrossbarModule { | trait PeripherySlaveAXI4Module extends TopNetworkModule { | ||||||
|   val outer: PeripherySlaveAXI4 |   val outer: PeripherySlaveAXI4 | ||||||
|   val io: PeripherySlaveAXI4Bundle |   val io: PeripherySlaveAXI4Bundle | ||||||
|   // nothing to do |   // nothing to do | ||||||
| @@ -231,7 +231,7 @@ trait PeripheryMasterTLMMIOModule { | |||||||
| ///// | ///// | ||||||
|  |  | ||||||
| // NOTE: this port is NOT allowed to issue Acquires | // NOTE: this port is NOT allowed to issue Acquires | ||||||
| trait PeripherySlaveTL extends L2Crossbar { | trait PeripherySlaveTL extends TopNetwork { | ||||||
|   private val config = p(ExtIn) |   private val config = p(ExtIn) | ||||||
|   val l2_tl = TLBlindInputNode(Seq(TLClientPortParameters( |   val l2_tl = TLBlindInputNode(Seq(TLClientPortParameters( | ||||||
|     clients = Seq(TLClientParameters( |     clients = Seq(TLClientParameters( | ||||||
| @@ -243,12 +243,12 @@ trait PeripherySlaveTL extends L2Crossbar { | |||||||
|     l2_tl)) |     l2_tl)) | ||||||
| } | } | ||||||
|  |  | ||||||
| trait PeripherySlaveTLBundle extends L2CrossbarBundle { | trait PeripherySlaveTLBundle extends TopNetworkBundle { | ||||||
|   val outer: PeripherySlaveTL |   val outer: PeripherySlaveTL | ||||||
|   val l2_tl = outer.l2_tl.bundleIn |   val l2_tl = outer.l2_tl.bundleIn | ||||||
| } | } | ||||||
|  |  | ||||||
| trait PeripherySlaveTLModule extends L2CrossbarModule { | trait PeripherySlaveTLModule extends TopNetworkModule { | ||||||
|   val outer: PeripherySlaveTL |   val outer: PeripherySlaveTL | ||||||
|   val io: PeripherySlaveTLBundle |   val io: PeripherySlaveTLBundle | ||||||
|   // nothing to do |   // nothing to do | ||||||
|   | |||||||
| @@ -10,23 +10,23 @@ import uncore.devices._ | |||||||
| import util._ | import util._ | ||||||
| import coreplex._ | import coreplex._ | ||||||
|  |  | ||||||
| trait RocketPlexMaster extends L2Crossbar { | trait RocketPlexMaster extends TopNetwork { | ||||||
|   val module: RocketPlexMasterModule |   val module: RocketPlexMasterModule | ||||||
|   val mem: Seq[TLInwardNode] |   val mem: Seq[TLInwardNode] | ||||||
|  |  | ||||||
|   val coreplex = LazyModule(new DefaultCoreplex) |   val coreplex = LazyModule(new DefaultCoreplex) | ||||||
|  |  | ||||||
|   coreplex.l2in := l2.node |   coreplex.l2in :=* l2.node | ||||||
|   socBus.node := coreplex.mmio |   socBus.node := coreplex.mmio | ||||||
|   coreplex.mmioInt := intBus.intnode |   coreplex.mmioInt := intBus.intnode | ||||||
|   mem.foreach { _ := coreplex.mem } |   mem.foreach { _ := coreplex.mem } | ||||||
| } | } | ||||||
|  |  | ||||||
| trait RocketPlexMasterBundle extends L2CrossbarBundle { | trait RocketPlexMasterBundle extends TopNetworkBundle { | ||||||
|   val outer: RocketPlexMaster |   val outer: RocketPlexMaster | ||||||
| } | } | ||||||
|  |  | ||||||
| trait RocketPlexMasterModule extends L2CrossbarModule { | trait RocketPlexMasterModule extends TopNetworkModule { | ||||||
|   val outer: RocketPlexMaster |   val outer: RocketPlexMaster | ||||||
|   val io: RocketPlexMasterBundle |   val io: RocketPlexMasterBundle | ||||||
|   val clock: Clock |   val clock: Clock | ||||||
|   | |||||||
| @@ -19,10 +19,6 @@ object AHBImp extends NodeImp[AHBMasterPortParameters, AHBSlavePortParameters, A | |||||||
|   override def labelI(ei: AHBEdgeParameters) = (ei.slave.beatBytes * 8).toString |   override def labelI(ei: AHBEdgeParameters) = (ei.slave.beatBytes * 8).toString | ||||||
|   override def labelO(eo: AHBEdgeParameters) = (eo.slave.beatBytes * 8).toString |   override def labelO(eo: AHBEdgeParameters) = (eo.slave.beatBytes * 8).toString | ||||||
|  |  | ||||||
|   def connect(bo: => AHBBundle, bi: => AHBBundle, ei: => AHBEdgeParameters)(implicit p: Parameters, sourceInfo: SourceInfo): (Option[LazyModule], () => Unit) = { |  | ||||||
|     (None, () => { bi <> bo }) |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   override def mixO(pd: AHBMasterPortParameters, node: OutwardNode[AHBMasterPortParameters, AHBSlavePortParameters, AHBBundle]): AHBMasterPortParameters  = |   override def mixO(pd: AHBMasterPortParameters, node: OutwardNode[AHBMasterPortParameters, AHBSlavePortParameters, AHBBundle]): AHBMasterPortParameters  = | ||||||
|    pd.copy(masters = pd.masters.map  { c => c.copy (nodePath = node +: c.nodePath) }) |    pd.copy(masters = pd.masters.map  { c => c.copy (nodePath = node +: c.nodePath) }) | ||||||
|   override def mixI(pu: AHBSlavePortParameters, node: InwardNode[AHBMasterPortParameters, AHBSlavePortParameters, AHBBundle]): AHBSlavePortParameters = |   override def mixI(pu: AHBSlavePortParameters, node: InwardNode[AHBMasterPortParameters, AHBSlavePortParameters, AHBBundle]): AHBSlavePortParameters = | ||||||
| @@ -31,16 +27,14 @@ object AHBImp extends NodeImp[AHBMasterPortParameters, AHBSlavePortParameters, A | |||||||
|  |  | ||||||
| // Nodes implemented inside modules | // Nodes implemented inside modules | ||||||
| case class AHBIdentityNode() extends IdentityNode(AHBImp) | case class AHBIdentityNode() extends IdentityNode(AHBImp) | ||||||
| case class AHBMasterNode(portParams: AHBMasterPortParameters, numPorts: Range.Inclusive = 1 to 1) | case class AHBMasterNode(portParams: Seq[AHBMasterPortParameters]) extends SourceNode(AHBImp)(portParams) | ||||||
|   extends SourceNode(AHBImp)(portParams, numPorts) | case class AHBSlaveNode(portParams: Seq[AHBSlavePortParameters]) extends SinkNode(AHBImp)(portParams) | ||||||
| case class AHBSlaveNode(portParams: AHBSlavePortParameters, numPorts: Range.Inclusive = 1 to 1) | case class AHBNexusNode( | ||||||
|   extends SinkNode(AHBImp)(portParams, numPorts) |   masterFn:       Seq[AHBMasterPortParameters] => AHBMasterPortParameters, | ||||||
| case class AHBAdapterNode( |   slaveFn:        Seq[AHBSlavePortParameters]  => AHBSlavePortParameters, | ||||||
|   masterFn:       Seq[AHBMasterPortParameters]  => AHBMasterPortParameters, |   numMasterPorts: Range.Inclusive = 1 to 999, | ||||||
|   slaveFn:        Seq[AHBSlavePortParameters] => AHBSlavePortParameters, |   numSlavePorts:  Range.Inclusive = 1 to 999) | ||||||
|   numMasterPorts: Range.Inclusive = 1 to 1, |   extends NexusNode(AHBImp)(masterFn, slaveFn, numMasterPorts, numSlavePorts) | ||||||
|   numSlavePorts:  Range.Inclusive = 1 to 1) |  | ||||||
|   extends InteriorNode(AHBImp)(masterFn, slaveFn, numMasterPorts, numSlavePorts) |  | ||||||
|  |  | ||||||
| // Nodes passed from an inner module | // Nodes passed from an inner module | ||||||
| case class AHBOutputNode() extends OutputNode(AHBImp) | case class AHBOutputNode() extends OutputNode(AHBImp) | ||||||
|   | |||||||
| @@ -9,13 +9,13 @@ import regmapper._ | |||||||
| import scala.math.{min,max} | import scala.math.{min,max} | ||||||
|  |  | ||||||
| class AHBRegisterNode(address: AddressSet, concurrency: Int = 0, beatBytes: Int = 4, undefZero: Boolean = true, executable: Boolean = false) | class AHBRegisterNode(address: AddressSet, concurrency: Int = 0, beatBytes: Int = 4, undefZero: Boolean = true, executable: Boolean = false) | ||||||
|   extends AHBSlaveNode(AHBSlavePortParameters( |   extends AHBSlaveNode(Seq(AHBSlavePortParameters( | ||||||
|     Seq(AHBSlaveParameters( |     Seq(AHBSlaveParameters( | ||||||
|       address       = Seq(address), |       address       = Seq(address), | ||||||
|       executable    = executable, |       executable    = executable, | ||||||
|       supportsWrite = TransferSizes(1, min(address.alignment.toInt, beatBytes * AHBParameters.maxTransfer)), |       supportsWrite = TransferSizes(1, min(address.alignment.toInt, beatBytes * AHBParameters.maxTransfer)), | ||||||
|       supportsRead  = TransferSizes(1, min(address.alignment.toInt, beatBytes * AHBParameters.maxTransfer)))), |       supportsRead  = TransferSizes(1, min(address.alignment.toInt, beatBytes * AHBParameters.maxTransfer)))), | ||||||
|     beatBytes  = beatBytes)) |     beatBytes  = beatBytes))) | ||||||
| { | { | ||||||
|   require (address.contiguous) |   require (address.contiguous) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -8,14 +8,14 @@ import diplomacy._ | |||||||
|  |  | ||||||
| class AHBRAM(address: AddressSet, executable: Boolean = true, beatBytes: Int = 4)(implicit p: Parameters) extends LazyModule | class AHBRAM(address: AddressSet, executable: Boolean = true, beatBytes: Int = 4)(implicit p: Parameters) extends LazyModule | ||||||
| { | { | ||||||
|   val node = AHBSlaveNode(AHBSlavePortParameters( |   val node = AHBSlaveNode(Seq(AHBSlavePortParameters( | ||||||
|     Seq(AHBSlaveParameters( |     Seq(AHBSlaveParameters( | ||||||
|       address       = List(address), |       address       = List(address), | ||||||
|       regionType    = RegionType.UNCACHED, |       regionType    = RegionType.UNCACHED, | ||||||
|       executable    = executable, |       executable    = executable, | ||||||
|       supportsRead  = TransferSizes(1, beatBytes * AHBParameters.maxTransfer), |       supportsRead  = TransferSizes(1, beatBytes * AHBParameters.maxTransfer), | ||||||
|       supportsWrite = TransferSizes(1, beatBytes * AHBParameters.maxTransfer))), |       supportsWrite = TransferSizes(1, beatBytes * AHBParameters.maxTransfer))), | ||||||
|     beatBytes  = beatBytes)) |     beatBytes  = beatBytes))) | ||||||
|  |  | ||||||
|   // We require the address range to include an entire beat (for the write mask) |   // We require the address range to include an entire beat (for the write mask) | ||||||
|   require ((address.mask & (beatBytes-1)) == beatBytes-1) |   require ((address.mask & (beatBytes-1)) == beatBytes-1) | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ import regmapper._ | |||||||
| import scala.math.{min,max} | import scala.math.{min,max} | ||||||
|  |  | ||||||
| class AHBFanout()(implicit p: Parameters) extends LazyModule { | class AHBFanout()(implicit p: Parameters) extends LazyModule { | ||||||
|   val node = AHBAdapterNode( |   val node = AHBNexusNode( | ||||||
|     numSlavePorts  = 1 to 1, |     numSlavePorts  = 1 to 1, | ||||||
|     numMasterPorts = 1 to 32, |     numMasterPorts = 1 to 32, | ||||||
|     masterFn = { case Seq(m) => m }, |     masterFn = { case Seq(m) => m }, | ||||||
|   | |||||||
| @@ -19,10 +19,6 @@ object APBImp extends NodeImp[APBMasterPortParameters, APBSlavePortParameters, A | |||||||
|   override def labelI(ei: APBEdgeParameters) = (ei.slave.beatBytes * 8).toString |   override def labelI(ei: APBEdgeParameters) = (ei.slave.beatBytes * 8).toString | ||||||
|   override def labelO(eo: APBEdgeParameters) = (eo.slave.beatBytes * 8).toString |   override def labelO(eo: APBEdgeParameters) = (eo.slave.beatBytes * 8).toString | ||||||
|  |  | ||||||
|   def connect(bo: => APBBundle, bi: => APBBundle, ei: => APBEdgeParameters)(implicit p: Parameters, sourceInfo: SourceInfo): (Option[LazyModule], () => Unit) = { |  | ||||||
|     (None, () => { bi <> bo }) |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   override def mixO(pd: APBMasterPortParameters, node: OutwardNode[APBMasterPortParameters, APBSlavePortParameters, APBBundle]): APBMasterPortParameters  = |   override def mixO(pd: APBMasterPortParameters, node: OutwardNode[APBMasterPortParameters, APBSlavePortParameters, APBBundle]): APBMasterPortParameters  = | ||||||
|    pd.copy(masters = pd.masters.map  { c => c.copy (nodePath = node +: c.nodePath) }) |    pd.copy(masters = pd.masters.map  { c => c.copy (nodePath = node +: c.nodePath) }) | ||||||
|   override def mixI(pu: APBSlavePortParameters, node: InwardNode[APBMasterPortParameters, APBSlavePortParameters, APBBundle]): APBSlavePortParameters = |   override def mixI(pu: APBSlavePortParameters, node: InwardNode[APBMasterPortParameters, APBSlavePortParameters, APBBundle]): APBSlavePortParameters = | ||||||
| @@ -31,16 +27,14 @@ object APBImp extends NodeImp[APBMasterPortParameters, APBSlavePortParameters, A | |||||||
|  |  | ||||||
| // Nodes implemented inside modules | // Nodes implemented inside modules | ||||||
| case class APBIdentityNode() extends IdentityNode(APBImp) | case class APBIdentityNode() extends IdentityNode(APBImp) | ||||||
| case class APBMasterNode(portParams: APBMasterPortParameters, numPorts: Range.Inclusive = 1 to 1) | case class APBMasterNode(portParams: Seq[APBMasterPortParameters]) extends SourceNode(APBImp)(portParams) | ||||||
|   extends SourceNode(APBImp)(portParams, numPorts) | case class APBSlaveNode(portParams: Seq[APBSlavePortParameters]) extends SinkNode(APBImp)(portParams) | ||||||
| case class APBSlaveNode(portParams: APBSlavePortParameters, numPorts: Range.Inclusive = 1 to 1) | case class APBNexusNode( | ||||||
|   extends SinkNode(APBImp)(portParams, numPorts) |   masterFn:       Seq[APBMasterPortParameters] => APBMasterPortParameters, | ||||||
| case class APBAdapterNode( |   slaveFn:        Seq[APBSlavePortParameters]  => APBSlavePortParameters, | ||||||
|   masterFn:       Seq[APBMasterPortParameters]  => APBMasterPortParameters, |  | ||||||
|   slaveFn:        Seq[APBSlavePortParameters] => APBSlavePortParameters, |  | ||||||
|   numMasterPorts: Range.Inclusive = 1 to 1, |   numMasterPorts: Range.Inclusive = 1 to 1, | ||||||
|   numSlavePorts:  Range.Inclusive = 1 to 1) |   numSlavePorts:  Range.Inclusive = 1 to 1) | ||||||
|   extends InteriorNode(APBImp)(masterFn, slaveFn, numMasterPorts, numSlavePorts) |   extends NexusNode(APBImp)(masterFn, slaveFn, numMasterPorts, numSlavePorts) | ||||||
|  |  | ||||||
| // Nodes passed from an inner module | // Nodes passed from an inner module | ||||||
| case class APBOutputNode() extends OutputNode(APBImp) | case class APBOutputNode() extends OutputNode(APBImp) | ||||||
|   | |||||||
| @@ -9,13 +9,13 @@ import regmapper._ | |||||||
| import scala.math.{min,max} | import scala.math.{min,max} | ||||||
|  |  | ||||||
| class APBRegisterNode(address: AddressSet, concurrency: Int = 0, beatBytes: Int = 4, undefZero: Boolean = true, executable: Boolean = false) | class APBRegisterNode(address: AddressSet, concurrency: Int = 0, beatBytes: Int = 4, undefZero: Boolean = true, executable: Boolean = false) | ||||||
|   extends APBSlaveNode(APBSlavePortParameters( |   extends APBSlaveNode(Seq(APBSlavePortParameters( | ||||||
|     Seq(APBSlaveParameters( |     Seq(APBSlaveParameters( | ||||||
|       address       = Seq(address), |       address       = Seq(address), | ||||||
|       executable    = executable, |       executable    = executable, | ||||||
|       supportsWrite = true, |       supportsWrite = true, | ||||||
|       supportsRead  = true)), |       supportsRead  = true)), | ||||||
|     beatBytes  = beatBytes)) |     beatBytes  = beatBytes))) | ||||||
| { | { | ||||||
|   require (address.contiguous) |   require (address.contiguous) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -8,14 +8,14 @@ import diplomacy._ | |||||||
|  |  | ||||||
| class APBRAM(address: AddressSet, executable: Boolean = true, beatBytes: Int = 4)(implicit p: Parameters) extends LazyModule | class APBRAM(address: AddressSet, executable: Boolean = true, beatBytes: Int = 4)(implicit p: Parameters) extends LazyModule | ||||||
| { | { | ||||||
|   val node = APBSlaveNode(APBSlavePortParameters( |   val node = APBSlaveNode(Seq(APBSlavePortParameters( | ||||||
|     Seq(APBSlaveParameters( |     Seq(APBSlaveParameters( | ||||||
|       address       = List(address), |       address       = List(address), | ||||||
|       regionType    = RegionType.UNCACHED, |       regionType    = RegionType.UNCACHED, | ||||||
|       executable    = executable, |       executable    = executable, | ||||||
|       supportsRead  = true, |       supportsRead  = true, | ||||||
|       supportsWrite = true)), |       supportsWrite = true)), | ||||||
|     beatBytes  = beatBytes)) |     beatBytes  = beatBytes))) | ||||||
|  |  | ||||||
|   // We require the address range to include an entire beat (for the write mask) |   // We require the address range to include an entire beat (for the write mask) | ||||||
|   require ((address.mask & (beatBytes-1)) == beatBytes-1) |   require ((address.mask & (beatBytes-1)) == beatBytes-1) | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ import regmapper._ | |||||||
| import scala.math.{min,max} | import scala.math.{min,max} | ||||||
|  |  | ||||||
| class APBFanout()(implicit p: Parameters) extends LazyModule { | class APBFanout()(implicit p: Parameters) extends LazyModule { | ||||||
|   val node = APBAdapterNode( |   val node = APBNexusNode( | ||||||
|     numSlavePorts  = 1 to 1, |     numSlavePorts  = 1 to 1, | ||||||
|     numMasterPorts = 1 to 32, |     numMasterPorts = 1 to 32, | ||||||
|     masterFn = { case Seq(m) => m }, |     masterFn = { case Seq(m) => m }, | ||||||
|   | |||||||
| @@ -18,8 +18,8 @@ class AXI4Buffer(aw: Int = 2, w: Int = 2, b: Int = 2, ar: Int = 2, r: Int = 2, p | |||||||
|   require (r  >= 0) |   require (r  >= 0) | ||||||
|  |  | ||||||
|   val node = AXI4AdapterNode( |   val node = AXI4AdapterNode( | ||||||
|     masterFn = { case Seq(p) => p }, |     masterFn = { p => p }, | ||||||
|     slaveFn  = { case Seq(p) => p.copy(minLatency = p.minLatency + min(1,min(aw,ar)) + min(1,min(r,b))) }) |     slaveFn  = { p => p.copy(minLatency = p.minLatency + min(1,min(aw,ar)) + min(1,min(r,b))) }) | ||||||
|  |  | ||||||
|   lazy val module = new LazyModuleImp(this) { |   lazy val module = new LazyModuleImp(this) { | ||||||
|     val io = new Bundle { |     val io = new Bundle { | ||||||
|   | |||||||
| @@ -23,8 +23,8 @@ class AXI4Fragmenter(lite: Boolean = false, maxInFlight: => Int = 32, combinatio | |||||||
|   def mapMaster(m: AXI4MasterParameters) = m.copy(aligned = true) |   def mapMaster(m: AXI4MasterParameters) = m.copy(aligned = true) | ||||||
|  |  | ||||||
|   val node = AXI4AdapterNode( |   val node = AXI4AdapterNode( | ||||||
|     masterFn = { case Seq(mp) => mp.copy(masters = mp.masters.map(m => mapMaster(m))) }, |     masterFn = { mp => mp.copy(masters = mp.masters.map(m => mapMaster(m))) }, | ||||||
|     slaveFn  = { case Seq(sp) => sp.copy(slaves  = sp.slaves .map(s => mapSlave(s, sp.beatBytes))) }) |     slaveFn  = { sp => sp.copy(slaves  = sp.slaves .map(s => mapSlave(s, sp.beatBytes))) }) | ||||||
|  |  | ||||||
|   lazy val module = new LazyModuleImp(this) { |   lazy val module = new LazyModuleImp(this) { | ||||||
|     val io = new Bundle { |     val io = new Bundle { | ||||||
| @@ -32,256 +32,253 @@ class AXI4Fragmenter(lite: Boolean = false, maxInFlight: => Int = 32, combinatio | |||||||
|       val out = node.bundleOut |       val out = node.bundleOut | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     val edgeOut   = node.edgesOut(0) |     ((io.in zip io.out) zip (node.edgesIn zip node.edgesOut)) foreach { case ((in, out), (edgeIn, edgeOut)) => | ||||||
|     val edgeIn    = node.edgesIn(0) |       val slave     = edgeOut.slave | ||||||
|     val slave     = edgeOut.slave |       val slaves    = slave.slaves | ||||||
|     val slaves    = slave.slaves |       val beatBytes = slave.beatBytes | ||||||
|     val beatBytes = slave.beatBytes |       val lgBytes   = log2Ceil(beatBytes) | ||||||
|     val lgBytes   = log2Ceil(beatBytes) |       val master    = edgeIn.master | ||||||
|     val master    = edgeIn.master |       val masters   = master.masters | ||||||
|     val masters   = master.masters |  | ||||||
|  |  | ||||||
|     // If the user claimed this was a lite interface, then there must be only one Id |       // If the user claimed this was a lite interface, then there must be only one Id | ||||||
|     require (!lite || master.endId == 1) |       require (!lite || master.endId == 1) | ||||||
|  |  | ||||||
|     // We don't support fragmenting to sub-beat accesses |       // We don't support fragmenting to sub-beat accesses | ||||||
|     slaves.foreach { s => |       slaves.foreach { s => | ||||||
|       require (!s.supportsRead  || s.supportsRead.contains(beatBytes)) |         require (!s.supportsRead  || s.supportsRead.contains(beatBytes)) | ||||||
|       require (!s.supportsWrite || s.supportsWrite.contains(beatBytes)) |         require (!s.supportsWrite || s.supportsWrite.contains(beatBytes)) | ||||||
|     } |       } | ||||||
|  |  | ||||||
|     /* We need to decompose a request into  |       /* We need to decompose a request into  | ||||||
|      *   FIXED => each beat is a new request |        *   FIXED => each beat is a new request | ||||||
|      *   WRAP/INCR => take xfr up to next power of two, capped by max size of target |        *   WRAP/INCR => take xfr up to next power of two, capped by max size of target | ||||||
|      * |        * | ||||||
|      * On AR and AW, we fragment one request into many |        * On AR and AW, we fragment one request into many | ||||||
|      * On W we set 'last' on beats which are fragment boundaries |        * On W we set 'last' on beats which are fragment boundaries | ||||||
|      * On R we clear 'last' on the fragments being reassembled |        * On R we clear 'last' on the fragments being reassembled | ||||||
|      * On B we clear 'valid' on the responses for the injected fragments |        * On B we clear 'valid' on the responses for the injected fragments | ||||||
|      * |        * | ||||||
|      * AR=>R and AW+W=>B are completely independent state machines. |        * AR=>R and AW+W=>B are completely independent state machines. | ||||||
|      */ |  | ||||||
|  |  | ||||||
|     /* Returns the number of beats to execute and the new address */ |  | ||||||
|     def fragment(a: IrrevocableIO[AXI4BundleA], supportedSizes1: Seq[Int]): (IrrevocableIO[AXI4BundleA], Bool, UInt) = { |  | ||||||
|       val out = Wire(a) |  | ||||||
|  |  | ||||||
|       val busy   = RegInit(Bool(false)) |  | ||||||
|       val r_addr = Reg(UInt(width = a.bits.params.addrBits)) |  | ||||||
|       val r_len  = Reg(UInt(width = AXI4Parameters.lenBits)) |  | ||||||
|  |  | ||||||
|       val len  = Mux(busy, r_len,  a.bits.len) |  | ||||||
|       val addr = Mux(busy, r_addr, a.bits.addr) |  | ||||||
|  |  | ||||||
|       val lo = if (lgBytes == 0) UInt(0) else addr(lgBytes-1, 0) |  | ||||||
|       val hi = addr >> lgBytes |  | ||||||
|       val alignment = hi(AXI4Parameters.lenBits-1,0) |  | ||||||
|  |  | ||||||
|       val allSame = supportedSizes1.filter(_ >= 0).distinct.size <= 1 |  | ||||||
|       val dynamic1 = Mux1H(slave.findFast(addr), supportedSizes1.map(s => UInt(max(0, s)))) |  | ||||||
|       val fixed1 = UInt(supportedSizes1.filter(_ >= 0).headOption.getOrElse(0)) |  | ||||||
|  |  | ||||||
|       /* We need to compute the largest transfer allowed by the AXI len. |  | ||||||
|        * len+1 is the number of beats to execute. |  | ||||||
|        * We want the MSB(len+1)-1; one less than the largest power of two we could execute. |  | ||||||
|        * There are two cases; either len is 2^n-1 in which case we leave it unchanged, ELSE |  | ||||||
|        *   fill the bits from highest to lowest, and shift right by one bit. |  | ||||||
|        */ |        */ | ||||||
|       val fillLow  = rightOR(len) >> 1   // set   all bits in positions <  a set     bit |  | ||||||
|       val wipeHigh = ~leftOR(~len)       // clear all bits in position  >= a cleared bit |  | ||||||
|       val remain1  = fillLow | wipeHigh  // MSB(a.len+1)-1 |  | ||||||
|       val align1   = ~leftOR(alignment)  // transfer size limited by address alignment |  | ||||||
|       val support1 = if (allSame) fixed1 else dynamic1 // maximum supported size-1 based on target address |  | ||||||
|       val maxSupported1 = remain1 & align1 & support1 // Take the minimum of all the limits |  | ||||||
|  |  | ||||||
|       // Things that cause us to degenerate to a single beat |       /* Returns the number of beats to execute and the new address */ | ||||||
|       val fixed = a.bits.burst === AXI4Parameters.BURST_FIXED |       def fragment(a: IrrevocableIO[AXI4BundleA], supportedSizes1: Seq[Int]): (IrrevocableIO[AXI4BundleA], Bool, UInt) = { | ||||||
|       val narrow = a.bits.size =/= UInt(lgBytes) |         val out = Wire(a) | ||||||
|       val bad = fixed || narrow |  | ||||||
|  |  | ||||||
|       // The number of beats-1 to execute |         val busy   = RegInit(Bool(false)) | ||||||
|       val beats1 = Mux(bad, UInt(0), maxSupported1) |         val r_addr = Reg(UInt(width = a.bits.params.addrBits)) | ||||||
|       val beats = OH1ToOH(beats1) // beats1 + 1 |         val r_len  = Reg(UInt(width = AXI4Parameters.lenBits)) | ||||||
|  |  | ||||||
|       val inc_addr = addr + (beats << a.bits.size) // address after adding transfer |         val len  = Mux(busy, r_len,  a.bits.len) | ||||||
|       val wrapMask = a.bits.bytes1() // only these bits may change, if wrapping |         val addr = Mux(busy, r_addr, a.bits.addr) | ||||||
|       val mux_addr = Wire(init = inc_addr) |  | ||||||
|       when (a.bits.burst === AXI4Parameters.BURST_WRAP) { |         val lo = if (lgBytes == 0) UInt(0) else addr(lgBytes-1, 0) | ||||||
|         mux_addr := (inc_addr & wrapMask) | ~(~a.bits.addr | wrapMask) |         val hi = addr >> lgBytes | ||||||
|       } |         val alignment = hi(AXI4Parameters.lenBits-1,0) | ||||||
|       when (a.bits.burst === AXI4Parameters.BURST_FIXED) { |  | ||||||
|         mux_addr := a.bits.addr |         val allSame = supportedSizes1.filter(_ >= 0).distinct.size <= 1 | ||||||
|  |         val dynamic1 = Mux1H(slave.findFast(addr), supportedSizes1.map(s => UInt(max(0, s)))) | ||||||
|  |         val fixed1 = UInt(supportedSizes1.filter(_ >= 0).headOption.getOrElse(0)) | ||||||
|  |  | ||||||
|  |         /* We need to compute the largest transfer allowed by the AXI len. | ||||||
|  |          * len+1 is the number of beats to execute. | ||||||
|  |          * We want the MSB(len+1)-1; one less than the largest power of two we could execute. | ||||||
|  |          * There are two cases; either len is 2^n-1 in which case we leave it unchanged, ELSE | ||||||
|  |          *   fill the bits from highest to lowest, and shift right by one bit. | ||||||
|  |          */ | ||||||
|  |         val fillLow  = rightOR(len) >> 1   // set   all bits in positions <  a set     bit | ||||||
|  |         val wipeHigh = ~leftOR(~len)       // clear all bits in position  >= a cleared bit | ||||||
|  |         val remain1  = fillLow | wipeHigh  // MSB(a.len+1)-1 | ||||||
|  |         val align1   = ~leftOR(alignment)  // transfer size limited by address alignment | ||||||
|  |         val support1 = if (allSame) fixed1 else dynamic1 // maximum supported size-1 based on target address | ||||||
|  |         val maxSupported1 = remain1 & align1 & support1 // Take the minimum of all the limits | ||||||
|  |  | ||||||
|  |         // Things that cause us to degenerate to a single beat | ||||||
|  |         val fixed = a.bits.burst === AXI4Parameters.BURST_FIXED | ||||||
|  |         val narrow = a.bits.size =/= UInt(lgBytes) | ||||||
|  |         val bad = fixed || narrow | ||||||
|  |  | ||||||
|  |         // The number of beats-1 to execute | ||||||
|  |         val beats1 = Mux(bad, UInt(0), maxSupported1) | ||||||
|  |         val beats = OH1ToOH(beats1) // beats1 + 1 | ||||||
|  |  | ||||||
|  |         val inc_addr = addr + (beats << a.bits.size) // address after adding transfer | ||||||
|  |         val wrapMask = a.bits.bytes1() // only these bits may change, if wrapping | ||||||
|  |         val mux_addr = Wire(init = inc_addr) | ||||||
|  |         when (a.bits.burst === AXI4Parameters.BURST_WRAP) { | ||||||
|  |           mux_addr := (inc_addr & wrapMask) | ~(~a.bits.addr | wrapMask) | ||||||
|  |         } | ||||||
|  |         when (a.bits.burst === AXI4Parameters.BURST_FIXED) { | ||||||
|  |           mux_addr := a.bits.addr | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         val last = beats1 === len | ||||||
|  |         a.ready := out.ready && last | ||||||
|  |         out.valid := a.valid | ||||||
|  |  | ||||||
|  |         out.bits := a.bits | ||||||
|  |         out.bits.len := beats1 | ||||||
|  |  | ||||||
|  |         // We forcibly align every access. If the first beat was misaligned, the strb bits | ||||||
|  |         // for the lower addresses must not have been set. Therefore, rounding the address | ||||||
|  |         // down is harmless. We can do this after the address update algorithm, because the | ||||||
|  |         // incremented values will be rounded down the same way. Furthermore, a subword | ||||||
|  |         // offset cannot cause a premature wrap-around. | ||||||
|  |         out.bits.addr := ~(~addr | UIntToOH1(a.bits.size, lgBytes)) | ||||||
|  |  | ||||||
|  |         when (out.fire()) { | ||||||
|  |           busy := !last | ||||||
|  |           r_addr := mux_addr | ||||||
|  |           r_len  := len - beats | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         (out, last, beats) | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       val last = beats1 === len |       // The size to which we will fragment the access | ||||||
|       a.ready := out.ready && last |       val readSizes1  = slaves.map(s => s.supportsRead .max/beatBytes-1) | ||||||
|       out.valid := a.valid |       val writeSizes1 = slaves.map(s => s.supportsWrite.max/beatBytes-1) | ||||||
|  |  | ||||||
|       out.bits := a.bits |       // Indirection variables for inputs and outputs; makes transformation application easier | ||||||
|       out.bits.len := beats1 |       val (in_ar, ar_last, _)       = fragment(Queue.irrevocable(in.ar, 1, flow=true), readSizes1) | ||||||
|  |       val (in_aw, aw_last, w_beats) = fragment(Queue.irrevocable(in.aw, 1, flow=true), writeSizes1) | ||||||
|  |       val in_w = in.w | ||||||
|  |       val in_r = in.r | ||||||
|  |       val in_b = in.b | ||||||
|  |       val out_ar = Wire(out.ar) | ||||||
|  |       val out_aw = out.aw | ||||||
|  |       val out_w = out.w | ||||||
|  |       val out_r = Wire(out.r) | ||||||
|  |       val out_b = Wire(out.b) | ||||||
|  |  | ||||||
|       // We forcibly align every access. If the first beat was misaligned, the strb bits |       val depth = if (combinational) 1 else 2 | ||||||
|       // for the lower addresses must not have been set. Therefore, rounding the address |       // In case a slave ties arready := rready, we need a queue to break the combinational loop | ||||||
|       // down is harmless. We can do this after the address update algorithm, because the |       // between the two branches (in_ar => {out_ar => out_r, sideband} => in_r). | ||||||
|       // incremented values will be rounded down the same way. Furthermore, a subword |       if (in.ar.bits.getWidth < in.r.bits.getWidth) { | ||||||
|       // offset cannot cause a premature wrap-around. |         out.ar <> Queue(out_ar, depth, flow=combinational) | ||||||
|       out.bits.addr := ~(~addr | UIntToOH1(a.bits.size, lgBytes)) |         out_r <> out.r | ||||||
|  |       } else { | ||||||
|       when (out.fire()) { |         out.ar <> out_ar | ||||||
|         busy := !last |         out_r <> Queue(out.r, depth, flow=combinational) | ||||||
|         r_addr := mux_addr |  | ||||||
|         r_len  := len - beats |  | ||||||
|       } |       } | ||||||
|  |       // In case a slave ties awready := bready or wready := bready, we need this queue | ||||||
|  |       out_b <> Queue(out.b, depth, flow=combinational) | ||||||
|  |  | ||||||
|       (out, last, beats) |       // Sideband to track which transfers were the last fragment | ||||||
|     } |       def sideband() = if (lite) { | ||||||
|  |         Module(new Queue(Bool(), maxInFlight, flow=combinational)).io | ||||||
|  |       } else { | ||||||
|  |         Module(new AXI4FragmenterSideband(maxInFlight, flow=combinational)).io | ||||||
|  |       } | ||||||
|  |       val sideband_ar_r = sideband() | ||||||
|  |       val sideband_aw_b = sideband() | ||||||
|  |  | ||||||
|     val in = io.in(0) |       // AR flow control | ||||||
|     val out = io.out(0) |       out_ar.valid := in_ar.valid && sideband_ar_r.enq.ready | ||||||
|  |       in_ar.ready := sideband_ar_r.enq.ready && out_ar.ready | ||||||
|  |       sideband_ar_r.enq.valid := in_ar.valid && out_ar.ready | ||||||
|  |       out_ar.bits := in_ar.bits | ||||||
|  |       sideband_ar_r.enq.bits := ar_last | ||||||
|  |  | ||||||
|     // The size to which we will fragment the access |       // When does W channel start counting a new transfer | ||||||
|     val readSizes1  = slaves.map(s => s.supportsRead .max/beatBytes-1) |       val wbeats_latched = RegInit(Bool(false)) | ||||||
|     val writeSizes1 = slaves.map(s => s.supportsWrite.max/beatBytes-1) |       val wbeats_ready = Wire(Bool()) | ||||||
|  |       val wbeats_valid = Wire(Bool()) | ||||||
|  |       when (wbeats_valid && wbeats_ready) { wbeats_latched := Bool(true) } | ||||||
|  |       when (out_aw.fire()) { wbeats_latched := Bool(false) } | ||||||
|  |  | ||||||
|     // Indirection variables for inputs and outputs; makes transformation application easier |       // AW flow control | ||||||
|     val (in_ar, ar_last, _)       = fragment(Queue.irrevocable(in.ar, 1, flow=true), readSizes1) |       out_aw.valid := in_aw.valid && sideband_aw_b.enq.ready && (wbeats_ready || wbeats_latched) | ||||||
|     val (in_aw, aw_last, w_beats) = fragment(Queue.irrevocable(in.aw, 1, flow=true), writeSizes1) |       in_aw.ready := sideband_aw_b.enq.ready && out_aw.ready && (wbeats_ready || wbeats_latched) | ||||||
|     val in_w = in.w |       sideband_aw_b.enq.valid := in_aw.valid && out_aw.ready && (wbeats_ready || wbeats_latched) | ||||||
|     val in_r = in.r |       wbeats_valid := in_aw.valid && !wbeats_latched | ||||||
|     val in_b = in.b |       out_aw.bits := in_aw.bits | ||||||
|     val out_ar = Wire(out.ar) |       sideband_aw_b.enq.bits := aw_last | ||||||
|     val out_aw = out.aw |  | ||||||
|     val out_w = out.w |  | ||||||
|     val out_r = Wire(out.r) |  | ||||||
|     val out_b = Wire(out.b) |  | ||||||
|  |  | ||||||
|     val depth = if (combinational) 1 else 2 |       // We need to inject 'last' into the W channel fragments, count! | ||||||
|     // In case a slave ties arready := rready, we need a queue to break the combinational loop |       val w_counter = RegInit(UInt(0, width = AXI4Parameters.lenBits+1)) | ||||||
|     // between the two branches (in_ar => {out_ar => out_r, sideband} => in_r). |       val w_idle = w_counter === UInt(0) | ||||||
|     if (in.ar.bits.getWidth < in.r.bits.getWidth) { |       val w_todo = Mux(w_idle, Mux(wbeats_valid, w_beats, UInt(0)), w_counter) | ||||||
|       out.ar <> Queue(out_ar, depth, flow=combinational) |       val w_last = w_todo === UInt(1) | ||||||
|       out_r <> out.r |       w_counter := w_todo - out_w.fire() | ||||||
|     } else { |       assert (!out_w.fire() || w_todo =/= UInt(0)) // underflow impossible | ||||||
|       out.ar <> out_ar |  | ||||||
|       out_r <> Queue(out.r, depth, flow=combinational) |  | ||||||
|     } |  | ||||||
|     // In case a slave ties awready := bready or wready := bready, we need this queue |  | ||||||
|     out_b <> Queue(out.b, depth, flow=combinational) |  | ||||||
|  |  | ||||||
|     // Sideband to track which transfers were the last fragment |       // W flow control | ||||||
|     def sideband() = if (lite) { |       wbeats_ready := w_idle | ||||||
|       Module(new Queue(Bool(), maxInFlight, flow=combinational)).io |       out_w.valid := in_w.valid && (!wbeats_ready || wbeats_valid) | ||||||
|     } else { |       in_w.ready := out_w.ready && (!wbeats_ready || wbeats_valid) | ||||||
|       Module(new AXI4FragmenterSideband(maxInFlight, flow=combinational)).io |       out_w.bits := in_w.bits | ||||||
|     } |       out_w.bits.last := w_last | ||||||
|     val sideband_ar_r = sideband() |       // We should also recreate the last last | ||||||
|     val sideband_aw_b = sideband() |       assert (!out_w.valid || !in_w.bits.last || w_last) | ||||||
|  |  | ||||||
|     // AR flow control |       // R flow control | ||||||
|     out_ar.valid := in_ar.valid && sideband_ar_r.enq.ready |       val r_last = out_r.bits.last | ||||||
|     in_ar.ready := sideband_ar_r.enq.ready && out_ar.ready |       in_r.valid := out_r.valid && (!r_last || sideband_ar_r.deq.valid) | ||||||
|     sideband_ar_r.enq.valid := in_ar.valid && out_ar.ready |       out_r.ready := in_r.ready && (!r_last || sideband_ar_r.deq.valid) | ||||||
|     out_ar.bits := in_ar.bits |       sideband_ar_r.deq.ready := r_last && out_r.valid && in_r.ready | ||||||
|     sideband_ar_r.enq.bits := ar_last |       in_r.bits := out_r.bits | ||||||
|  |       in_r.bits.last := r_last && sideband_ar_r.deq.bits | ||||||
|  |  | ||||||
|     // When does W channel start counting a new transfer |       // B flow control | ||||||
|     val wbeats_latched = RegInit(Bool(false)) |       val b_last = sideband_aw_b.deq.bits | ||||||
|     val wbeats_ready = Wire(Bool()) |       in_b.valid := out_b.valid && sideband_aw_b.deq.valid && b_last | ||||||
|     val wbeats_valid = Wire(Bool()) |       out_b.ready := sideband_aw_b.deq.valid && (!b_last || in_b.ready) | ||||||
|     when (wbeats_valid && wbeats_ready) { wbeats_latched := Bool(true) } |       sideband_aw_b.deq.ready := out_b.valid && (!b_last || in_b.ready) | ||||||
|     when (out_aw.fire()) { wbeats_latched := Bool(false) } |       in_b.bits := out_b.bits | ||||||
|  |  | ||||||
|     // AW flow control |       // Merge errors from dropped B responses | ||||||
|     out_aw.valid := in_aw.valid && sideband_aw_b.enq.ready && (wbeats_ready || wbeats_latched) |       val r_resp = RegInit(UInt(0, width = AXI4Parameters.respBits)) | ||||||
|     in_aw.ready := sideband_aw_b.enq.ready && out_aw.ready && (wbeats_ready || wbeats_latched) |       val resp = out_b.bits.resp | r_resp | ||||||
|     sideband_aw_b.enq.valid := in_aw.valid && out_aw.ready && (wbeats_ready || wbeats_latched) |       when (out_b.fire()) { r_resp := Mux(b_last, UInt(0), resp) } | ||||||
|     wbeats_valid := in_aw.valid && !wbeats_latched |       in_b.bits.resp := resp | ||||||
|     out_aw.bits := in_aw.bits |  | ||||||
|     sideband_aw_b.enq.bits := aw_last |  | ||||||
|  |  | ||||||
|     // We need to inject 'last' into the W channel fragments, count! |  | ||||||
|     val w_counter = RegInit(UInt(0, width = AXI4Parameters.lenBits+1)) |  | ||||||
|     val w_idle = w_counter === UInt(0) |  | ||||||
|     val w_todo = Mux(w_idle, Mux(wbeats_valid, w_beats, UInt(0)), w_counter) |  | ||||||
|     val w_last = w_todo === UInt(1) |  | ||||||
|     w_counter := w_todo - out_w.fire() |  | ||||||
|     assert (!out_w.fire() || w_todo =/= UInt(0)) // underflow impossible |  | ||||||
|  |  | ||||||
|     // W flow control |  | ||||||
|     wbeats_ready := w_idle |  | ||||||
|     out_w.valid := in_w.valid && (!wbeats_ready || wbeats_valid) |  | ||||||
|     in_w.ready := out_w.ready && (!wbeats_ready || wbeats_valid) |  | ||||||
|     out_w.bits := in_w.bits |  | ||||||
|     out_w.bits.last := w_last |  | ||||||
|     // We should also recreate the last last |  | ||||||
|     assert (!out_w.valid || !in_w.bits.last || w_last) |  | ||||||
|  |  | ||||||
|     // R flow control |  | ||||||
|     val r_last = out_r.bits.last |  | ||||||
|     in_r.valid := out_r.valid && (!r_last || sideband_ar_r.deq.valid) |  | ||||||
|     out_r.ready := in_r.ready && (!r_last || sideband_ar_r.deq.valid) |  | ||||||
|     sideband_ar_r.deq.ready := r_last && out_r.valid && in_r.ready |  | ||||||
|     in_r.bits := out_r.bits |  | ||||||
|     in_r.bits.last := r_last && sideband_ar_r.deq.bits |  | ||||||
|  |  | ||||||
|     // B flow control |  | ||||||
|     val b_last = sideband_aw_b.deq.bits |  | ||||||
|     in_b.valid := out_b.valid && sideband_aw_b.deq.valid && b_last |  | ||||||
|     out_b.ready := sideband_aw_b.deq.valid && (!b_last || in_b.ready) |  | ||||||
|     sideband_aw_b.deq.ready := out_b.valid && (!b_last || in_b.ready) |  | ||||||
|     in_b.bits := out_b.bits |  | ||||||
|  |  | ||||||
|     // Merge errors from dropped B responses |  | ||||||
|     val r_resp = RegInit(UInt(0, width = AXI4Parameters.respBits)) |  | ||||||
|     val resp = out_b.bits.resp | r_resp |  | ||||||
|     when (out_b.fire()) { r_resp := Mux(b_last, UInt(0), resp) } |  | ||||||
|     in_b.bits.resp := resp |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* We want to put barriers between the fragments of a fragmented transfer and all other transfers. |  | ||||||
|  * This lets us use very little state to reassemble the fragments (else we need one FIFO per ID). |  | ||||||
|  * Furthermore, because all the fragments share the same AXI ID, they come back contiguously. |  | ||||||
|  * This guarantees that no other R responses might get mixed between fragments, ensuring that the |  | ||||||
|  * interleavedId for the slaves remains unaffected by the fragmentation transformation. |  | ||||||
|  * Of course, if you need to fragment, this means there is a potentially hefty serialization cost. |  | ||||||
|  * However, this design allows full concurrency in the common no-fragmentation-needed scenario. |  | ||||||
|  */ |  | ||||||
| class AXI4FragmenterSideband(maxInFlight: Int, flow: Boolean = false) extends Module |  | ||||||
| { |  | ||||||
|   val io = new QueueIO(Bool(), maxInFlight) |  | ||||||
|   io.count := UInt(0) |  | ||||||
|  |  | ||||||
|   val PASS = UInt(2, width = 2) // allow 'last=1' bits to enque, on 'last=0' if count>0 block else accept+FIND |  | ||||||
|   val FIND = UInt(0, width = 2) // allow 'last=0' bits to enque, accept 'last=1' and switch to WAIT |  | ||||||
|   val WAIT = UInt(1, width = 2) // block all access till count=0 |  | ||||||
|  |  | ||||||
|   val state = RegInit(PASS) |  | ||||||
|   val count = RegInit(UInt(0, width = log2Up(maxInFlight))) |  | ||||||
|   val full  = count === UInt(maxInFlight-1) |  | ||||||
|   val empty = count === UInt(0) |  | ||||||
|   val last  = count === UInt(1) |  | ||||||
|  |  | ||||||
|   io.deq.bits := state(1) || (last && state(0)) // PASS || (last && WAIT) |  | ||||||
|   io.deq.valid := !empty |  | ||||||
|  |  | ||||||
|   io.enq.ready := !full && (empty || (state === FIND) || (state === PASS && io.enq.bits)) |  | ||||||
|  |  | ||||||
|   // WAIT => count > 0 |  | ||||||
|   assert (state =/= WAIT || count =/= UInt(0)) |  | ||||||
|  |  | ||||||
|   if (flow) { |  | ||||||
|     when (io.enq.valid) { |  | ||||||
|       io.deq.valid := Bool(true) |  | ||||||
|       when (empty) { io.deq.bits := io.enq.bits } |  | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   count := count + io.enq.fire() - io.deq.fire() |   /* We want to put barriers between the fragments of a fragmented transfer and all other transfers. | ||||||
|   switch (state) { |    * This lets us use very little state to reassemble the fragments (else we need one FIFO per ID). | ||||||
|     is(PASS) { when (io.enq.valid && !io.enq.bits && empty) { state := FIND } } |    * Furthermore, because all the fragments share the same AXI ID, they come back contiguously. | ||||||
|     is(FIND) { when (io.enq.valid &&  io.enq.bits && !full) { state := Mux(empty, PASS, WAIT) } } |    * This guarantees that no other R responses might get mixed between fragments, ensuring that the | ||||||
|     is(WAIT) { when (last && io.deq.ready)                  { state := PASS } } |    * interleavedId for the slaves remains unaffected by the fragmentation transformation. | ||||||
|  |    * Of course, if you need to fragment, this means there is a potentially hefty serialization cost. | ||||||
|  |    * However, this design allows full concurrency in the common no-fragmentation-needed scenario. | ||||||
|  |    */ | ||||||
|  |   class AXI4FragmenterSideband(maxInFlight: Int, flow: Boolean = false) extends Module | ||||||
|  |   { | ||||||
|  |     val io = new QueueIO(Bool(), maxInFlight) | ||||||
|  |     io.count := UInt(0) | ||||||
|  |  | ||||||
|  |     val PASS = UInt(2, width = 2) // allow 'last=1' bits to enque, on 'last=0' if count>0 block else accept+FIND | ||||||
|  |     val FIND = UInt(0, width = 2) // allow 'last=0' bits to enque, accept 'last=1' and switch to WAIT | ||||||
|  |     val WAIT = UInt(1, width = 2) // block all access till count=0 | ||||||
|  |  | ||||||
|  |     val state = RegInit(PASS) | ||||||
|  |     val count = RegInit(UInt(0, width = log2Up(maxInFlight))) | ||||||
|  |     val full  = count === UInt(maxInFlight-1) | ||||||
|  |     val empty = count === UInt(0) | ||||||
|  |     val last  = count === UInt(1) | ||||||
|  |  | ||||||
|  |     io.deq.bits := state(1) || (last && state(0)) // PASS || (last && WAIT) | ||||||
|  |     io.deq.valid := !empty | ||||||
|  |  | ||||||
|  |     io.enq.ready := !full && (empty || (state === FIND) || (state === PASS && io.enq.bits)) | ||||||
|  |  | ||||||
|  |     // WAIT => count > 0 | ||||||
|  |     assert (state =/= WAIT || count =/= UInt(0)) | ||||||
|  |  | ||||||
|  |     if (flow) { | ||||||
|  |       when (io.enq.valid) { | ||||||
|  |         io.deq.valid := Bool(true) | ||||||
|  |         when (empty) { io.deq.bits := io.enq.bits } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     count := count + io.enq.fire() - io.deq.fire() | ||||||
|  |     switch (state) { | ||||||
|  |       is(PASS) { when (io.enq.valid && !io.enq.bits && empty) { state := FIND } } | ||||||
|  |       is(FIND) { when (io.enq.valid &&  io.enq.bits && !full) { state := Mux(empty, PASS, WAIT) } } | ||||||
|  |       is(WAIT) { when (last && io.deq.ready)                  { state := PASS } } | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -19,10 +19,6 @@ object AXI4Imp extends NodeImp[AXI4MasterPortParameters, AXI4SlavePortParameters | |||||||
|   override def labelI(ei: AXI4EdgeParameters) = (ei.slave.beatBytes * 8).toString |   override def labelI(ei: AXI4EdgeParameters) = (ei.slave.beatBytes * 8).toString | ||||||
|   override def labelO(eo: AXI4EdgeParameters) = (eo.slave.beatBytes * 8).toString |   override def labelO(eo: AXI4EdgeParameters) = (eo.slave.beatBytes * 8).toString | ||||||
|  |  | ||||||
|   def connect(bo: => AXI4Bundle, bi: => AXI4Bundle, ei: => AXI4EdgeParameters)(implicit p: Parameters, sourceInfo: SourceInfo): (Option[LazyModule], () => Unit) = { |  | ||||||
|     (None, () => { bi <> bo }) |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   override def mixO(pd: AXI4MasterPortParameters, node: OutwardNode[AXI4MasterPortParameters, AXI4SlavePortParameters, AXI4Bundle]): AXI4MasterPortParameters  = |   override def mixO(pd: AXI4MasterPortParameters, node: OutwardNode[AXI4MasterPortParameters, AXI4SlavePortParameters, AXI4Bundle]): AXI4MasterPortParameters  = | ||||||
|    pd.copy(masters = pd.masters.map  { c => c.copy (nodePath = node +: c.nodePath) }) |    pd.copy(masters = pd.masters.map  { c => c.copy (nodePath = node +: c.nodePath) }) | ||||||
|   override def mixI(pu: AXI4SlavePortParameters, node: InwardNode[AXI4MasterPortParameters, AXI4SlavePortParameters, AXI4Bundle]): AXI4SlavePortParameters = |   override def mixI(pu: AXI4SlavePortParameters, node: InwardNode[AXI4MasterPortParameters, AXI4SlavePortParameters, AXI4Bundle]): AXI4SlavePortParameters = | ||||||
| @@ -31,16 +27,13 @@ object AXI4Imp extends NodeImp[AXI4MasterPortParameters, AXI4SlavePortParameters | |||||||
|  |  | ||||||
| // Nodes implemented inside modules | // Nodes implemented inside modules | ||||||
| case class AXI4IdentityNode() extends IdentityNode(AXI4Imp) | case class AXI4IdentityNode() extends IdentityNode(AXI4Imp) | ||||||
| case class AXI4MasterNode(portParams: AXI4MasterPortParameters, numPorts: Range.Inclusive = 1 to 1) | case class AXI4MasterNode(portParams: Seq[AXI4MasterPortParameters]) extends SourceNode(AXI4Imp)(portParams) | ||||||
|   extends SourceNode(AXI4Imp)(portParams, numPorts) | case class AXI4SlaveNode(portParams: Seq[AXI4SlavePortParameters]) extends SinkNode(AXI4Imp)(portParams) | ||||||
| case class AXI4SlaveNode(portParams: AXI4SlavePortParameters, numPorts: Range.Inclusive = 1 to 1) |  | ||||||
|   extends SinkNode(AXI4Imp)(portParams, numPorts) |  | ||||||
| case class AXI4AdapterNode( | case class AXI4AdapterNode( | ||||||
|   masterFn:       Seq[AXI4MasterPortParameters]  => AXI4MasterPortParameters, |   masterFn:  AXI4MasterPortParameters => AXI4MasterPortParameters, | ||||||
|   slaveFn:        Seq[AXI4SlavePortParameters] => AXI4SlavePortParameters, |   slaveFn:   AXI4SlavePortParameters  => AXI4SlavePortParameters, | ||||||
|   numMasterPorts: Range.Inclusive = 1 to 1, |   numPorts:  Range.Inclusive = 0 to 999) | ||||||
|   numSlavePorts:  Range.Inclusive = 1 to 1) |   extends AdapterNode(AXI4Imp)(masterFn, slaveFn, numPorts) | ||||||
|   extends InteriorNode(AXI4Imp)(masterFn, slaveFn, numMasterPorts, numSlavePorts) |  | ||||||
|  |  | ||||||
| // Nodes passed from an inner module | // Nodes passed from an inner module | ||||||
| case class AXI4OutputNode() extends OutputNode(AXI4Imp) | case class AXI4OutputNode() extends OutputNode(AXI4Imp) | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ import regmapper._ | |||||||
| import scala.math.{min,max} | import scala.math.{min,max} | ||||||
|  |  | ||||||
| class AXI4RegisterNode(address: AddressSet, concurrency: Int = 0, beatBytes: Int = 4, undefZero: Boolean = true, executable: Boolean = false) | class AXI4RegisterNode(address: AddressSet, concurrency: Int = 0, beatBytes: Int = 4, undefZero: Boolean = true, executable: Boolean = false) | ||||||
|   extends AXI4SlaveNode(AXI4SlavePortParameters( |   extends AXI4SlaveNode(Seq(AXI4SlavePortParameters( | ||||||
|     Seq(AXI4SlaveParameters( |     Seq(AXI4SlaveParameters( | ||||||
|       address       = Seq(address), |       address       = Seq(address), | ||||||
|       executable    = executable, |       executable    = executable, | ||||||
| @@ -17,7 +17,7 @@ class AXI4RegisterNode(address: AddressSet, concurrency: Int = 0, beatBytes: Int | |||||||
|       supportsRead  = TransferSizes(1, beatBytes), |       supportsRead  = TransferSizes(1, beatBytes), | ||||||
|       interleavedId = Some(0))), |       interleavedId = Some(0))), | ||||||
|     beatBytes  = beatBytes, |     beatBytes  = beatBytes, | ||||||
|     minLatency = min(concurrency, 1))) // the Queue adds at most one cycle |     minLatency = min(concurrency, 1)))) // the Queue adds at most one cycle | ||||||
| { | { | ||||||
|   require (address.contiguous) |   require (address.contiguous) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -8,7 +8,7 @@ import diplomacy._ | |||||||
|  |  | ||||||
| class AXI4RAM(address: AddressSet, executable: Boolean = true, beatBytes: Int = 4)(implicit p: Parameters) extends LazyModule | class AXI4RAM(address: AddressSet, executable: Boolean = true, beatBytes: Int = 4)(implicit p: Parameters) extends LazyModule | ||||||
| { | { | ||||||
|   val node = AXI4SlaveNode(AXI4SlavePortParameters( |   val node = AXI4SlaveNode(Seq(AXI4SlavePortParameters( | ||||||
|     Seq(AXI4SlaveParameters( |     Seq(AXI4SlaveParameters( | ||||||
|       address       = List(address), |       address       = List(address), | ||||||
|       regionType    = RegionType.UNCACHED, |       regionType    = RegionType.UNCACHED, | ||||||
| @@ -17,7 +17,7 @@ class AXI4RAM(address: AddressSet, executable: Boolean = true, beatBytes: Int = | |||||||
|       supportsWrite = TransferSizes(1, beatBytes), |       supportsWrite = TransferSizes(1, beatBytes), | ||||||
|       interleavedId = Some(0))), |       interleavedId = Some(0))), | ||||||
|     beatBytes  = beatBytes, |     beatBytes  = beatBytes, | ||||||
|     minLatency = 0)) // B responds on same cycle |     minLatency = 0))) // B responds on same cycle | ||||||
|  |  | ||||||
|   // We require the address range to include an entire beat (for the write mask) |   // We require the address range to include an entire beat (for the write mask) | ||||||
|   require ((address.mask & (beatBytes-1)) == beatBytes-1) |   require ((address.mask & (beatBytes-1)) == beatBytes-1) | ||||||
|   | |||||||
| @@ -8,15 +8,15 @@ import config._ | |||||||
| import diplomacy._ | import diplomacy._ | ||||||
| import uncore.tilelink2._ | import uncore.tilelink2._ | ||||||
|  |  | ||||||
| case class AXI4ToTLNode() extends MixedNode(AXI4Imp, TLImp)( | case class AXI4ToTLNode() extends MixedAdapterNode(AXI4Imp, TLImp)( | ||||||
|   dFn = { case (1, Seq(AXI4MasterPortParameters(masters))) => |   dFn = { case AXI4MasterPortParameters(masters) => | ||||||
|     Seq(TLClientPortParameters(clients = masters.map { m => |     TLClientPortParameters(clients = masters.map { m => | ||||||
|       TLClientParameters( |       TLClientParameters( | ||||||
|         sourceId = IdRange(m.id.start << 1, m.id.end << 1), // R+W ids are distinct |         sourceId = IdRange(m.id.start << 1, m.id.end << 1), // R+W ids are distinct | ||||||
|         nodePath = m.nodePath) |         nodePath = m.nodePath) | ||||||
|     })) |     }) | ||||||
|   }, |   }, | ||||||
|   uFn = { case (1, Seq(mp)) => Seq(AXI4SlavePortParameters( |   uFn = { mp => AXI4SlavePortParameters( | ||||||
|     slaves = mp.managers.map { m => |     slaves = mp.managers.map { m => | ||||||
|       AXI4SlaveParameters( |       AXI4SlaveParameters( | ||||||
|         address       = m.address, |         address       = m.address, | ||||||
| @@ -27,10 +27,8 @@ case class AXI4ToTLNode() extends MixedNode(AXI4Imp, TLImp)( | |||||||
|         supportsRead  = m.supportsGet, |         supportsRead  = m.supportsGet, | ||||||
|         interleavedId = Some(0))}, // TL2 never interleaves D beats |         interleavedId = Some(0))}, // TL2 never interleaves D beats | ||||||
|     beatBytes = mp.beatBytes, |     beatBytes = mp.beatBytes, | ||||||
|     minLatency = mp.minLatency)) |     minLatency = mp.minLatency) | ||||||
|   }, |   }) | ||||||
|   numPO = 1 to 1, |  | ||||||
|   numPI = 1 to 1) |  | ||||||
|  |  | ||||||
| class AXI4ToTL()(implicit p: Parameters) extends LazyModule | class AXI4ToTL()(implicit p: Parameters) extends LazyModule | ||||||
| { | { | ||||||
| @@ -42,131 +40,129 @@ class AXI4ToTL()(implicit p: Parameters) extends LazyModule | |||||||
|       val out = node.bundleOut |       val out = node.bundleOut | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     val in = io.in(0) |     ((io.in zip io.out) zip (node.edgesIn zip node.edgesOut)) foreach { case ((in, out), (edgeIn, edgeOut)) => | ||||||
|     val out = io.out(0) |       val numIds = edgeIn.master.endId | ||||||
|     val edgeIn = node.edgesIn(0) |       val beatBytes = edgeOut.manager.beatBytes | ||||||
|     val edgeOut = node.edgesOut(0) |       val countBits = AXI4Parameters.lenBits + (1 << AXI4Parameters.sizeBits) - 1 | ||||||
|     val numIds = edgeIn.master.endId |  | ||||||
|     val beatBytes = edgeOut.manager.beatBytes |  | ||||||
|     val countBits = AXI4Parameters.lenBits + (1 << AXI4Parameters.sizeBits) - 1 |  | ||||||
|  |  | ||||||
|     require (edgeIn.master.masters(0).aligned) |       require (edgeIn.master.masters(0).aligned) | ||||||
|  |  | ||||||
|     val r_out = Wire(out.a) |       val r_out = Wire(out.a) | ||||||
|     val r_inflight = RegInit(UInt(0, width = numIds)) |       val r_inflight = RegInit(UInt(0, width = numIds)) | ||||||
|     val r_block = r_inflight(in.ar.bits.id) |       val r_block = r_inflight(in.ar.bits.id) | ||||||
|     val r_size1 = in.ar.bits.bytes1() |       val r_size1 = in.ar.bits.bytes1() | ||||||
|     val r_size = OH1ToUInt(r_size1) |       val r_size = OH1ToUInt(r_size1) | ||||||
|     val r_addr = in.ar.bits.addr |       val r_addr = in.ar.bits.addr | ||||||
|     val r_ok = edgeOut.manager.supportsGetSafe(r_addr, r_size) |       val r_ok = edgeOut.manager.supportsGetSafe(r_addr, r_size) | ||||||
|     val r_err_in = Wire(Decoupled(new AXI4BundleRError(in.ar.bits.params))) |       val r_err_in = Wire(Decoupled(new AXI4BundleRError(in.ar.bits.params))) | ||||||
|     val r_err_out = Queue(r_err_in, 2) |       val r_err_out = Queue(r_err_in, 2) | ||||||
|     val r_count = RegInit(UInt(0, width = in.ar.bits.params.lenBits)) |       val r_count = RegInit(UInt(0, width = in.ar.bits.params.lenBits)) | ||||||
|     val r_last = r_count === in.ar.bits.len |       val r_last = r_count === in.ar.bits.len | ||||||
|  |  | ||||||
|     assert (!in.ar.valid || r_size1 === UIntToOH1(r_size, countBits)) // because aligned |       assert (!in.ar.valid || r_size1 === UIntToOH1(r_size, countBits)) // because aligned | ||||||
|     in.ar.ready := Mux(r_ok, r_out.ready, r_err_in.ready && r_last) && !r_block |       in.ar.ready := Mux(r_ok, r_out.ready, r_err_in.ready && r_last) && !r_block | ||||||
|     r_out.valid := in.ar.valid && !r_block && r_ok |       r_out.valid := in.ar.valid && !r_block && r_ok | ||||||
|     r_out.bits := edgeOut.Get(in.ar.bits.id << 1 | UInt(1), r_addr, r_size)._2 |       r_out.bits := edgeOut.Get(in.ar.bits.id << 1 | UInt(1), r_addr, r_size)._2 | ||||||
|     r_err_in.valid := in.ar.valid && !r_block && !r_ok |       r_err_in.valid := in.ar.valid && !r_block && !r_ok | ||||||
|     r_err_in.bits.last := r_last |       r_err_in.bits.last := r_last | ||||||
|     r_err_in.bits.id := in.ar.bits.id |       r_err_in.bits.id := in.ar.bits.id | ||||||
|  |  | ||||||
|     when (r_err_in.fire()) { r_count := Mux(r_last, UInt(0), r_count + UInt(1)) } |       when (r_err_in.fire()) { r_count := Mux(r_last, UInt(0), r_count + UInt(1)) } | ||||||
|  |  | ||||||
|     val w_out = Wire(out.a) |       val w_out = Wire(out.a) | ||||||
|     val w_inflight = RegInit(UInt(0, width = numIds)) |       val w_inflight = RegInit(UInt(0, width = numIds)) | ||||||
|     val w_block = w_inflight(in.aw.bits.id) |       val w_block = w_inflight(in.aw.bits.id) | ||||||
|     val w_size1 = in.aw.bits.bytes1() |       val w_size1 = in.aw.bits.bytes1() | ||||||
|     val w_size = OH1ToUInt(w_size1) |       val w_size = OH1ToUInt(w_size1) | ||||||
|     val w_addr = in.aw.bits.addr |       val w_addr = in.aw.bits.addr | ||||||
|     val w_ok = edgeOut.manager.supportsPutPartialSafe(w_addr, w_size) |       val w_ok = edgeOut.manager.supportsPutPartialSafe(w_addr, w_size) | ||||||
|     val w_err_in = Wire(Decoupled(in.aw.bits.id)) |       val w_err_in = Wire(Decoupled(in.aw.bits.id)) | ||||||
|     val w_err_out = Queue(w_err_in, 2) |       val w_err_out = Queue(w_err_in, 2) | ||||||
|  |  | ||||||
|     assert (!in.aw.valid || w_size1 === UIntToOH1(w_size, countBits)) // because aligned |       assert (!in.aw.valid || w_size1 === UIntToOH1(w_size, countBits)) // because aligned | ||||||
|     assert (!in.aw.valid || in.aw.bits.len === UInt(0) || in.aw.bits.size === UInt(log2Ceil(beatBytes))) // because aligned |       assert (!in.aw.valid || in.aw.bits.len === UInt(0) || in.aw.bits.size === UInt(log2Ceil(beatBytes))) // because aligned | ||||||
|     in.aw.ready := Mux(w_ok, w_out.ready, w_err_in.ready) && in.w.valid && in.w.bits.last && !w_block |       in.aw.ready := Mux(w_ok, w_out.ready, w_err_in.ready) && in.w.valid && in.w.bits.last && !w_block | ||||||
|     in.w.ready  := Mux(w_ok, w_out.ready, w_err_in.ready || !in.w.bits.last) && in.aw.valid && !w_block |       in.w.ready  := Mux(w_ok, w_out.ready, w_err_in.ready || !in.w.bits.last) && in.aw.valid && !w_block | ||||||
|     w_out.valid := in.aw.valid && in.w.valid && !w_block && w_ok |       w_out.valid := in.aw.valid && in.w.valid && !w_block && w_ok | ||||||
|     w_out.bits := edgeOut.Put(in.aw.bits.id << 1, w_addr, w_size, in.w.bits.data, in.w.bits.strb)._2 |       w_out.bits := edgeOut.Put(in.aw.bits.id << 1, w_addr, w_size, in.w.bits.data, in.w.bits.strb)._2 | ||||||
|     w_err_in.valid := in.aw.valid && in.w.valid && !w_block && !w_ok && in.w.bits.last |       w_err_in.valid := in.aw.valid && in.w.valid && !w_block && !w_ok && in.w.bits.last | ||||||
|     w_err_in.bits := in.aw.bits.id |       w_err_in.bits := in.aw.bits.id | ||||||
|  |  | ||||||
|     TLArbiter(TLArbiter.lowestIndexFirst)(out.a, (UInt(0), r_out), (in.aw.bits.len, w_out)) |       TLArbiter(TLArbiter.lowestIndexFirst)(out.a, (UInt(0), r_out), (in.aw.bits.len, w_out)) | ||||||
|  |  | ||||||
|     val ok_b  = Wire(in.b) |       val ok_b  = Wire(in.b) | ||||||
|     val err_b = Wire(in.b) |       val err_b = Wire(in.b) | ||||||
|     val mux_b = Wire(in.b) |       val mux_b = Wire(in.b) | ||||||
|     val ok_r  = Wire(in.r) |       val ok_r  = Wire(in.r) | ||||||
|     val err_r = Wire(in.r) |       val err_r = Wire(in.r) | ||||||
|     val mux_r = Wire(in.r) |       val mux_r = Wire(in.r) | ||||||
|  |  | ||||||
|     val d_resp = Mux(out.d.bits.error, AXI4Parameters.RESP_SLVERR, AXI4Parameters.RESP_OKAY) |       val d_resp = Mux(out.d.bits.error, AXI4Parameters.RESP_SLVERR, AXI4Parameters.RESP_OKAY) | ||||||
|     val d_hasData = edgeOut.hasData(out.d.bits) |       val d_hasData = edgeOut.hasData(out.d.bits) | ||||||
|     val d_last = edgeOut.last(out.d) |       val d_last = edgeOut.last(out.d) | ||||||
|  |  | ||||||
|     out.d.ready := Mux(d_hasData, ok_r.ready, ok_b.ready) |       out.d.ready := Mux(d_hasData, ok_r.ready, ok_b.ready) | ||||||
|     ok_r.valid := out.d.valid && d_hasData |       ok_r.valid := out.d.valid && d_hasData | ||||||
|     ok_b.valid := out.d.valid && !d_hasData |       ok_b.valid := out.d.valid && !d_hasData | ||||||
|  |  | ||||||
|     ok_r.bits.id   := out.d.bits.source >> 1 |       ok_r.bits.id   := out.d.bits.source >> 1 | ||||||
|     ok_r.bits.data := out.d.bits.data |       ok_r.bits.data := out.d.bits.data | ||||||
|     ok_r.bits.resp := d_resp |       ok_r.bits.resp := d_resp | ||||||
|     ok_r.bits.last := d_last |       ok_r.bits.last := d_last | ||||||
|  |  | ||||||
|     r_err_out.ready := err_r.ready |       r_err_out.ready := err_r.ready | ||||||
|     err_r.valid := r_err_out.valid |       err_r.valid := r_err_out.valid | ||||||
|     err_r.bits.id   := r_err_out.bits.id |       err_r.bits.id   := r_err_out.bits.id | ||||||
|     err_r.bits.data := out.d.bits.data // don't care |       err_r.bits.data := out.d.bits.data // don't care | ||||||
|     err_r.bits.resp := AXI4Parameters.RESP_DECERR |       err_r.bits.resp := AXI4Parameters.RESP_DECERR | ||||||
|     err_r.bits.last := r_err_out.bits.last |       err_r.bits.last := r_err_out.bits.last | ||||||
|  |  | ||||||
|     // AXI4 must hold R to one source until last |       // AXI4 must hold R to one source until last | ||||||
|     val mux_lock_ok  = RegInit(Bool(false)) |       val mux_lock_ok  = RegInit(Bool(false)) | ||||||
|     val mux_lock_err = RegInit(Bool(false)) |       val mux_lock_err = RegInit(Bool(false)) | ||||||
|     when (ok_r .fire()) { mux_lock_ok  := !ok_r .bits.last } |       when (ok_r .fire()) { mux_lock_ok  := !ok_r .bits.last } | ||||||
|     when (err_r.fire()) { mux_lock_err := !err_r.bits.last } |       when (err_r.fire()) { mux_lock_err := !err_r.bits.last } | ||||||
|     assert (!mux_lock_ok || !mux_lock_err) |       assert (!mux_lock_ok || !mux_lock_err) | ||||||
|  |  | ||||||
|     // Prioritize err over ok (b/c err_r.valid comes from a register) |       // Prioritize err over ok (b/c err_r.valid comes from a register) | ||||||
|     mux_r.valid := (!mux_lock_err && ok_r.valid) || (!mux_lock_ok && err_r.valid) |       mux_r.valid := (!mux_lock_err && ok_r.valid) || (!mux_lock_ok && err_r.valid) | ||||||
|     mux_r.bits  := Mux(!mux_lock_ok && err_r.valid, err_r.bits, ok_r.bits) |       mux_r.bits  := Mux(!mux_lock_ok && err_r.valid, err_r.bits, ok_r.bits) | ||||||
|     ok_r.ready  := mux_r.ready && (mux_lock_ok || !err_r.valid) |       ok_r.ready  := mux_r.ready && (mux_lock_ok || !err_r.valid) | ||||||
|     err_r.ready := mux_r.ready && !mux_lock_ok |       err_r.ready := mux_r.ready && !mux_lock_ok | ||||||
|  |  | ||||||
|     // AXI4 needs irrevocable behaviour |       // AXI4 needs irrevocable behaviour | ||||||
|     in.r <> Queue.irrevocable(mux_r, 1, flow=true) |       in.r <> Queue.irrevocable(mux_r, 1, flow=true) | ||||||
|  |  | ||||||
|     ok_b.bits.id   := out.d.bits.source >> 1 |       ok_b.bits.id   := out.d.bits.source >> 1 | ||||||
|     ok_b.bits.resp := d_resp |       ok_b.bits.resp := d_resp | ||||||
|  |  | ||||||
|     w_err_out.ready := err_b.ready |       w_err_out.ready := err_b.ready | ||||||
|     err_b.valid := w_err_out.valid |       err_b.valid := w_err_out.valid | ||||||
|     err_b.bits.id   := w_err_out.bits |       err_b.bits.id   := w_err_out.bits | ||||||
|     err_b.bits.resp := AXI4Parameters.RESP_DECERR |       err_b.bits.resp := AXI4Parameters.RESP_DECERR | ||||||
|  |  | ||||||
|     // Prioritize err over ok (b/c err_b.valid comes from a register) |       // Prioritize err over ok (b/c err_b.valid comes from a register) | ||||||
|     mux_b.valid := ok_b.valid || err_b.valid |       mux_b.valid := ok_b.valid || err_b.valid | ||||||
|     mux_b.bits  := Mux(err_b.valid, err_b.bits, ok_b.bits) |       mux_b.bits  := Mux(err_b.valid, err_b.bits, ok_b.bits) | ||||||
|     ok_b.ready  := mux_b.ready && !err_b.valid |       ok_b.ready  := mux_b.ready && !err_b.valid | ||||||
|     err_b.ready := mux_b.ready |       err_b.ready := mux_b.ready | ||||||
|  |  | ||||||
|     // AXI4 needs irrevocable behaviour |       // AXI4 needs irrevocable behaviour | ||||||
|     in.b <> Queue.irrevocable(mux_b, 1, flow=true) |       in.b <> Queue.irrevocable(mux_b, 1, flow=true) | ||||||
|  |  | ||||||
|     // Update flight trackers |       // Update flight trackers | ||||||
|     val r_set = in.ar.fire().asUInt << in.ar.bits.id |       val r_set = in.ar.fire().asUInt << in.ar.bits.id | ||||||
|     val r_clr = (in.r.fire() && in.r.bits.last).asUInt << in.r.bits.id |       val r_clr = (in.r.fire() && in.r.bits.last).asUInt << in.r.bits.id | ||||||
|     r_inflight := (r_inflight | r_set) & ~r_clr |       r_inflight := (r_inflight | r_set) & ~r_clr | ||||||
|     val w_set = in.aw.fire().asUInt << in.aw.bits.id |       val w_set = in.aw.fire().asUInt << in.aw.bits.id | ||||||
|     val w_clr = in.b.fire().asUInt << in.b.bits.id |       val w_clr = in.b.fire().asUInt << in.b.bits.id | ||||||
|     w_inflight := (w_inflight | w_set) & ~w_clr |       w_inflight := (w_inflight | w_set) & ~w_clr | ||||||
|  |  | ||||||
|     // Unused channels |       // Unused channels | ||||||
|     out.b.ready := Bool(true) |       out.b.ready := Bool(true) | ||||||
|     out.c.valid := Bool(false) |       out.c.valid := Bool(false) | ||||||
|     out.e.valid := Bool(false) |       out.e.valid := Bool(false) | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -62,7 +62,7 @@ class TLPLIC(supervisor: Boolean, maxPriorities: Int, address: BigInt = 0xC00000 | |||||||
|     beatBytes = p(rocket.XLen)/8, |     beatBytes = p(rocket.XLen)/8, | ||||||
|     undefZero = false) |     undefZero = false) | ||||||
|  |  | ||||||
|   val intnode = IntAdapterNode( |   val intnode = IntNexusNode( | ||||||
|     numSourcePorts = 0 to 1024, |     numSourcePorts = 0 to 1024, | ||||||
|     numSinkPorts   = 0 to 1024, |     numSinkPorts   = 0 to 1024, | ||||||
|     sourceFn       = { _ => IntSourcePortParameters(Seq(IntSourceParameters(contextsPerHart))) }, |     sourceFn       = { _ => IntSourcePortParameters(Seq(IntSourceParameters(contextsPerHart))) }, | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ import Chisel._ | |||||||
| import chisel3.internal.sourceinfo.SourceInfo | import chisel3.internal.sourceinfo.SourceInfo | ||||||
| import config._ | import config._ | ||||||
| import diplomacy._ | import diplomacy._ | ||||||
|  | import util.GenericParameterizedBundle | ||||||
| import scala.math.{min,max} | import scala.math.{min,max} | ||||||
|  |  | ||||||
| // Ensures that all downstream RW managers support Atomic operationss. | // Ensures that all downstream RW managers support Atomic operationss. | ||||||
| @@ -15,8 +16,8 @@ class TLAtomicAutomata(logical: Boolean = true, arithmetic: Boolean = true, conc | |||||||
|   require (concurrency >= 1) |   require (concurrency >= 1) | ||||||
|  |  | ||||||
|   val node = TLAdapterNode( |   val node = TLAdapterNode( | ||||||
|     clientFn  = { case Seq(cp) => require (!cp.unsafeAtomics); cp.copy(unsafeAtomics = true) }, |     clientFn  = { case cp => require (!cp.unsafeAtomics); cp.copy(unsafeAtomics = true) }, | ||||||
|     managerFn = { case Seq(mp) => mp.copy(managers = mp.managers.map { m => |     managerFn = { case mp => mp.copy(managers = mp.managers.map { m => | ||||||
|       val ourSupport = TransferSizes(1, mp.beatBytes) |       val ourSupport = TransferSizes(1, mp.beatBytes) | ||||||
|       def widen(x: TransferSizes) = if (passthrough && x.min <= 2*mp.beatBytes) TransferSizes(1, max(mp.beatBytes, x.max)) else ourSupport |       def widen(x: TransferSizes) = if (passthrough && x.min <= 2*mp.beatBytes) TransferSizes(1, max(mp.beatBytes, x.max)) else ourSupport | ||||||
|       val canDoit = m.supportsPutFull.contains(ourSupport) && m.supportsGet.contains(ourSupport) |       val canDoit = m.supportsPutFull.contains(ourSupport) && m.supportsGet.contains(ourSupport) | ||||||
| @@ -33,245 +34,232 @@ class TLAtomicAutomata(logical: Boolean = true, arithmetic: Boolean = true, conc | |||||||
|       val out = node.bundleOut |       val out = node.bundleOut | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     val in  = io.in(0) |     ((io.in zip io.out) zip (node.edgesIn zip node.edgesOut)) foreach { case ((in, out), (edgeIn, edgeOut)) => | ||||||
|     val out = io.out(0) |       val managers = edgeOut.manager.managers | ||||||
|     val edgeIn  = node.edgesIn(0) |       val beatBytes = edgeOut.manager.beatBytes | ||||||
|     val edgeOut = node.edgesOut(0) |  | ||||||
|     val managers = edgeOut.manager.managers |  | ||||||
|     val beatBytes = edgeOut.manager.beatBytes |  | ||||||
|  |  | ||||||
|     // To which managers are we adding atomic support? |       // To which managers are we adding atomic support? | ||||||
|     val ourSupport = TransferSizes(1, edgeOut.manager.beatBytes) |       val ourSupport = TransferSizes(1, edgeOut.manager.beatBytes) | ||||||
|     val managersNeedingHelp = managers.filter { m => |       val managersNeedingHelp = managers.filter { m => | ||||||
|       m.supportsPutFull.contains(ourSupport) && |         m.supportsPutFull.contains(ourSupport) && | ||||||
|       m.supportsGet.contains(ourSupport) && |         m.supportsGet.contains(ourSupport) && | ||||||
|       ((logical    && !m.supportsLogical   .contains(ourSupport)) || |         ((logical    && !m.supportsLogical   .contains(ourSupport)) || | ||||||
|        (arithmetic && !m.supportsArithmetic.contains(ourSupport)) || |          (arithmetic && !m.supportsArithmetic.contains(ourSupport)) || | ||||||
|        !passthrough) // we will do atomics for everyone we can |          !passthrough) // we will do atomics for everyone we can | ||||||
|     } |  | ||||||
|     // We cannot add atomcis to a non-FIFO manager |  | ||||||
|     managersNeedingHelp foreach { m => require (m.fifoId.isDefined) } |  | ||||||
|     // We need to preserve FIFO semantics across FIFO domains, not managers |  | ||||||
|     // Suppose you have Put(42) Atomic(+1) both inflight; valid results: 42 or 43 |  | ||||||
|     // If we allow Put(42) Get() Put(+1) concurrent; valid results: 42 43 OR undef |  | ||||||
|     // Making non-FIFO work requires waiting for all Acks to come back (=> use FIFOFixer) |  | ||||||
|     val domainsNeedingHelp = managersNeedingHelp.map(_.fifoId.get).distinct |  | ||||||
|     // Don't overprovision the CAM |  | ||||||
|     val camSize = min(domainsNeedingHelp.size, concurrency) |  | ||||||
|     // Compact the fifoIds to only those we care about |  | ||||||
|     val camFifoIds = managers.map(m => UInt(m.fifoId.map(id => max(0, domainsNeedingHelp.indexOf(id))).getOrElse(0))) |  | ||||||
|  |  | ||||||
|     // CAM entry state machine |  | ||||||
|     val FREE = UInt(0) // unused                   waiting on Atomic from A |  | ||||||
|     val GET  = UInt(3) // Get sent down A          waiting on AccessDataAck from D |  | ||||||
|     val AMO  = UInt(2) // AccessDataAck sent up D  waiting for A availability |  | ||||||
|     val ACK  = UInt(1) // Put sent down A          waiting for PutAck from D |  | ||||||
|  |  | ||||||
|     def helper(select: Seq[Bool], x: Seq[TransferSizes], lgSize: UInt) = |  | ||||||
|       if (!passthrough) Bool(false) else |  | ||||||
|       if (x.map(_ == x(0)).reduce(_ && _)) x(0).containsLg(lgSize) else |  | ||||||
|       Mux1H(select, x.map(_.containsLg(lgSize)))  |  | ||||||
|  |  | ||||||
|     // Do we need to do anything at all? |  | ||||||
|     if (camSize > 0) { |  | ||||||
|       class CAM_S extends Bundle { |  | ||||||
|         val state = UInt(width = 2) |  | ||||||
|       } |  | ||||||
|       class CAM_A extends Bundle { |  | ||||||
|         val bits    = new TLBundleA(out.a.bits.params) |  | ||||||
|         val fifoId  = UInt(width = log2Up(domainsNeedingHelp.size)) |  | ||||||
|         val lut     = UInt(width = 4) |  | ||||||
|       } |  | ||||||
|       class CAM_D extends Bundle { |  | ||||||
|         val data = UInt(width = out.a.bits.params.dataBits) |  | ||||||
|       } |       } | ||||||
|  |       // We cannot add atomcis to a non-FIFO manager | ||||||
|  |       managersNeedingHelp foreach { m => require (m.fifoId.isDefined) } | ||||||
|  |       // We need to preserve FIFO semantics across FIFO domains, not managers | ||||||
|  |       // Suppose you have Put(42) Atomic(+1) both inflight; valid results: 42 or 43 | ||||||
|  |       // If we allow Put(42) Get() Put(+1) concurrent; valid results: 42 43 OR undef | ||||||
|  |       // Making non-FIFO work requires waiting for all Acks to come back (=> use FIFOFixer) | ||||||
|  |       val domainsNeedingHelp = managersNeedingHelp.map(_.fifoId.get).distinct | ||||||
|  |       // Don't overprovision the CAM | ||||||
|  |       val camSize = min(domainsNeedingHelp.size, concurrency) | ||||||
|  |       // Compact the fifoIds to only those we care about | ||||||
|  |       val camFifoIds = managers.map(m => UInt(m.fifoId.map(id => max(0, domainsNeedingHelp.indexOf(id))).getOrElse(0))) | ||||||
|  |  | ||||||
|       val initval = Wire(new CAM_S) |       // CAM entry state machine | ||||||
|       initval.state := FREE |       val FREE = UInt(0) // unused                   waiting on Atomic from A | ||||||
|       val cam_s = RegInit(Vec.fill(camSize)(initval)) |       val GET  = UInt(3) // Get sent down A          waiting on AccessDataAck from D | ||||||
|       val cam_a = Reg(Vec(camSize, new CAM_A)) |       val AMO  = UInt(2) // AccessDataAck sent up D  waiting for A availability | ||||||
|       val cam_d = Reg(Vec(camSize, new CAM_D)) |       val ACK  = UInt(1) // Put sent down A          waiting for PutAck from D | ||||||
|  |  | ||||||
|       val cam_free   = cam_s.map(_.state === FREE) |       def helper(select: Seq[Bool], x: Seq[TransferSizes], lgSize: UInt) = | ||||||
|       val cam_amo    = cam_s.map(_.state === AMO) |         if (!passthrough) Bool(false) else | ||||||
|       val cam_abusy  = cam_s.map(e => e.state === GET || e.state === AMO) // A is blocked |         if (x.map(_ == x(0)).reduce(_ && _)) x(0).containsLg(lgSize) else | ||||||
|       val cam_dmatch = cam_s.map(e => e.state =/= FREE) // D should inspect these entries |         Mux1H(select, x.map(_.containsLg(lgSize)))  | ||||||
|  |  | ||||||
|       // Can the manager already handle this message? |       val params = TLAtomicAutomata.CAMParams(out.a.bits.params, domainsNeedingHelp.size) | ||||||
|       val a_size = edgeIn.size(in.a.bits) |       // Do we need to do anything at all? | ||||||
|       val a_select = edgeOut.manager.findFast(edgeIn.address(in.a.bits)) |       if (camSize > 0) { | ||||||
|       val a_canLogical    = helper(a_select, managers.map(_.supportsLogical),    a_size) |         val initval = Wire(new TLAtomicAutomata.CAM_S(params)) | ||||||
|       val a_canArithmetic = helper(a_select, managers.map(_.supportsArithmetic), a_size) |         initval.state := FREE | ||||||
|       val a_isLogical    = in.a.bits.opcode === TLMessages.LogicalData |         val cam_s = RegInit(Vec.fill(camSize)(initval)) | ||||||
|       val a_isArithmetic = in.a.bits.opcode === TLMessages.ArithmeticData |         val cam_a = Reg(Vec(camSize, new TLAtomicAutomata.CAM_A(params))) | ||||||
|       val a_isSupported = Mux(a_isLogical, a_canLogical, Mux(a_isArithmetic, a_canArithmetic, Bool(true))) |         val cam_d = Reg(Vec(camSize, new TLAtomicAutomata.CAM_D(params))) | ||||||
|  |  | ||||||
|       // Must we do a Put? |         val cam_free   = cam_s.map(_.state === FREE) | ||||||
|       val a_cam_any_put = cam_amo.reduce(_ || _) |         val cam_amo    = cam_s.map(_.state === AMO) | ||||||
|       val a_cam_por_put = cam_amo.scanLeft(Bool(false))(_||_).init |         val cam_abusy  = cam_s.map(e => e.state === GET || e.state === AMO) // A is blocked | ||||||
|       val a_cam_sel_put = (cam_amo zip a_cam_por_put) map { case (a, b) => a && !b } |         val cam_dmatch = cam_s.map(e => e.state =/= FREE) // D should inspect these entries | ||||||
|       val a_cam_a = PriorityMux(cam_amo, cam_a) |  | ||||||
|       val a_cam_d = PriorityMux(cam_amo, cam_d) |  | ||||||
|       val a_a = a_cam_a.bits.data |  | ||||||
|       val a_d = a_cam_d.data |  | ||||||
|  |  | ||||||
|       // Does the A request conflict with an inflight AMO? |         // Can the manager already handle this message? | ||||||
|       val a_fifoId  = Mux1H(a_select, camFifoIds) |         val a_size = edgeIn.size(in.a.bits) | ||||||
|       val a_cam_busy = (cam_abusy zip cam_a.map(_.fifoId === a_fifoId)) map { case (a,b) => a&&b } reduce (_||_) |         val a_select = edgeOut.manager.findFast(edgeIn.address(in.a.bits)) | ||||||
|  |         val a_canLogical    = helper(a_select, managers.map(_.supportsLogical),    a_size) | ||||||
|  |         val a_canArithmetic = helper(a_select, managers.map(_.supportsArithmetic), a_size) | ||||||
|  |         val a_isLogical    = in.a.bits.opcode === TLMessages.LogicalData | ||||||
|  |         val a_isArithmetic = in.a.bits.opcode === TLMessages.ArithmeticData | ||||||
|  |         val a_isSupported = Mux(a_isLogical, a_canLogical, Mux(a_isArithmetic, a_canArithmetic, Bool(true))) | ||||||
|  |  | ||||||
|       // (Where) are we are allocating in the CAM? |         // Must we do a Put? | ||||||
|       val a_cam_any_free = cam_free.reduce(_ || _) |         val a_cam_any_put = cam_amo.reduce(_ || _) | ||||||
|       val a_cam_por_free = cam_free.scanLeft(Bool(false))(_||_).init |         val a_cam_por_put = cam_amo.scanLeft(Bool(false))(_||_).init | ||||||
|       val a_cam_sel_free = (cam_free zip a_cam_por_free) map { case (a,b) => a && !b } |         val a_cam_sel_put = (cam_amo zip a_cam_por_put) map { case (a, b) => a && !b } | ||||||
|  |         val a_cam_a = PriorityMux(cam_amo, cam_a) | ||||||
|  |         val a_cam_d = PriorityMux(cam_amo, cam_d) | ||||||
|  |         val a_a = a_cam_a.bits.data | ||||||
|  |         val a_d = a_cam_d.data | ||||||
|  |  | ||||||
|       // Logical AMO |         // Does the A request conflict with an inflight AMO? | ||||||
|       val indexes = Seq.tabulate(beatBytes*8) { i => Cat(a_a(i,i), a_d(i,i)) } |         val a_fifoId  = Mux1H(a_select, camFifoIds) | ||||||
|       val logic_out = Cat(indexes.map(x => a_cam_a.lut(x).asUInt).reverse) |         val a_cam_busy = (cam_abusy zip cam_a.map(_.fifoId === a_fifoId)) map { case (a,b) => a&&b } reduce (_||_) | ||||||
|  |  | ||||||
|       // Arithmetic AMO |         // (Where) are we are allocating in the CAM? | ||||||
|       val unsigned = a_cam_a.bits.param(1) |         val a_cam_any_free = cam_free.reduce(_ || _) | ||||||
|       val take_max = a_cam_a.bits.param(0) |         val a_cam_por_free = cam_free.scanLeft(Bool(false))(_||_).init | ||||||
|       val adder = a_cam_a.bits.param(2) |         val a_cam_sel_free = (cam_free zip a_cam_por_free) map { case (a,b) => a && !b } | ||||||
|       val mask = a_cam_a.bits.mask |  | ||||||
|       val signSel = ~(~mask | (mask >> 1)) |  | ||||||
|       val signbits_a = Cat(Seq.tabulate(beatBytes) { i => a_a(8*i+7,8*i+7) } .reverse) |  | ||||||
|       val signbits_d = Cat(Seq.tabulate(beatBytes) { i => a_d(8*i+7,8*i+7) } .reverse) |  | ||||||
|       // Move the selected sign bit into the first byte position it will extend |  | ||||||
|       val signbit_a = ((signbits_a & signSel) << 1)(beatBytes-1, 0) |  | ||||||
|       val signbit_d = ((signbits_d & signSel) << 1)(beatBytes-1, 0) |  | ||||||
|       val signext_a = FillInterleaved(8, leftOR(signbit_a)) |  | ||||||
|       val signext_d = FillInterleaved(8, leftOR(signbit_d)) |  | ||||||
|       // NOTE: sign-extension does not change the relative ordering in EITHER unsigned or signed arithmetic |  | ||||||
|       val wide_mask = FillInterleaved(8, mask) |  | ||||||
|       val a_a_ext = (a_a & wide_mask) | signext_a |  | ||||||
|       val a_d_ext = (a_d & wide_mask) | signext_d |  | ||||||
|       val a_d_inv = Mux(adder, a_d_ext, ~a_d_ext) |  | ||||||
|       val adder_out = a_a_ext + a_d_inv |  | ||||||
|       val h = 8*beatBytes-1 // now sign-extended; use biggest bit |  | ||||||
|       val a_bigger_uneq = unsigned === a_a_ext(h) // result if high bits are unequal |  | ||||||
|       val a_bigger = Mux(a_a_ext(h) === a_d_ext(h), !adder_out(h), a_bigger_uneq) |  | ||||||
|       val pick_a = take_max === a_bigger |  | ||||||
|       val arith_out = Mux(adder, adder_out, Mux(pick_a, a_a, a_d)) |  | ||||||
|  |  | ||||||
|       // AMO result data |         // Logical AMO | ||||||
|       val amo_data = |         val indexes = Seq.tabulate(beatBytes*8) { i => Cat(a_a(i,i), a_d(i,i)) } | ||||||
|         if (!logical)    arith_out else |         val logic_out = Cat(indexes.map(x => a_cam_a.lut(x).asUInt).reverse) | ||||||
|         if (!arithmetic) logic_out else |  | ||||||
|         Mux(a_cam_a.bits.opcode(0), logic_out, arith_out) |  | ||||||
|  |  | ||||||
|       // Potentially mutate the message from inner |         // Arithmetic AMO | ||||||
|       val source_i = Wire(in.a) |         val unsigned = a_cam_a.bits.param(1) | ||||||
|       val a_allow = !a_cam_busy && (a_isSupported || a_cam_any_free) |         val take_max = a_cam_a.bits.param(0) | ||||||
|       in.a.ready := source_i.ready && a_allow |         val adder = a_cam_a.bits.param(2) | ||||||
|       source_i.valid := in.a.valid && a_allow |         val mask = a_cam_a.bits.mask | ||||||
|       source_i.bits  := in.a.bits |         val signSel = ~(~mask | (mask >> 1)) | ||||||
|       when (!a_isSupported) { // minimal mux difference |         val signbits_a = Cat(Seq.tabulate(beatBytes) { i => a_a(8*i+7,8*i+7) } .reverse) | ||||||
|         source_i.bits.opcode := TLMessages.Get |         val signbits_d = Cat(Seq.tabulate(beatBytes) { i => a_d(8*i+7,8*i+7) } .reverse) | ||||||
|         source_i.bits.param  := UInt(0) |         // Move the selected sign bit into the first byte position it will extend | ||||||
|       } |         val signbit_a = ((signbits_a & signSel) << 1)(beatBytes-1, 0) | ||||||
|  |         val signbit_d = ((signbits_d & signSel) << 1)(beatBytes-1, 0) | ||||||
|  |         val signext_a = FillInterleaved(8, leftOR(signbit_a)) | ||||||
|  |         val signext_d = FillInterleaved(8, leftOR(signbit_d)) | ||||||
|  |         // NOTE: sign-extension does not change the relative ordering in EITHER unsigned or signed arithmetic | ||||||
|  |         val wide_mask = FillInterleaved(8, mask) | ||||||
|  |         val a_a_ext = (a_a & wide_mask) | signext_a | ||||||
|  |         val a_d_ext = (a_d & wide_mask) | signext_d | ||||||
|  |         val a_d_inv = Mux(adder, a_d_ext, ~a_d_ext) | ||||||
|  |         val adder_out = a_a_ext + a_d_inv | ||||||
|  |         val h = 8*beatBytes-1 // now sign-extended; use biggest bit | ||||||
|  |         val a_bigger_uneq = unsigned === a_a_ext(h) // result if high bits are unequal | ||||||
|  |         val a_bigger = Mux(a_a_ext(h) === a_d_ext(h), !adder_out(h), a_bigger_uneq) | ||||||
|  |         val pick_a = take_max === a_bigger | ||||||
|  |         val arith_out = Mux(adder, adder_out, Mux(pick_a, a_a, a_d)) | ||||||
|  |  | ||||||
|       // Potentially take the message from the CAM |         // AMO result data | ||||||
|       val source_c = Wire(in.a) |         val amo_data = | ||||||
|       source_c.valid := a_cam_any_put |           if (!logical)    arith_out else | ||||||
|       source_c.bits := edgeOut.Put(a_cam_a.bits.source, edgeIn.address(a_cam_a.bits), a_cam_a.bits.size, amo_data)._2 |           if (!arithmetic) logic_out else | ||||||
|  |           Mux(a_cam_a.bits.opcode(0), logic_out, arith_out) | ||||||
|  |  | ||||||
|       // Finishing an AMO from the CAM has highest priority |         // Potentially mutate the message from inner | ||||||
|       TLArbiter(TLArbiter.lowestIndexFirst)(out.a, (UInt(0), source_c), (edgeOut.numBeats1(in.a.bits), source_i)) |         val source_i = Wire(in.a) | ||||||
|  |         val a_allow = !a_cam_busy && (a_isSupported || a_cam_any_free) | ||||||
|  |         in.a.ready := source_i.ready && a_allow | ||||||
|  |         source_i.valid := in.a.valid && a_allow | ||||||
|  |         source_i.bits  := in.a.bits | ||||||
|  |         when (!a_isSupported) { // minimal mux difference | ||||||
|  |           source_i.bits.opcode := TLMessages.Get | ||||||
|  |           source_i.bits.param  := UInt(0) | ||||||
|  |         } | ||||||
|  |  | ||||||
|       // Capture the A state into the CAM |         // Potentially take the message from the CAM | ||||||
|       when (source_i.fire() && !a_isSupported) { |         val source_c = Wire(in.a) | ||||||
|         (a_cam_sel_free zip cam_a) foreach { case (en, r) => |         source_c.valid := a_cam_any_put | ||||||
|           when (en) { |         source_c.bits := edgeOut.Put(a_cam_a.bits.source, edgeIn.address(a_cam_a.bits), a_cam_a.bits.size, amo_data)._2 | ||||||
|             r.fifoId := a_fifoId |  | ||||||
|             r.bits   := in.a.bits |         // Finishing an AMO from the CAM has highest priority | ||||||
|             r.lut    := MuxLookup(in.a.bits.param(1, 0), UInt(0, width = 4), Array( |         TLArbiter(TLArbiter.lowestIndexFirst)(out.a, (UInt(0), source_c), (edgeOut.numBeats1(in.a.bits), source_i)) | ||||||
|               TLAtomics.AND  -> UInt(0x8), |  | ||||||
|               TLAtomics.OR   -> UInt(0xe), |         // Capture the A state into the CAM | ||||||
|               TLAtomics.XOR  -> UInt(0x6), |         when (source_i.fire() && !a_isSupported) { | ||||||
|               TLAtomics.SWAP -> UInt(0xc))) |           (a_cam_sel_free zip cam_a) foreach { case (en, r) => | ||||||
|  |             when (en) { | ||||||
|  |               r.fifoId := a_fifoId | ||||||
|  |               r.bits   := in.a.bits | ||||||
|  |               r.lut    := MuxLookup(in.a.bits.param(1, 0), UInt(0, width = 4), Array( | ||||||
|  |                 TLAtomics.AND  -> UInt(0x8), | ||||||
|  |                 TLAtomics.OR   -> UInt(0xe), | ||||||
|  |                 TLAtomics.XOR  -> UInt(0x6), | ||||||
|  |                 TLAtomics.SWAP -> UInt(0xc))) | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |           (a_cam_sel_free zip cam_s) foreach { case (en, r) => | ||||||
|  |             when (en) { | ||||||
|  |               r.state := GET | ||||||
|  |             } | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
|         (a_cam_sel_free zip cam_s) foreach { case (en, r) => |  | ||||||
|           when (en) { |         // Advance the put state | ||||||
|             r.state := GET |         when (source_c.fire()) { | ||||||
|  |           (a_cam_sel_put zip cam_s) foreach { case (en, r) => | ||||||
|  |             when (en) { | ||||||
|  |               r.state := ACK | ||||||
|  |             } | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
|       } |  | ||||||
|  |  | ||||||
|       // Advance the put state |         // We need to deal with a potential D response in the same cycle as the A request | ||||||
|       when (source_c.fire()) { |         val d_cam_sel_raw = cam_a.map(_.bits.source === in.d.bits.source) | ||||||
|         (a_cam_sel_put zip cam_s) foreach { case (en, r) => |         val d_cam_sel_match = (d_cam_sel_raw zip cam_dmatch) map { case (a,b) => a&&b } | ||||||
|           when (en) { |         val d_cam_data = Mux1H(d_cam_sel_match, cam_d.map(_.data)) | ||||||
|             r.state := ACK |         val d_cam_sel_bypass = if (edgeOut.manager.minLatency > 0) Bool(false) else | ||||||
|  |                                out.d.bits.source === in.a.bits.source && in.a.valid && !a_isSupported | ||||||
|  |         val d_cam_sel = (a_cam_sel_free zip d_cam_sel_match) map { case (a,d) => Mux(d_cam_sel_bypass, a, d) } | ||||||
|  |         val d_cam_sel_any = d_cam_sel_bypass || d_cam_sel_match.reduce(_ || _) | ||||||
|  |         val d_ackd = out.d.bits.opcode === TLMessages.AccessAckData | ||||||
|  |         val d_ack  = out.d.bits.opcode === TLMessages.AccessAck | ||||||
|  |  | ||||||
|  |         when (out.d.fire()) { | ||||||
|  |           (d_cam_sel zip cam_d) foreach { case (en, r) => | ||||||
|  |             when (en && d_ackd) { | ||||||
|  |               r.data := out.d.bits.data | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |           (d_cam_sel zip cam_s) foreach { case (en, r) => | ||||||
|  |             when (en) { | ||||||
|  |               // Note: it is important that this comes AFTER the := GET, so we can go FREE=>GET=>AMO in one cycle | ||||||
|  |               r.state := Mux(d_ackd, AMO, FREE) | ||||||
|  |             } | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
|       } |  | ||||||
|  |  | ||||||
|       // We need to deal with a potential D response in the same cycle as the A request |         val d_drop = d_ackd && d_cam_sel_any | ||||||
|       val d_cam_sel_raw = cam_a.map(_.bits.source === in.d.bits.source) |         val d_replace = d_ack && d_cam_sel_match.reduce(_ || _) | ||||||
|       val d_cam_sel_match = (d_cam_sel_raw zip cam_dmatch) map { case (a,b) => a&&b } |  | ||||||
|       val d_cam_data = Mux1H(d_cam_sel_match, cam_d.map(_.data)) |  | ||||||
|       val d_cam_sel_bypass = if (edgeOut.manager.minLatency > 0) Bool(false) else |  | ||||||
|                              out.d.bits.source === in.a.bits.source && in.a.valid && !a_isSupported |  | ||||||
|       val d_cam_sel = (a_cam_sel_free zip d_cam_sel_match) map { case (a,d) => Mux(d_cam_sel_bypass, a, d) } |  | ||||||
|       val d_cam_sel_any = d_cam_sel_bypass || d_cam_sel_match.reduce(_ || _) |  | ||||||
|       val d_ackd = out.d.bits.opcode === TLMessages.AccessAckData |  | ||||||
|       val d_ack  = out.d.bits.opcode === TLMessages.AccessAck |  | ||||||
|  |  | ||||||
|       when (out.d.fire()) { |         in.d.valid := out.d.valid && !d_drop | ||||||
|         (d_cam_sel zip cam_d) foreach { case (en, r) => |         out.d.ready := in.d.ready || d_drop | ||||||
|           when (en && d_ackd) { |  | ||||||
|             r.data := out.d.bits.data |         in.d.bits := out.d.bits | ||||||
|           } |         when (d_replace) { // minimal muxes | ||||||
|         } |           in.d.bits.opcode := TLMessages.AccessAckData | ||||||
|         (d_cam_sel zip cam_s) foreach { case (en, r) => |           in.d.bits.data := d_cam_data | ||||||
|           when (en) { |  | ||||||
|             // Note: it is important that this comes AFTER the := GET, so we can go FREE=>GET=>AMO in one cycle |  | ||||||
|             r.state := Mux(d_ackd, AMO, FREE) |  | ||||||
|           } |  | ||||||
|         } |         } | ||||||
|  |       } else { | ||||||
|  |         out.a.valid := in.a.valid | ||||||
|  |         in.a.ready := out.a.ready | ||||||
|  |         out.a.bits := in.a.bits | ||||||
|  |  | ||||||
|  |         in.d.valid := out.d.valid | ||||||
|  |         out.d.ready := in.d.ready | ||||||
|  |         in.d.bits := out.d.bits | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       val d_drop = d_ackd && d_cam_sel_any |       if (edgeOut.manager.anySupportAcquireB && edgeIn.client.anySupportProbe) { | ||||||
|       val d_replace = d_ack && d_cam_sel_match.reduce(_ || _) |         in.b.valid := out.b.valid | ||||||
|  |         out.b.ready := in.b.ready | ||||||
|  |         in.b.bits := out.b.bits | ||||||
|  |  | ||||||
|       in.d.valid := out.d.valid && !d_drop |         out.c.valid := in.c.valid | ||||||
|       out.d.ready := in.d.ready || d_drop |         in.c.ready := out.c.ready | ||||||
|  |         out.c.bits := in.c.bits | ||||||
|  |  | ||||||
|       in.d.bits := out.d.bits |         out.e.valid := in.e.valid | ||||||
|       when (d_replace) { // minimal muxes |         in.e.ready := out.e.ready | ||||||
|         in.d.bits.opcode := TLMessages.AccessAckData |         out.e.bits := in.e.bits | ||||||
|         in.d.bits.data := d_cam_data |       } else { | ||||||
|  |         in.b.valid := Bool(false) | ||||||
|  |         in.c.ready := Bool(true) | ||||||
|  |         in.e.ready := Bool(true) | ||||||
|  |         out.b.ready := Bool(true) | ||||||
|  |         out.c.valid := Bool(false) | ||||||
|  |         out.e.valid := Bool(false) | ||||||
|       } |       } | ||||||
|     } else { |  | ||||||
|       out.a.valid := in.a.valid |  | ||||||
|       in.a.ready := out.a.ready |  | ||||||
|       out.a.bits := in.a.bits |  | ||||||
|  |  | ||||||
|       in.d.valid := out.d.valid |  | ||||||
|       out.d.ready := in.d.ready |  | ||||||
|       in.d.bits := out.d.bits |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (edgeOut.manager.anySupportAcquireB && edgeIn.client.anySupportProbe) { |  | ||||||
|       in.b.valid := out.b.valid |  | ||||||
|       out.b.ready := in.b.ready |  | ||||||
|       in.b.bits := out.b.bits |  | ||||||
|  |  | ||||||
|       out.c.valid := in.c.valid |  | ||||||
|       in.c.ready := out.c.ready |  | ||||||
|       out.c.bits := in.c.bits |  | ||||||
|  |  | ||||||
|       out.e.valid := in.e.valid |  | ||||||
|       in.e.ready := out.e.ready |  | ||||||
|       out.e.bits := in.e.bits |  | ||||||
|     } else { |  | ||||||
|       in.b.valid := Bool(false) |  | ||||||
|       in.c.ready := Bool(true) |  | ||||||
|       in.e.ready := Bool(true) |  | ||||||
|       out.b.ready := Bool(true) |  | ||||||
|       out.c.valid := Bool(false) |  | ||||||
|       out.e.valid := Bool(false) |  | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
| @@ -284,6 +272,20 @@ object TLAtomicAutomata | |||||||
|     atomics.node := x |     atomics.node := x | ||||||
|     atomics.node |     atomics.node | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   case class CAMParams(a: TLBundleParameters, domainsNeedingHelp: Int) | ||||||
|  |  | ||||||
|  |   class CAM_S(params: CAMParams) extends GenericParameterizedBundle(params) { | ||||||
|  |     val state = UInt(width = 2) | ||||||
|  |   } | ||||||
|  |   class CAM_A(params: CAMParams) extends GenericParameterizedBundle(params) { | ||||||
|  |     val bits    = new TLBundleA(params.a) | ||||||
|  |     val fifoId  = UInt(width = log2Up(params.domainsNeedingHelp)) | ||||||
|  |     val lut     = UInt(width = 4) | ||||||
|  |   } | ||||||
|  |   class CAM_D(params: CAMParams) extends GenericParameterizedBundle(params) { | ||||||
|  |     val data = UInt(width = params.a.dataBits) | ||||||
|  |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| /** Synthesizeable unit tests */ | /** Synthesizeable unit tests */ | ||||||
|   | |||||||
| @@ -13,11 +13,11 @@ class TLBroadcast(lineBytes: Int, numTrackers: Int = 4, bufferless: Boolean = fa | |||||||
|   require (numTrackers > 0) |   require (numTrackers > 0) | ||||||
|  |  | ||||||
|   val node = TLAdapterNode( |   val node = TLAdapterNode( | ||||||
|     clientFn  = { case Seq(cp) => |     clientFn  = { cp => | ||||||
|       cp.copy(clients = Seq(TLClientParameters( |       cp.copy(clients = Seq(TLClientParameters( | ||||||
|         sourceId = IdRange(0, 1 << log2Ceil(cp.endSourceId*4))))) |         sourceId = IdRange(0, 1 << log2Ceil(cp.endSourceId*4))))) | ||||||
|     }, |     }, | ||||||
|     managerFn = { case Seq(mp) => |     managerFn = { mp => | ||||||
|       mp.copy( |       mp.copy( | ||||||
|         endSinkId  = numTrackers, |         endSinkId  = numTrackers, | ||||||
|         managers   = mp.managers.map { m => |         managers   = mp.managers.map { m => | ||||||
| @@ -56,154 +56,152 @@ class TLBroadcast(lineBytes: Int, numTrackers: Int = 4, bufferless: Boolean = fa | |||||||
|       val out = node.bundleOut |       val out = node.bundleOut | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     val in = io.in(0) |     ((io.in zip io.out) zip (node.edgesIn zip node.edgesOut)) foreach { case ((in, out), (edgeIn, edgeOut)) => | ||||||
|     val out = io.out(0) |       val clients = edgeIn.client.clients | ||||||
|     val edgeIn = node.edgesIn(0) |       val managers = edgeOut.manager.managers | ||||||
|     val edgeOut = node.edgesOut(0) |       val lineShift = log2Ceil(lineBytes) | ||||||
|     val clients = edgeIn.client.clients |  | ||||||
|     val managers = edgeOut.manager.managers |  | ||||||
|     val lineShift = log2Ceil(lineBytes) |  | ||||||
|  |  | ||||||
|     import TLBroadcastConstants._ |       import TLBroadcastConstants._ | ||||||
|  |  | ||||||
|     require (lineBytes >= edgeOut.manager.beatBytes) |       require (lineBytes >= edgeOut.manager.beatBytes) | ||||||
|     // For the probe walker, we need to identify all the caches |       // For the probe walker, we need to identify all the caches | ||||||
|     val caches = clients.filter(_.supportsProbe).map(_.sourceId) |       val caches = clients.filter(_.supportsProbe).map(_.sourceId) | ||||||
|     val cache_targets = caches.map(c => UInt(c.start)) |       val cache_targets = caches.map(c => UInt(c.start)) | ||||||
|  |  | ||||||
|     // Create the request tracker queues |       // Create the request tracker queues | ||||||
|     val trackers = Seq.tabulate(numTrackers) { id => |       val trackers = Seq.tabulate(numTrackers) { id => | ||||||
|       Module(new TLBroadcastTracker(id, lineBytes, log2Up(caches.size+1), bufferless, edgeIn, edgeOut)).io |         Module(new TLBroadcastTracker(id, lineBytes, log2Up(caches.size+1), bufferless, edgeIn, edgeOut)).io | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       // We always accept E | ||||||
|  |       in.e.ready := Bool(true) | ||||||
|  |       (trackers zip UIntToOH(in.e.bits.sink).toBools) foreach { case (tracker, select) => | ||||||
|  |         tracker.e_last := select && in.e.fire() | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       // Depending on the high source bits, we might transform D | ||||||
|  |       val d_high = log2Ceil(edgeIn.client.endSourceId) | ||||||
|  |       val d_what = out.d.bits.source(d_high+1, d_high) | ||||||
|  |       val d_drop = d_what === DROP | ||||||
|  |       val d_hasData = edgeOut.hasData(out.d.bits) | ||||||
|  |       val d_normal = Wire(in.d) | ||||||
|  |       val d_trackerOH = Vec(trackers.map { t => !t.idle && t.source === d_normal.bits.source }).asUInt | ||||||
|  |  | ||||||
|  |       assert (!out.d.valid || !d_drop || out.d.bits.opcode === TLMessages.AccessAck) | ||||||
|  |  | ||||||
|  |       out.d.ready := d_normal.ready || d_drop | ||||||
|  |       d_normal.valid := out.d.valid && !d_drop | ||||||
|  |       d_normal.bits := out.d.bits // truncates source | ||||||
|  |       when (d_what(1)) { // TRANSFORM_* | ||||||
|  |         d_normal.bits.opcode := Mux(d_hasData, TLMessages.GrantData, TLMessages.ReleaseAck) | ||||||
|  |         d_normal.bits.param  := Mux(d_hasData, Mux(d_what(0), TLPermissions.toT, TLPermissions.toB), UInt(0)) | ||||||
|  |       } | ||||||
|  |       d_normal.bits.sink := OHToUInt(d_trackerOH) | ||||||
|  |       assert (!d_normal.valid || (d_trackerOH.orR() || d_normal.bits.opcode === TLMessages.ReleaseAck)) | ||||||
|  |  | ||||||
|  |       // A tracker response is anything neither dropped nor a ReleaseAck | ||||||
|  |       val d_response = d_hasData || !d_what(1) | ||||||
|  |       val d_last = edgeIn.last(d_normal) | ||||||
|  |       (trackers zip d_trackerOH.toBools) foreach { case (tracker, select) => | ||||||
|  |         tracker.d_last := select && d_normal.fire() && d_response && d_last | ||||||
|  |         tracker.probedack := select && out.d.fire() && d_drop | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       // Incoming C can be: | ||||||
|  |       // ProbeAck     => decrement tracker, drop  | ||||||
|  |       // ProbeAckData => decrement tracker, send out A as PutFull(DROP) | ||||||
|  |       // ReleaseData  =>                    send out A as PutFull(TRANSFORM) | ||||||
|  |       // Release      => send out D as ReleaseAck | ||||||
|  |  | ||||||
|  |       val c_probeack     = in.c.bits.opcode === TLMessages.ProbeAck | ||||||
|  |       val c_probeackdata = in.c.bits.opcode === TLMessages.ProbeAckData | ||||||
|  |       val c_releasedata  = in.c.bits.opcode === TLMessages.ReleaseData | ||||||
|  |       val c_release      = in.c.bits.opcode === TLMessages.Release | ||||||
|  |       val c_trackerOH    = trackers.map { t => t.line === (in.c.bits.address >> lineShift) } | ||||||
|  |       val c_trackerSrc   = Mux1H(c_trackerOH, trackers.map { _.source }) | ||||||
|  |  | ||||||
|  |       // Decrement the tracker's outstanding probe counter | ||||||
|  |       (trackers zip c_trackerOH) foreach { case (tracker, select) => | ||||||
|  |         tracker.probenack := in.c.fire() && c_probeack && select | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       val releaseack = Wire(in.d) | ||||||
|  |       val putfull = Wire(out.a) | ||||||
|  |  | ||||||
|  |       in.c.ready := c_probeack || Mux(c_release, releaseack.ready, putfull.ready) | ||||||
|  |  | ||||||
|  |       releaseack.valid := in.c.valid && c_release | ||||||
|  |       releaseack.bits  := edgeIn.ReleaseAck(in.c.bits.address, UInt(0), in.c.bits.source, in.c.bits.size) | ||||||
|  |  | ||||||
|  |       val put_what = Mux(c_releasedata, TRANSFORM_B, DROP) | ||||||
|  |       val put_who  = Mux(c_releasedata, in.c.bits.source, c_trackerSrc) | ||||||
|  |       putfull.valid := in.c.valid && (c_probeackdata || c_releasedata) | ||||||
|  |       putfull.bits := edgeOut.Put(Cat(put_what, put_who), in.c.bits.address, in.c.bits.size, in.c.bits.data)._2 | ||||||
|  |  | ||||||
|  |       // Combine ReleaseAck or the modified D | ||||||
|  |       TLArbiter.lowest(edgeOut, in.d, releaseack, d_normal) | ||||||
|  |       // Combine the PutFull with the trackers | ||||||
|  |       TLArbiter.lowestFromSeq(edgeOut, out.a, putfull +: trackers.map(_.out_a)) | ||||||
|  |  | ||||||
|  |       // The Probe FSM walks all caches and probes them | ||||||
|  |       val probe_todo = RegInit(UInt(0, width = max(1, caches.size))) | ||||||
|  |       val probe_line = Reg(UInt()) | ||||||
|  |       val probe_perms = Reg(UInt(width = 2)) | ||||||
|  |       val probe_next = probe_todo & ~(leftOR(probe_todo) << 1) | ||||||
|  |       val probe_busy = probe_todo.orR() | ||||||
|  |       val probe_target = if (caches.size == 0) UInt(0) else Mux1H(probe_next, cache_targets) | ||||||
|  |  | ||||||
|  |       // Probe whatever the FSM wants to do next | ||||||
|  |       in.b.valid := probe_busy | ||||||
|  |       if (caches.size != 0) { | ||||||
|  |         in.b.bits := edgeIn.Probe(probe_line << lineShift, probe_target, UInt(lineShift), probe_perms)._2 | ||||||
|  |       } | ||||||
|  |       when (in.b.fire()) { probe_todo := probe_todo & ~probe_next } | ||||||
|  |  | ||||||
|  |       // Which cache does a request come from? | ||||||
|  |       val a_cache = if (caches.size == 0) UInt(1) else Vec(caches.map(_.contains(in.a.bits.source))).asUInt | ||||||
|  |       val a_first = edgeIn.first(in.a) | ||||||
|  |  | ||||||
|  |       // To accept a request from A, the probe FSM must be idle and there must be a matching tracker | ||||||
|  |       val freeTrackers = Vec(trackers.map { t => t.idle }).asUInt | ||||||
|  |       val freeTracker = freeTrackers.orR() | ||||||
|  |       val matchTrackers = Vec(trackers.map { t => t.line === in.a.bits.address >> lineShift }).asUInt | ||||||
|  |       val matchTracker = matchTrackers.orR() | ||||||
|  |       val allocTracker = freeTrackers & ~(leftOR(freeTrackers) << 1) | ||||||
|  |       val selectTracker = Mux(matchTracker, matchTrackers, allocTracker) | ||||||
|  |  | ||||||
|  |       val trackerReady = Vec(trackers.map(_.in_a.ready)).asUInt | ||||||
|  |       in.a.ready := (!a_first || !probe_busy) && (selectTracker & trackerReady).orR() | ||||||
|  |       (trackers zip selectTracker.toBools) foreach { case (t, select) => | ||||||
|  |         t.in_a.valid := in.a.valid && select && (!a_first || !probe_busy) | ||||||
|  |         t.in_a.bits := in.a.bits | ||||||
|  |         t.in_a_first := a_first | ||||||
|  |         t.probe := (if (caches.size == 0) UInt(0) else Mux(a_cache.orR(), UInt(caches.size-1), UInt(caches.size))) | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       when (in.a.fire() && a_first) { | ||||||
|  |         probe_todo  := ~a_cache // probe all but the cache who poked us | ||||||
|  |         probe_line  := in.a.bits.address >> lineShift | ||||||
|  |         probe_perms := MuxLookup(in.a.bits.opcode, Wire(UInt(width = 2)), Array( | ||||||
|  |           TLMessages.PutFullData    -> TLPermissions.toN, | ||||||
|  |           TLMessages.PutPartialData -> TLPermissions.toN, | ||||||
|  |           TLMessages.ArithmeticData -> TLPermissions.toN, | ||||||
|  |           TLMessages.LogicalData    -> TLPermissions.toN, | ||||||
|  |           TLMessages.Get            -> TLPermissions.toB, | ||||||
|  |           TLMessages.Hint           -> MuxLookup(in.a.bits.param, Wire(UInt(width = 2)), Array( | ||||||
|  |             TLHints.PREFETCH_READ   -> TLPermissions.toB, | ||||||
|  |             TLHints.PREFETCH_WRITE  -> TLPermissions.toN)), | ||||||
|  |           TLMessages.Acquire        -> MuxLookup(in.a.bits.param, Wire(UInt(width = 2)), Array( | ||||||
|  |             TLPermissions.NtoB      -> TLPermissions.toB, | ||||||
|  |             TLPermissions.NtoT      -> TLPermissions.toN, | ||||||
|  |             TLPermissions.BtoT      -> TLPermissions.toN)))) | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       // The outer TL connections may not be cached | ||||||
|  |       out.b.ready := Bool(true) | ||||||
|  |       out.c.valid := Bool(false) | ||||||
|  |       out.e.valid := Bool(false) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // We always accept E |  | ||||||
|     in.e.ready := Bool(true) |  | ||||||
|     (trackers zip UIntToOH(in.e.bits.sink).toBools) foreach { case (tracker, select) => |  | ||||||
|       tracker.e_last := select && in.e.fire() |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // Depending on the high source bits, we might transform D |  | ||||||
|     val d_high = log2Ceil(edgeIn.client.endSourceId) |  | ||||||
|     val d_what = out.d.bits.source(d_high+1, d_high) |  | ||||||
|     val d_drop = d_what === DROP |  | ||||||
|     val d_hasData = edgeOut.hasData(out.d.bits) |  | ||||||
|     val d_normal = Wire(in.d) |  | ||||||
|     val d_trackerOH = Vec(trackers.map { t => !t.idle && t.source === d_normal.bits.source }).asUInt |  | ||||||
|  |  | ||||||
|     assert (!out.d.valid || !d_drop || out.d.bits.opcode === TLMessages.AccessAck) |  | ||||||
|  |  | ||||||
|     out.d.ready := d_normal.ready || d_drop |  | ||||||
|     d_normal.valid := out.d.valid && !d_drop |  | ||||||
|     d_normal.bits := out.d.bits // truncates source |  | ||||||
|     when (d_what(1)) { // TRANSFORM_* |  | ||||||
|       d_normal.bits.opcode := Mux(d_hasData, TLMessages.GrantData, TLMessages.ReleaseAck) |  | ||||||
|       d_normal.bits.param  := Mux(d_hasData, Mux(d_what(0), TLPermissions.toT, TLPermissions.toB), UInt(0)) |  | ||||||
|     } |  | ||||||
|     d_normal.bits.sink := OHToUInt(d_trackerOH) |  | ||||||
|     assert (!d_normal.valid || (d_trackerOH.orR() || d_normal.bits.opcode === TLMessages.ReleaseAck)) |  | ||||||
|  |  | ||||||
|     // A tracker response is anything neither dropped nor a ReleaseAck |  | ||||||
|     val d_response = d_hasData || !d_what(1) |  | ||||||
|     val d_last = edgeIn.last(d_normal) |  | ||||||
|     (trackers zip d_trackerOH.toBools) foreach { case (tracker, select) => |  | ||||||
|       tracker.d_last := select && d_normal.fire() && d_response && d_last |  | ||||||
|       tracker.probedack := select && out.d.fire() && d_drop |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // Incoming C can be: |  | ||||||
|     // ProbeAck     => decrement tracker, drop  |  | ||||||
|     // ProbeAckData => decrement tracker, send out A as PutFull(DROP) |  | ||||||
|     // ReleaseData  =>                    send out A as PutFull(TRANSFORM) |  | ||||||
|     // Release      => send out D as ReleaseAck |  | ||||||
|  |  | ||||||
|     val c_probeack     = in.c.bits.opcode === TLMessages.ProbeAck |  | ||||||
|     val c_probeackdata = in.c.bits.opcode === TLMessages.ProbeAckData |  | ||||||
|     val c_releasedata  = in.c.bits.opcode === TLMessages.ReleaseData |  | ||||||
|     val c_release      = in.c.bits.opcode === TLMessages.Release |  | ||||||
|     val c_trackerOH    = trackers.map { t => t.line === (in.c.bits.address >> lineShift) } |  | ||||||
|     val c_trackerSrc   = Mux1H(c_trackerOH, trackers.map { _.source }) |  | ||||||
|  |  | ||||||
|     // Decrement the tracker's outstanding probe counter |  | ||||||
|     (trackers zip c_trackerOH) foreach { case (tracker, select) => |  | ||||||
|       tracker.probenack := in.c.fire() && c_probeack && select |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     val releaseack = Wire(in.d) |  | ||||||
|     val putfull = Wire(out.a) |  | ||||||
|  |  | ||||||
|     in.c.ready := c_probeack || Mux(c_release, releaseack.ready, putfull.ready) |  | ||||||
|  |  | ||||||
|     releaseack.valid := in.c.valid && c_release |  | ||||||
|     releaseack.bits  := edgeIn.ReleaseAck(in.c.bits.address, UInt(0), in.c.bits.source, in.c.bits.size) |  | ||||||
|  |  | ||||||
|     val put_what = Mux(c_releasedata, TRANSFORM_B, DROP) |  | ||||||
|     val put_who  = Mux(c_releasedata, in.c.bits.source, c_trackerSrc) |  | ||||||
|     putfull.valid := in.c.valid && (c_probeackdata || c_releasedata) |  | ||||||
|     putfull.bits := edgeOut.Put(Cat(put_what, put_who), in.c.bits.address, in.c.bits.size, in.c.bits.data)._2 |  | ||||||
|  |  | ||||||
|     // Combine ReleaseAck or the modified D |  | ||||||
|     TLArbiter.lowest(edgeOut, in.d, releaseack, d_normal) |  | ||||||
|     // Combine the PutFull with the trackers |  | ||||||
|     TLArbiter.lowestFromSeq(edgeOut, out.a, putfull +: trackers.map(_.out_a)) |  | ||||||
|  |  | ||||||
|     // The Probe FSM walks all caches and probes them |  | ||||||
|     val probe_todo = RegInit(UInt(0, width = max(1, caches.size))) |  | ||||||
|     val probe_line = Reg(UInt()) |  | ||||||
|     val probe_perms = Reg(UInt(width = 2)) |  | ||||||
|     val probe_next = probe_todo & ~(leftOR(probe_todo) << 1) |  | ||||||
|     val probe_busy = probe_todo.orR() |  | ||||||
|     val probe_target = if (caches.size == 0) UInt(0) else Mux1H(probe_next, cache_targets) |  | ||||||
|  |  | ||||||
|     // Probe whatever the FSM wants to do next |  | ||||||
|     in.b.valid := probe_busy |  | ||||||
|     if (caches.size != 0) { |  | ||||||
|       in.b.bits := edgeIn.Probe(probe_line << lineShift, probe_target, UInt(lineShift), probe_perms)._2 |  | ||||||
|     } |  | ||||||
|     when (in.b.fire()) { probe_todo := probe_todo & ~probe_next } |  | ||||||
|  |  | ||||||
|     // Which cache does a request come from? |  | ||||||
|     val a_cache = if (caches.size == 0) UInt(1) else Vec(caches.map(_.contains(in.a.bits.source))).asUInt |  | ||||||
|     val a_first = edgeIn.first(in.a) |  | ||||||
|  |  | ||||||
|     // To accept a request from A, the probe FSM must be idle and there must be a matching tracker |  | ||||||
|     val freeTrackers = Vec(trackers.map { t => t.idle }).asUInt |  | ||||||
|     val freeTracker = freeTrackers.orR() |  | ||||||
|     val matchTrackers = Vec(trackers.map { t => t.line === in.a.bits.address >> lineShift }).asUInt |  | ||||||
|     val matchTracker = matchTrackers.orR() |  | ||||||
|     val allocTracker = freeTrackers & ~(leftOR(freeTrackers) << 1) |  | ||||||
|     val selectTracker = Mux(matchTracker, matchTrackers, allocTracker) |  | ||||||
|  |  | ||||||
|     val trackerReady = Vec(trackers.map(_.in_a.ready)).asUInt |  | ||||||
|     in.a.ready := (!a_first || !probe_busy) && (selectTracker & trackerReady).orR() |  | ||||||
|     (trackers zip selectTracker.toBools) foreach { case (t, select) => |  | ||||||
|       t.in_a.valid := in.a.valid && select && (!a_first || !probe_busy) |  | ||||||
|       t.in_a.bits := in.a.bits |  | ||||||
|       t.in_a_first := a_first |  | ||||||
|       t.probe := (if (caches.size == 0) UInt(0) else Mux(a_cache.orR(), UInt(caches.size-1), UInt(caches.size))) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     when (in.a.fire() && a_first) { |  | ||||||
|       probe_todo  := ~a_cache // probe all but the cache who poked us |  | ||||||
|       probe_line  := in.a.bits.address >> lineShift |  | ||||||
|       probe_perms := MuxLookup(in.a.bits.opcode, Wire(UInt(width = 2)), Array( |  | ||||||
|         TLMessages.PutFullData    -> TLPermissions.toN, |  | ||||||
|         TLMessages.PutPartialData -> TLPermissions.toN, |  | ||||||
|         TLMessages.ArithmeticData -> TLPermissions.toN, |  | ||||||
|         TLMessages.LogicalData    -> TLPermissions.toN, |  | ||||||
|         TLMessages.Get            -> TLPermissions.toB, |  | ||||||
|         TLMessages.Hint           -> MuxLookup(in.a.bits.param, Wire(UInt(width = 2)), Array( |  | ||||||
|           TLHints.PREFETCH_READ   -> TLPermissions.toB, |  | ||||||
|           TLHints.PREFETCH_WRITE  -> TLPermissions.toN)), |  | ||||||
|         TLMessages.Acquire        -> MuxLookup(in.a.bits.param, Wire(UInt(width = 2)), Array( |  | ||||||
|           TLPermissions.NtoB      -> TLPermissions.toB, |  | ||||||
|           TLPermissions.NtoT      -> TLPermissions.toN, |  | ||||||
|           TLPermissions.BtoT      -> TLPermissions.toN)))) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // The outer TL connections may not be cached |  | ||||||
|     out.b.ready := Bool(true) |  | ||||||
|     out.c.valid := Bool(false) |  | ||||||
|     out.e.valid := Bool(false) |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -18,8 +18,8 @@ class TLBuffer(a: Int = 2, b: Int = 2, c: Int = 2, d: Int = 2, e: Int = 2, pipe: | |||||||
|   require (e >= 0) |   require (e >= 0) | ||||||
|  |  | ||||||
|   val node = TLAdapterNode( |   val node = TLAdapterNode( | ||||||
|     clientFn  = { case Seq(p) => p.copy(minLatency = p.minLatency + min(1,b) + min(1,c)) }, |     clientFn  = { p => p.copy(minLatency = p.minLatency + min(1,b) + min(1,c)) }, | ||||||
|     managerFn = { case Seq(p) => p.copy(minLatency = p.minLatency + min(1,a) + min(1,d)) }) |     managerFn = { p => p.copy(minLatency = p.minLatency + min(1,a) + min(1,d)) }) | ||||||
|  |  | ||||||
|   lazy val module = new LazyModuleImp(this) { |   lazy val module = new LazyModuleImp(this) { | ||||||
|     val io = new Bundle { |     val io = new Bundle { | ||||||
|   | |||||||
| @@ -210,11 +210,11 @@ final class DecoupledSnoop[+T <: Data](gen: T) extends Bundle | |||||||
|  |  | ||||||
| object DecoupledSnoop | object DecoupledSnoop | ||||||
| { | { | ||||||
|   def apply[T <: Data](i: DecoupledIO[T]) = { |   def apply[T <: Data](source: DecoupledIO[T], sink: DecoupledIO[T]) = { | ||||||
|     val out = Wire(new DecoupledSnoop(i.bits)) |     val out = Wire(new DecoupledSnoop(sink.bits)) | ||||||
|     out.ready := i.ready |     out.ready := sink.ready | ||||||
|     out.valid := i.valid |     out.valid := source.valid | ||||||
|     out.bits  := i.bits |     out.bits  := source.bits | ||||||
|     out |     out | ||||||
|   } |   } | ||||||
| } | } | ||||||
| @@ -230,13 +230,13 @@ class TLBundleSnoop(params: TLBundleParameters) extends TLBundleBase(params) | |||||||
|  |  | ||||||
| object TLBundleSnoop | object TLBundleSnoop | ||||||
| { | { | ||||||
|   def apply(x: TLBundle) = { |   def apply(source: TLBundle, sink: TLBundle) = { | ||||||
|     val out = Wire(new TLBundleSnoop(x.params)) |     val out = Wire(new TLBundleSnoop(sink.params)) | ||||||
|     out.a <> DecoupledSnoop(x.a) |     out.a := DecoupledSnoop(source.a, sink.a) | ||||||
|     out.b <> DecoupledSnoop(x.b) |     out.b := DecoupledSnoop(sink.b, source.b) | ||||||
|     out.c <> DecoupledSnoop(x.c) |     out.c := DecoupledSnoop(source.c, sink.c) | ||||||
|     out.d <> DecoupledSnoop(x.d) |     out.d := DecoupledSnoop(sink.d, source.d) | ||||||
|     out.e <> DecoupledSnoop(x.e) |     out.e := DecoupledSnoop(source.e, sink.e) | ||||||
|     out |     out | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -12,10 +12,10 @@ import TLMessages._ | |||||||
| class TLCacheCork(unsafe: Boolean = false)(implicit p: Parameters) extends LazyModule | class TLCacheCork(unsafe: Boolean = false)(implicit p: Parameters) extends LazyModule | ||||||
| { | { | ||||||
|   val node = TLAdapterNode( |   val node = TLAdapterNode( | ||||||
|     clientFn  = { case Seq(cp) => |     clientFn  = { case cp => | ||||||
|       cp.copy(clients = cp.clients.map { c => c.copy( |       cp.copy(clients = cp.clients.map { c => c.copy( | ||||||
|         sourceId = IdRange(c.sourceId.start*2, c.sourceId.end*2))})}, |         sourceId = IdRange(c.sourceId.start*2, c.sourceId.end*2))})}, | ||||||
|     managerFn = { case Seq(mp) => |     managerFn = { case mp => | ||||||
|       mp.copy(managers = mp.managers.map { m => m.copy( |       mp.copy(managers = mp.managers.map { m => m.copy( | ||||||
|         regionType         = if (m.regionType == RegionType.UNCACHED) RegionType.TRACKED else m.regionType, |         regionType         = if (m.regionType == RegionType.UNCACHED) RegionType.TRACKED else m.regionType, | ||||||
|         supportsAcquireB   = m.supportsGet, |         supportsAcquireB   = m.supportsGet, | ||||||
| @@ -27,93 +27,89 @@ class TLCacheCork(unsafe: Boolean = false)(implicit p: Parameters) extends LazyM | |||||||
|       val out = node.bundleOut |       val out = node.bundleOut | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     val edgeIn = node.edgesIn(0) |     ((io.in zip io.out) zip (node.edgesIn zip node.edgesOut)) foreach { case ((in, out), (edgeIn, edgeOut)) => | ||||||
|     val edgeOut = node.edgesOut(0) |       require (edgeIn.client.clients.size == 1 || unsafe, "Only one client can safely use a TLCacheCork") | ||||||
|  |       require (edgeIn.client.clients.filter(_.supportsProbe).size == 1, "Only one caching client allowed") | ||||||
|  |       edgeOut.manager.managers.foreach { case m => | ||||||
|  |         require (!m.supportsAcquireB, "Cannot support caches beyond the Cork") | ||||||
|  |       } | ||||||
|  |  | ||||||
|     require (edgeIn.client.clients.size == 1 || unsafe, "Only one client can safely use a TLCacheCork") |       // The Cork turns [Acquire=>Get] => [AccessAckData=>GrantData] | ||||||
|     require (edgeIn.client.clients.filter(_.supportsProbe).size == 1, "Only one caching client allowed") |       //            and [ReleaseData=>PutFullData] => [AccessAck=>ReleaseAck] | ||||||
|     edgeOut.manager.managers.foreach { case m => |       // We need to encode information sufficient to reverse the transformation in output. | ||||||
|       require (!m.supportsAcquireB, "Cannot support caches beyond the Cork") |       // A caveat is that we get Acquire+Release with the same source and must keep the | ||||||
|  |       // source unique after transformation onto the A channel. | ||||||
|  |       // The coding scheme is: | ||||||
|  |       //   Put: 1, Release: 0 => AccessAck | ||||||
|  |       //   *: 0, Acquire: 1 => AccessAckData | ||||||
|  |  | ||||||
|  |       // Take requests from A to A | ||||||
|  |       val isPut = in.a.bits.opcode === PutFullData || in.a.bits.opcode === PutPartialData | ||||||
|  |       val a_a = Wire(out.a) | ||||||
|  |       a_a <> in.a | ||||||
|  |       a_a.bits.source := in.a.bits.source << 1 | Mux(isPut, UInt(1), UInt(0)) | ||||||
|  |  | ||||||
|  |       // Transform Acquire into Get | ||||||
|  |       when (in.a.bits.opcode === Acquire) { | ||||||
|  |         a_a.bits.opcode := Get | ||||||
|  |         a_a.bits.param  := UInt(0) | ||||||
|  |         a_a.bits.source := in.a.bits.source << 1 | UInt(1) | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       // Take ReleaseData from C to A; Release from C to D | ||||||
|  |       val c_a = Wire(out.a) | ||||||
|  |       c_a.valid := in.c.valid && in.c.bits.opcode === ReleaseData | ||||||
|  |       c_a.bits.opcode  := PutFullData | ||||||
|  |       c_a.bits.param   := UInt(0) | ||||||
|  |       c_a.bits.size    := in.c.bits.size | ||||||
|  |       c_a.bits.source  := in.c.bits.source << 1 | ||||||
|  |       c_a.bits.address := in.c.bits.address | ||||||
|  |       c_a.bits.mask    := edgeOut.mask(in.c.bits.address, in.c.bits.size) | ||||||
|  |       c_a.bits.data    := in.c.bits.data | ||||||
|  |  | ||||||
|  |       val c_d = Wire(in.d) | ||||||
|  |       c_d.valid := in.c.valid && in.c.bits.opcode === Release | ||||||
|  |       c_d.bits.opcode  := ReleaseAck | ||||||
|  |       c_d.bits.param   := UInt(0) | ||||||
|  |       c_d.bits.size    := in.c.bits.size | ||||||
|  |       c_d.bits.source  := in.c.bits.source | ||||||
|  |       c_d.bits.sink    := UInt(0) | ||||||
|  |       c_d.bits.addr_lo := in.c.bits.address | ||||||
|  |       c_d.bits.data    := UInt(0) | ||||||
|  |       c_d.bits.error   := Bool(false) | ||||||
|  |  | ||||||
|  |       assert (!in.c.valid || in.c.bits.opcode === Release || in.c.bits.opcode === ReleaseData) | ||||||
|  |       in.c.ready := Mux(in.c.bits.opcode === Release, c_d.ready, c_a.ready) | ||||||
|  |  | ||||||
|  |       // Discard E | ||||||
|  |       in.e.ready := Bool(true) | ||||||
|  |  | ||||||
|  |       // Block B; should never happen | ||||||
|  |       out.b.ready := Bool(false) | ||||||
|  |       assert (!out.b.valid) | ||||||
|  |  | ||||||
|  |       // Take responses from D and transform them | ||||||
|  |       val d_d = Wire(in.d) | ||||||
|  |       d_d <> out.d | ||||||
|  |       d_d.bits.source := out.d.bits.source >> 1 | ||||||
|  |  | ||||||
|  |       when (out.d.bits.opcode === AccessAckData && out.d.bits.source(0)) { | ||||||
|  |         d_d.bits.opcode := GrantData | ||||||
|  |         d_d.bits.param  := TLPermissions.toT | ||||||
|  |       } | ||||||
|  |       when (out.d.bits.opcode === AccessAck && !out.d.bits.source(0)) { | ||||||
|  |         d_d.bits.opcode := ReleaseAck | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       // Combine the sources of messages into the channels | ||||||
|  |       TLArbiter(TLArbiter.lowestIndexFirst)(out.a, (edgeOut.numBeats1(c_a.bits), c_a), (edgeOut.numBeats1(a_a.bits), a_a)) | ||||||
|  |       TLArbiter(TLArbiter.lowestIndexFirst)(in.d,  (edgeIn .numBeats1(d_d.bits), d_d), (UInt(0), Queue(c_d, 2))) | ||||||
|  |  | ||||||
|  |       // Tie off unused ports | ||||||
|  |       in.b.valid := Bool(false) | ||||||
|  |       out.c.valid := Bool(false) | ||||||
|  |       out.e.valid := Bool(false) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     val out = io.out(0) |  | ||||||
|     val in = io.in(0) |  | ||||||
|  |  | ||||||
|     // The Cork turns [Acquire=>Get] => [AccessAckData=>GrantData] |  | ||||||
|     //            and [ReleaseData=>PutFullData] => [AccessAck=>ReleaseAck] |  | ||||||
|     // We need to encode information sufficient to reverse the transformation in output. |  | ||||||
|     // A caveat is that we get Acquire+Release with the same source and must keep the |  | ||||||
|     // source unique after transformation onto the A channel. |  | ||||||
|     // The coding scheme is: |  | ||||||
|     //   Put: 1, Release: 0 => AccessAck |  | ||||||
|     //   *: 0, Acquire: 1 => AccessAckData |  | ||||||
|  |  | ||||||
|     // Take requests from A to A |  | ||||||
|     val isPut = in.a.bits.opcode === PutFullData || in.a.bits.opcode === PutPartialData |  | ||||||
|     val a_a = Wire(out.a) |  | ||||||
|     a_a <> in.a |  | ||||||
|     a_a.bits.source := in.a.bits.source << 1 | Mux(isPut, UInt(1), UInt(0)) |  | ||||||
|  |  | ||||||
|     // Transform Acquire into Get |  | ||||||
|     when (in.a.bits.opcode === Acquire) { |  | ||||||
|       a_a.bits.opcode := Get |  | ||||||
|       a_a.bits.param  := UInt(0) |  | ||||||
|       a_a.bits.source := in.a.bits.source << 1 | UInt(1) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // Take ReleaseData from C to A; Release from C to D |  | ||||||
|     val c_a = Wire(out.a) |  | ||||||
|     c_a.valid := in.c.valid && in.c.bits.opcode === ReleaseData |  | ||||||
|     c_a.bits.opcode  := PutFullData |  | ||||||
|     c_a.bits.param   := UInt(0) |  | ||||||
|     c_a.bits.size    := in.c.bits.size |  | ||||||
|     c_a.bits.source  := in.c.bits.source << 1 |  | ||||||
|     c_a.bits.address := in.c.bits.address |  | ||||||
|     c_a.bits.mask    := edgeOut.mask(in.c.bits.address, in.c.bits.size) |  | ||||||
|     c_a.bits.data    := in.c.bits.data |  | ||||||
|  |  | ||||||
|     val c_d = Wire(in.d) |  | ||||||
|     c_d.valid := in.c.valid && in.c.bits.opcode === Release |  | ||||||
|     c_d.bits.opcode  := ReleaseAck |  | ||||||
|     c_d.bits.param   := UInt(0) |  | ||||||
|     c_d.bits.size    := in.c.bits.size |  | ||||||
|     c_d.bits.source  := in.c.bits.source |  | ||||||
|     c_d.bits.sink    := UInt(0) |  | ||||||
|     c_d.bits.addr_lo := in.c.bits.address |  | ||||||
|     c_d.bits.data    := UInt(0) |  | ||||||
|     c_d.bits.error   := Bool(false) |  | ||||||
|  |  | ||||||
|     assert (!in.c.valid || in.c.bits.opcode === Release || in.c.bits.opcode === ReleaseData) |  | ||||||
|     in.c.ready := Mux(in.c.bits.opcode === Release, c_d.ready, c_a.ready) |  | ||||||
|  |  | ||||||
|     // Discard E |  | ||||||
|     in.e.ready := Bool(true) |  | ||||||
|  |  | ||||||
|     // Block B; should never happen |  | ||||||
|     out.b.ready := Bool(false) |  | ||||||
|     assert (!out.b.valid) |  | ||||||
|  |  | ||||||
|     // Take responses from D and transform them |  | ||||||
|     val d_d = Wire(in.d) |  | ||||||
|     d_d <> out.d |  | ||||||
|     d_d.bits.source := out.d.bits.source >> 1 |  | ||||||
|  |  | ||||||
|     when (out.d.bits.opcode === AccessAckData && out.d.bits.source(0)) { |  | ||||||
|       d_d.bits.opcode := GrantData |  | ||||||
|       d_d.bits.param  := TLPermissions.toT |  | ||||||
|     } |  | ||||||
|     when (out.d.bits.opcode === AccessAck && !out.d.bits.source(0)) { |  | ||||||
|       d_d.bits.opcode := ReleaseAck |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // Combine the sources of messages into the channels |  | ||||||
|     TLArbiter(TLArbiter.lowestIndexFirst)(out.a, (edgeOut.numBeats1(c_a.bits), c_a), (edgeOut.numBeats1(a_a.bits), a_a)) |  | ||||||
|     TLArbiter(TLArbiter.lowestIndexFirst)(in.d,  (edgeIn .numBeats1(d_d.bits), d_d), (UInt(0), Queue(c_d, 2))) |  | ||||||
|  |  | ||||||
|     // Tie off unused ports |  | ||||||
|     in.b.valid := Bool(false) |  | ||||||
|     out.c.valid := Bool(false) |  | ||||||
|     out.e.valid := Bool(false) |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -11,8 +11,8 @@ import scala.math.{min,max} | |||||||
| class TLFilter(select: AddressSet)(implicit p: Parameters) extends LazyModule | class TLFilter(select: AddressSet)(implicit p: Parameters) extends LazyModule | ||||||
| { | { | ||||||
|   val node = TLAdapterNode( |   val node = TLAdapterNode( | ||||||
|     clientFn  = { case Seq(cp) => cp }, |     clientFn  = { cp => cp }, | ||||||
|     managerFn = { case Seq(mp) => |     managerFn = { mp => | ||||||
|       mp.copy(managers = mp.managers.map { m => |       mp.copy(managers = mp.managers.map { m => | ||||||
|         val filtered = m.address.map(_.intersect(select)).flatten |         val filtered = m.address.map(_.intersect(select)).flatten | ||||||
|         val alignment = select.alignment /* alignment 0 means 'select' selected everything */ |         val alignment = select.alignment /* alignment 0 means 'select' selected everything */ | ||||||
|   | |||||||
| @@ -41,8 +41,8 @@ class TLFragmenter(val minSize: Int, val maxSize: Int, val alwaysMin: Boolean = | |||||||
|     sourceId = IdRange(c.sourceId.start << fragmentBits, c.sourceId.end << fragmentBits)) |     sourceId = IdRange(c.sourceId.start << fragmentBits, c.sourceId.end << fragmentBits)) | ||||||
|  |  | ||||||
|   val node = TLAdapterNode( |   val node = TLAdapterNode( | ||||||
|     clientFn  = { case Seq(c) => c.copy(clients = c.clients.map(mapClient)) }, |     clientFn  = { c => c.copy(clients = c.clients.map(mapClient)) }, | ||||||
|     managerFn = { case Seq(m) => m.copy(managers = m.managers.map(mapManager)) }) |     managerFn = { m => m.copy(managers = m.managers.map(mapManager)) }) | ||||||
|  |  | ||||||
|   lazy val module = new LazyModuleImp(this) { |   lazy val module = new LazyModuleImp(this) { | ||||||
|     val io = new Bundle { |     val io = new Bundle { | ||||||
| @@ -50,204 +50,201 @@ class TLFragmenter(val minSize: Int, val maxSize: Int, val alwaysMin: Boolean = | |||||||
|       val out = node.bundleOut |       val out = node.bundleOut | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // All managers must share a common FIFO domain (responses might end up interleaved) |     ((io.in zip io.out) zip (node.edgesIn zip node.edgesOut)) foreach { case ((in, out), (edgeIn, edgeOut)) => | ||||||
|     val edgeOut   = node.edgesOut(0) |       // All managers must share a common FIFO domain (responses might end up interleaved) | ||||||
|     val edgeIn    = node.edgesIn(0) |       val manager   = edgeOut.manager | ||||||
|     val manager   = edgeOut.manager |       val managers  = manager.managers | ||||||
|     val managers  = manager.managers |       val beatBytes = manager.beatBytes | ||||||
|     val beatBytes = manager.beatBytes |       val fifoId = managers(0).fifoId | ||||||
|     val fifoId = managers(0).fifoId |       require (fifoId.isDefined && managers.map(_.fifoId == fifoId).reduce(_ && _)) | ||||||
|     require (fifoId.isDefined && managers.map(_.fifoId == fifoId).reduce(_ && _)) |  | ||||||
|  |  | ||||||
|     // We don't support fragmenting to sub-beat accesses |       // We don't support fragmenting to sub-beat accesses | ||||||
|     require (minSize >= beatBytes) |       require (minSize >= beatBytes) | ||||||
|     // We can't support devices which are cached on both sides of us |       // We can't support devices which are cached on both sides of us | ||||||
|     require (!edgeOut.manager.anySupportAcquireB || !edgeIn.client.anySupportProbe) |       require (!edgeOut.manager.anySupportAcquireB || !edgeIn.client.anySupportProbe) | ||||||
|  |  | ||||||
|     /* The Fragmenter is a bit tricky, because there are 5 sizes in play: |       /* The Fragmenter is a bit tricky, because there are 5 sizes in play: | ||||||
|      *   max  size -- the maximum transfer size possible |        *   max  size -- the maximum transfer size possible | ||||||
|      *   orig size -- the original pre-fragmenter size |        *   orig size -- the original pre-fragmenter size | ||||||
|      *   frag size -- the modified post-fragmenter size |        *   frag size -- the modified post-fragmenter size | ||||||
|      *   min  size -- the threshold below which frag=orig |        *   min  size -- the threshold below which frag=orig | ||||||
|      *   beat size -- the amount transfered on any given beat |        *   beat size -- the amount transfered on any given beat | ||||||
|      * |        * | ||||||
|      * The relationships are as follows: |        * The relationships are as follows: | ||||||
|      *   max >= orig >= frag |        *   max >= orig >= frag | ||||||
|      *   max >  min  >= beat |        *   max >  min  >= beat | ||||||
|      * It IS possible that orig <= min (then frag=orig; ie: no fragmentation) |        * It IS possible that orig <= min (then frag=orig; ie: no fragmentation) | ||||||
|      * |        * | ||||||
|      * The fragment# (sent via TL.source) is measured in multiples of min size. |        * The fragment# (sent via TL.source) is measured in multiples of min size. | ||||||
|      * Meanwhile, to track the progress, counters measure in multiples of beat size. |        * Meanwhile, to track the progress, counters measure in multiples of beat size. | ||||||
|      * |        * | ||||||
|      * Here is an example of a bus with max=256, min=8, beat=4 and a device supporting 16. |        * Here is an example of a bus with max=256, min=8, beat=4 and a device supporting 16. | ||||||
|      * |        * | ||||||
|      * in.A    out.A (frag#)  out.D (frag#)  in.D     gen# ack# |        * in.A    out.A (frag#)  out.D (frag#)  in.D     gen# ack# | ||||||
|      * get64   get16  6       ackD16  6      ackD64    12   15 |        * get64   get16  6       ackD16  6      ackD64    12   15 | ||||||
|      *                        ackD16  6      ackD64         14 |        *                        ackD16  6      ackD64         14 | ||||||
|      *                        ackD16  6      ackD64         13 |        *                        ackD16  6      ackD64         13 | ||||||
|      *                        ackD16  6      ackD64         12 |        *                        ackD16  6      ackD64         12 | ||||||
|      *         get16  4       ackD16  4      ackD64    8    11 |        *         get16  4       ackD16  4      ackD64    8    11 | ||||||
|      *                        ackD16  4      ackD64         10 |        *                        ackD16  4      ackD64         10 | ||||||
|      *                        ackD16  4      ackD64         9 |        *                        ackD16  4      ackD64         9 | ||||||
|      *                        ackD16  4      ackD64         8 |        *                        ackD16  4      ackD64         8 | ||||||
|      *         get16  2       ackD16  2      ackD64    4    7 |        *         get16  2       ackD16  2      ackD64    4    7 | ||||||
|      *                        ackD16  2      ackD64         6 |        *                        ackD16  2      ackD64         6 | ||||||
|      *                        ackD16  2      ackD64         5 |        *                        ackD16  2      ackD64         5 | ||||||
|      *                        ackD16  2      ackD64         4 |        *                        ackD16  2      ackD64         4 | ||||||
|      *         get16  0       ackD16  0      ackD64    0    3 |        *         get16  0       ackD16  0      ackD64    0    3 | ||||||
|      *                        ackD16  0      ackD64         2 |        *                        ackD16  0      ackD64         2 | ||||||
|      *                        ackD16  0      ackD64         1 |        *                        ackD16  0      ackD64         1 | ||||||
|      *                        ackD16  0      ackD64         0 |        *                        ackD16  0      ackD64         0 | ||||||
|      * |        * | ||||||
|      * get8    get8   0       ackD8   0      ackD8     0    1 |        * get8    get8   0       ackD8   0      ackD8     0    1 | ||||||
|      *                        ackD8   0      ackD8          0 |        *                        ackD8   0      ackD8          0 | ||||||
|      * |        * | ||||||
|      * get4    get4   0       ackD4   0      ackD4     0    0 |        * get4    get4   0       ackD4   0      ackD4     0    0 | ||||||
|      * get1    get1   0       ackD1   0      ackD1     0    0 |        * get1    get1   0       ackD1   0      ackD1     0    0 | ||||||
|      * |        * | ||||||
|      * put64   put16  6                                15    |        * put64   put16  6                                15    | ||||||
|      * put64   put16  6                                14 |        * put64   put16  6                                14 | ||||||
|      * put64   put16  6                                13 |        * put64   put16  6                                13 | ||||||
|      * put64   put16  6       ack16   6                12    12 |        * put64   put16  6       ack16   6                12    12 | ||||||
|      * put64   put16  4                                11 |        * put64   put16  4                                11 | ||||||
|      * put64   put16  4                                10 |        * put64   put16  4                                10 | ||||||
|      * put64   put16  4                                9 |        * put64   put16  4                                9 | ||||||
|      * put64   put16  4       ack16   4                8     8 |        * put64   put16  4       ack16   4                8     8 | ||||||
|      * put64   put16  2                                7 |        * put64   put16  2                                7 | ||||||
|      * put64   put16  2                                6 |        * put64   put16  2                                6 | ||||||
|      * put64   put16  2                                5 |        * put64   put16  2                                5 | ||||||
|      * put64   put16  2       ack16   2                4     4 |        * put64   put16  2       ack16   2                4     4 | ||||||
|      * put64   put16  0                                3 |        * put64   put16  0                                3 | ||||||
|      * put64   put16  0                                2 |        * put64   put16  0                                2 | ||||||
|      * put64   put16  0                                1 |        * put64   put16  0                                1 | ||||||
|      * put64   put16  0       ack16   0      ack64     0     0 |        * put64   put16  0       ack16   0      ack64     0     0 | ||||||
|      * |        * | ||||||
|      * put8    put8   0                                1 |        * put8    put8   0                                1 | ||||||
|      * put8    put8   0       ack8    0      ack8      0     0 |        * put8    put8   0       ack8    0      ack8      0     0 | ||||||
|      * |        * | ||||||
|      * put4    put4   0       ack4    0      ack4      0     0 |        * put4    put4   0       ack4    0      ack4      0     0 | ||||||
|      * put1    put1   0       ack1    0      ack1      0     0 |        * put1    put1   0       ack1    0      ack1      0     0 | ||||||
|      */ |        */ | ||||||
|  |  | ||||||
|     val in = io.in(0) |       val counterBits = log2Up(maxSize/beatBytes) | ||||||
|     val out = io.out(0) |       val maxDownSize = if (alwaysMin) minSize else manager.maxTransfer | ||||||
|  |  | ||||||
|     val counterBits = log2Up(maxSize/beatBytes) |       // First, handle the return path | ||||||
|     val maxDownSize = if (alwaysMin) minSize else manager.maxTransfer |       val acknum = RegInit(UInt(0, width = counterBits)) | ||||||
|  |       val dOrig = Reg(UInt()) | ||||||
|  |       val dFragnum = out.d.bits.source(fragmentBits-1, 0) | ||||||
|  |       val dFirst = acknum === UInt(0) | ||||||
|  |       val dsizeOH  = UIntToOH (out.d.bits.size, log2Ceil(maxDownSize)+1) | ||||||
|  |       val dsizeOH1 = UIntToOH1(out.d.bits.size, log2Up(maxDownSize)) | ||||||
|  |       val dHasData = edgeOut.hasData(out.d.bits) | ||||||
|  |  | ||||||
|     // First, handle the return path |       // calculate new acknum | ||||||
|     val acknum = RegInit(UInt(0, width = counterBits)) |       val acknum_fragment = dFragnum << log2Ceil(minSize/beatBytes) | ||||||
|     val dOrig = Reg(UInt()) |       val acknum_size = dsizeOH1 >> log2Ceil(beatBytes) | ||||||
|     val dFragnum = out.d.bits.source(fragmentBits-1, 0) |       assert (!out.d.valid || (acknum_fragment & acknum_size) === UInt(0)) | ||||||
|     val dFirst = acknum === UInt(0) |       val dFirst_acknum = acknum_fragment | Mux(dHasData, acknum_size, UInt(0)) | ||||||
|     val dsizeOH  = UIntToOH (out.d.bits.size, log2Ceil(maxDownSize)+1) |       val ack_decrement = Mux(dHasData, UInt(1), dsizeOH >> log2Ceil(beatBytes)) | ||||||
|     val dsizeOH1 = UIntToOH1(out.d.bits.size, log2Up(maxDownSize)) |       // calculate the original size | ||||||
|     val dHasData = edgeOut.hasData(out.d.bits) |       val dFirst_size = OH1ToUInt((dFragnum << log2Ceil(minSize)) | dsizeOH1) | ||||||
|  |  | ||||||
|     // calculate new acknum |       when (out.d.fire()) { | ||||||
|     val acknum_fragment = dFragnum << log2Ceil(minSize/beatBytes) |         acknum := Mux(dFirst, dFirst_acknum, acknum - ack_decrement) | ||||||
|     val acknum_size = dsizeOH1 >> log2Ceil(beatBytes) |         when (dFirst) { dOrig := dFirst_size } | ||||||
|     assert (!out.d.valid || (acknum_fragment & acknum_size) === UInt(0)) |       } | ||||||
|     val dFirst_acknum = acknum_fragment | Mux(dHasData, acknum_size, UInt(0)) |  | ||||||
|     val ack_decrement = Mux(dHasData, UInt(1), dsizeOH >> log2Ceil(beatBytes)) |  | ||||||
|     // calculate the original size |  | ||||||
|     val dFirst_size = OH1ToUInt((dFragnum << log2Ceil(minSize)) | dsizeOH1) |  | ||||||
|  |  | ||||||
|     when (out.d.fire()) { |       // Swallow up non-data ack fragments | ||||||
|       acknum := Mux(dFirst, dFirst_acknum, acknum - ack_decrement) |       val drop = !dHasData && (dFragnum =/= UInt(0)) | ||||||
|       when (dFirst) { dOrig := dFirst_size } |       out.d.ready := in.d.ready || drop | ||||||
|  |       in.d.valid  := out.d.valid && !drop | ||||||
|  |       in.d.bits   := out.d.bits // pass most stuff unchanged | ||||||
|  |       in.d.bits.addr_lo := out.d.bits.addr_lo & ~dsizeOH1 | ||||||
|  |       in.d.bits.source := out.d.bits.source >> fragmentBits | ||||||
|  |       in.d.bits.size   := Mux(dFirst, dFirst_size, dOrig) | ||||||
|  |  | ||||||
|  |       // Combine the error flag | ||||||
|  |       val r_error = RegInit(Bool(false)) | ||||||
|  |       val d_error = r_error | out.d.bits.error | ||||||
|  |       when (out.d.fire()) { r_error := Mux(drop, d_error, UInt(0)) } | ||||||
|  |       in.d.bits.error := d_error | ||||||
|  |  | ||||||
|  |       // What maximum transfer sizes do downstream devices support? | ||||||
|  |       val maxArithmetics = managers.map(_.supportsArithmetic.max) | ||||||
|  |       val maxLogicals    = managers.map(_.supportsLogical.max) | ||||||
|  |       val maxGets        = managers.map(_.supportsGet.max) | ||||||
|  |       val maxPutFulls    = managers.map(_.supportsPutFull.max) | ||||||
|  |       val maxPutPartials = managers.map(_.supportsPutPartial.max) | ||||||
|  |       val maxHints       = managers.map(m => if (m.supportsHint) maxDownSize else 0) | ||||||
|  |  | ||||||
|  |       // We assume that the request is valid => size 0 is impossible | ||||||
|  |       val lgMinSize = UInt(log2Ceil(minSize)) | ||||||
|  |       val maxLgArithmetics = maxArithmetics.map(m => if (m == 0) lgMinSize else UInt(log2Ceil(m))) | ||||||
|  |       val maxLgLogicals    = maxLogicals   .map(m => if (m == 0) lgMinSize else UInt(log2Ceil(m))) | ||||||
|  |       val maxLgGets        = maxGets       .map(m => if (m == 0) lgMinSize else UInt(log2Ceil(m))) | ||||||
|  |       val maxLgPutFulls    = maxPutFulls   .map(m => if (m == 0) lgMinSize else UInt(log2Ceil(m))) | ||||||
|  |       val maxLgPutPartials = maxPutPartials.map(m => if (m == 0) lgMinSize else UInt(log2Ceil(m))) | ||||||
|  |       val maxLgHints       = maxHints      .map(m => if (m == 0) lgMinSize else UInt(log2Ceil(m))) | ||||||
|  |  | ||||||
|  |       // Make the request repeatable | ||||||
|  |       val repeater = Module(new Repeater(in.a.bits)) | ||||||
|  |       repeater.io.enq <> in.a | ||||||
|  |       val in_a = repeater.io.deq | ||||||
|  |  | ||||||
|  |       // If this is infront of a single manager, these become constants | ||||||
|  |       val find = manager.findFast(edgeIn.address(in_a.bits)) | ||||||
|  |       val maxLgArithmetic  = Mux1H(find, maxLgArithmetics) | ||||||
|  |       val maxLgLogical     = Mux1H(find, maxLgLogicals) | ||||||
|  |       val maxLgGet         = Mux1H(find, maxLgGets) | ||||||
|  |       val maxLgPutFull     = Mux1H(find, maxLgPutFulls) | ||||||
|  |       val maxLgPutPartial  = Mux1H(find, maxLgPutPartials) | ||||||
|  |       val maxLgHint        = Mux1H(find, maxLgHints) | ||||||
|  |  | ||||||
|  |       val limit = if (alwaysMin) lgMinSize else  | ||||||
|  |         MuxLookup(in_a.bits.opcode, lgMinSize, Array( | ||||||
|  |           TLMessages.PutFullData    -> maxLgPutFull, | ||||||
|  |           TLMessages.PutPartialData -> maxLgPutPartial, | ||||||
|  |           TLMessages.ArithmeticData -> maxLgArithmetic, | ||||||
|  |           TLMessages.LogicalData    -> maxLgLogical, | ||||||
|  |           TLMessages.Get            -> maxLgGet, | ||||||
|  |           TLMessages.Hint           -> maxLgHint)) | ||||||
|  |  | ||||||
|  |       val aOrig = in_a.bits.size | ||||||
|  |       val aFrag = Mux(aOrig > limit, limit, aOrig) | ||||||
|  |       val aOrigOH1 = UIntToOH1(aOrig, log2Ceil(maxSize)) | ||||||
|  |       val aFragOH1 = UIntToOH1(aFrag, log2Up(maxDownSize)) | ||||||
|  |       val aHasData = node.edgesIn(0).hasData(in_a.bits) | ||||||
|  |       val aMask = Mux(aHasData, UInt(0), aFragOH1) | ||||||
|  |  | ||||||
|  |       val gennum = RegInit(UInt(0, width = counterBits)) | ||||||
|  |       val aFirst = gennum === UInt(0) | ||||||
|  |       val old_gennum1 = Mux(aFirst, aOrigOH1 >> log2Ceil(beatBytes), gennum - UInt(1)) | ||||||
|  |       val new_gennum = ~(~old_gennum1 | (aMask >> log2Ceil(beatBytes))) // ~(~x|y) is width safe | ||||||
|  |       val aFragnum = ~(~(old_gennum1 >> log2Ceil(minSize/beatBytes)) | (aFragOH1 >> log2Ceil(minSize))) | ||||||
|  |  | ||||||
|  |       when (out.a.fire()) { gennum := new_gennum } | ||||||
|  |  | ||||||
|  |       repeater.io.repeat := !aHasData && aFragnum =/= UInt(0) | ||||||
|  |       out.a <> in_a | ||||||
|  |       out.a.bits.address := in_a.bits.address | (~aFragnum << log2Ceil(minSize) & aOrigOH1) | ||||||
|  |       out.a.bits.source := Cat(in_a.bits.source, aFragnum) | ||||||
|  |       out.a.bits.size := aFrag | ||||||
|  |  | ||||||
|  |       // Optimize away some of the Repeater's registers | ||||||
|  |       assert (!repeater.io.full || !aHasData) | ||||||
|  |       out.a.bits.data := in.a.bits.data | ||||||
|  |       val fullMask = UInt((BigInt(1) << beatBytes) - 1) | ||||||
|  |       assert (!repeater.io.full || in_a.bits.mask === fullMask) | ||||||
|  |       out.a.bits.mask := Mux(repeater.io.full, fullMask, in.a.bits.mask) | ||||||
|  |  | ||||||
|  |       // Tie off unused channels | ||||||
|  |       in.b.valid := Bool(false) | ||||||
|  |       in.c.ready := Bool(true) | ||||||
|  |       in.e.ready := Bool(true) | ||||||
|  |       out.b.ready := Bool(true) | ||||||
|  |       out.c.valid := Bool(false) | ||||||
|  |       out.e.valid := Bool(false) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // Swallow up non-data ack fragments |  | ||||||
|     val drop = !dHasData && (dFragnum =/= UInt(0)) |  | ||||||
|     out.d.ready := in.d.ready || drop |  | ||||||
|     in.d.valid  := out.d.valid && !drop |  | ||||||
|     in.d.bits   := out.d.bits // pass most stuff unchanged |  | ||||||
|     in.d.bits.addr_lo := out.d.bits.addr_lo & ~dsizeOH1 |  | ||||||
|     in.d.bits.source := out.d.bits.source >> fragmentBits |  | ||||||
|     in.d.bits.size   := Mux(dFirst, dFirst_size, dOrig) |  | ||||||
|  |  | ||||||
|     // Combine the error flag |  | ||||||
|     val r_error = RegInit(Bool(false)) |  | ||||||
|     val d_error = r_error | out.d.bits.error |  | ||||||
|     when (out.d.fire()) { r_error := Mux(drop, d_error, UInt(0)) } |  | ||||||
|     in.d.bits.error := d_error |  | ||||||
|  |  | ||||||
|     // What maximum transfer sizes do downstream devices support? |  | ||||||
|     val maxArithmetics = managers.map(_.supportsArithmetic.max) |  | ||||||
|     val maxLogicals    = managers.map(_.supportsLogical.max) |  | ||||||
|     val maxGets        = managers.map(_.supportsGet.max) |  | ||||||
|     val maxPutFulls    = managers.map(_.supportsPutFull.max) |  | ||||||
|     val maxPutPartials = managers.map(_.supportsPutPartial.max) |  | ||||||
|     val maxHints       = managers.map(m => if (m.supportsHint) maxDownSize else 0) |  | ||||||
|  |  | ||||||
|     // We assume that the request is valid => size 0 is impossible |  | ||||||
|     val lgMinSize = UInt(log2Ceil(minSize)) |  | ||||||
|     val maxLgArithmetics = maxArithmetics.map(m => if (m == 0) lgMinSize else UInt(log2Ceil(m))) |  | ||||||
|     val maxLgLogicals    = maxLogicals   .map(m => if (m == 0) lgMinSize else UInt(log2Ceil(m))) |  | ||||||
|     val maxLgGets        = maxGets       .map(m => if (m == 0) lgMinSize else UInt(log2Ceil(m))) |  | ||||||
|     val maxLgPutFulls    = maxPutFulls   .map(m => if (m == 0) lgMinSize else UInt(log2Ceil(m))) |  | ||||||
|     val maxLgPutPartials = maxPutPartials.map(m => if (m == 0) lgMinSize else UInt(log2Ceil(m))) |  | ||||||
|     val maxLgHints       = maxHints      .map(m => if (m == 0) lgMinSize else UInt(log2Ceil(m))) |  | ||||||
|  |  | ||||||
|     // Make the request repeatable |  | ||||||
|     val repeater = Module(new Repeater(in.a.bits)) |  | ||||||
|     repeater.io.enq <> in.a |  | ||||||
|     val in_a = repeater.io.deq |  | ||||||
|  |  | ||||||
|     // If this is infront of a single manager, these become constants |  | ||||||
|     val find = manager.findFast(edgeIn.address(in_a.bits)) |  | ||||||
|     val maxLgArithmetic  = Mux1H(find, maxLgArithmetics) |  | ||||||
|     val maxLgLogical     = Mux1H(find, maxLgLogicals) |  | ||||||
|     val maxLgGet         = Mux1H(find, maxLgGets) |  | ||||||
|     val maxLgPutFull     = Mux1H(find, maxLgPutFulls) |  | ||||||
|     val maxLgPutPartial  = Mux1H(find, maxLgPutPartials) |  | ||||||
|     val maxLgHint        = Mux1H(find, maxLgHints) |  | ||||||
|  |  | ||||||
|     val limit = if (alwaysMin) lgMinSize else  |  | ||||||
|       MuxLookup(in_a.bits.opcode, lgMinSize, Array( |  | ||||||
|         TLMessages.PutFullData    -> maxLgPutFull, |  | ||||||
|         TLMessages.PutPartialData -> maxLgPutPartial, |  | ||||||
|         TLMessages.ArithmeticData -> maxLgArithmetic, |  | ||||||
|         TLMessages.LogicalData    -> maxLgLogical, |  | ||||||
|         TLMessages.Get            -> maxLgGet, |  | ||||||
|         TLMessages.Hint           -> maxLgHint)) |  | ||||||
|  |  | ||||||
|     val aOrig = in_a.bits.size |  | ||||||
|     val aFrag = Mux(aOrig > limit, limit, aOrig) |  | ||||||
|     val aOrigOH1 = UIntToOH1(aOrig, log2Ceil(maxSize)) |  | ||||||
|     val aFragOH1 = UIntToOH1(aFrag, log2Up(maxDownSize)) |  | ||||||
|     val aHasData = node.edgesIn(0).hasData(in_a.bits) |  | ||||||
|     val aMask = Mux(aHasData, UInt(0), aFragOH1) |  | ||||||
|  |  | ||||||
|     val gennum = RegInit(UInt(0, width = counterBits)) |  | ||||||
|     val aFirst = gennum === UInt(0) |  | ||||||
|     val old_gennum1 = Mux(aFirst, aOrigOH1 >> log2Ceil(beatBytes), gennum - UInt(1)) |  | ||||||
|     val new_gennum = ~(~old_gennum1 | (aMask >> log2Ceil(beatBytes))) // ~(~x|y) is width safe |  | ||||||
|     val aFragnum = ~(~(old_gennum1 >> log2Ceil(minSize/beatBytes)) | (aFragOH1 >> log2Ceil(minSize))) |  | ||||||
|  |  | ||||||
|     when (out.a.fire()) { gennum := new_gennum } |  | ||||||
|  |  | ||||||
|     repeater.io.repeat := !aHasData && aFragnum =/= UInt(0) |  | ||||||
|     out.a <> in_a |  | ||||||
|     out.a.bits.address := in_a.bits.address | (~aFragnum << log2Ceil(minSize) & aOrigOH1) |  | ||||||
|     out.a.bits.source := Cat(in_a.bits.source, aFragnum) |  | ||||||
|     out.a.bits.size := aFrag |  | ||||||
|  |  | ||||||
|     // Optimize away some of the Repeater's registers |  | ||||||
|     assert (!repeater.io.full || !aHasData) |  | ||||||
|     out.a.bits.data := in.a.bits.data |  | ||||||
|     val fullMask = UInt((BigInt(1) << beatBytes) - 1) |  | ||||||
|     assert (!repeater.io.full || in_a.bits.mask === fullMask) |  | ||||||
|     out.a.bits.mask := Mux(repeater.io.full, fullMask, in.a.bits.mask) |  | ||||||
|  |  | ||||||
|     // Tie off unused channels |  | ||||||
|     in.b.valid := Bool(false) |  | ||||||
|     in.c.ready := Bool(true) |  | ||||||
|     in.e.ready := Bool(true) |  | ||||||
|     out.b.ready := Bool(true) |  | ||||||
|     out.c.valid := Bool(false) |  | ||||||
|     out.e.valid := Bool(false) |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -12,8 +12,8 @@ import diplomacy._ | |||||||
| class TLHintHandler(supportManagers: Boolean = true, supportClients: Boolean = false, passthrough: Boolean = true)(implicit p: Parameters) extends LazyModule | class TLHintHandler(supportManagers: Boolean = true, supportClients: Boolean = false, passthrough: Boolean = true)(implicit p: Parameters) extends LazyModule | ||||||
| { | { | ||||||
|   val node = TLAdapterNode( |   val node = TLAdapterNode( | ||||||
|     clientFn  = { case Seq(c) => if (!supportClients)  c else c.copy(minLatency = min(1, c.minLatency), clients  = c.clients .map(_.copy(supportsHint = TransferSizes(1, c.maxTransfer)))) }, |     clientFn  = { c => if (!supportClients)  c else c.copy(minLatency = min(1, c.minLatency), clients  = c.clients .map(_.copy(supportsHint = TransferSizes(1, c.maxTransfer)))) }, | ||||||
|     managerFn = { case Seq(m) => if (!supportManagers) m else m.copy(minLatency = min(1, m.minLatency), managers = m.managers.map(_.copy(supportsHint = TransferSizes(1, m.maxTransfer)))) }) |     managerFn = { m => if (!supportManagers) m else m.copy(minLatency = min(1, m.minLatency), managers = m.managers.map(_.copy(supportsHint = TransferSizes(1, m.maxTransfer)))) }) | ||||||
|  |  | ||||||
|   lazy val module = new LazyModuleImp(this) { |   lazy val module = new LazyModuleImp(this) { | ||||||
|     val io = new Bundle { |     val io = new Bundle { | ||||||
| @@ -21,79 +21,76 @@ class TLHintHandler(supportManagers: Boolean = true, supportClients: Boolean = f | |||||||
|       val out = node.bundleOut |       val out = node.bundleOut | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     val in  = io.in(0) |     ((io.in zip io.out) zip (node.edgesIn zip node.edgesOut)) foreach { case ((in, out), (edgeIn, edgeOut)) => | ||||||
|     val out = io.out(0) |       // Don't add support for clients if there is no BCE channel | ||||||
|     val edgeIn  = node.edgesIn(0) |       val bce = edgeOut.manager.anySupportAcquireB && edgeIn.client.anySupportProbe | ||||||
|     val edgeOut = node.edgesOut(0) |       require (!supportClients || bce) | ||||||
|  |  | ||||||
|     // Don't add support for clients if there is no BCE channel |       // Does it even make sense to add the HintHandler? | ||||||
|     val bce = edgeOut.manager.anySupportAcquireB && edgeIn.client.anySupportProbe |       val smartClients = edgeIn.client.clients.map(_.supportsHint.max == edgeIn.client.maxTransfer).reduce(_&&_) | ||||||
|     require (!supportClients || bce) |       val smartManagers = edgeOut.manager.managers.map(_.supportsHint.max == edgeOut.manager.maxTransfer).reduce(_&&_) | ||||||
|  |  | ||||||
|     // Does it even make sense to add the HintHandler? |       if (supportManagers && !(passthrough && smartManagers)) { | ||||||
|     val smartClients = edgeIn.client.clients.map(_.supportsHint.max == edgeIn.client.maxTransfer).reduce(_&&_) |         val address = edgeIn.address(in.a.bits) | ||||||
|     val smartManagers = edgeOut.manager.managers.map(_.supportsHint.max == edgeOut.manager.maxTransfer).reduce(_&&_) |         val handleA = if (passthrough) !edgeOut.manager.supportsHintFast(address, edgeIn.size(in.a.bits)) else Bool(true) | ||||||
|  |         val hintBitsAtA = handleA && in.a.bits.opcode === TLMessages.Hint | ||||||
|  |         val hint = Wire(out.d) | ||||||
|  |  | ||||||
|     if (supportManagers && !(passthrough && smartManagers)) { |         hint.valid  := in.a.valid &&  hintBitsAtA | ||||||
|       val address = edgeIn.address(in.a.bits) |         out.a.valid := in.a.valid && !hintBitsAtA | ||||||
|       val handleA = if (passthrough) !edgeOut.manager.supportsHintFast(address, edgeIn.size(in.a.bits)) else Bool(true) |         in.a.ready := Mux(hintBitsAtA, hint.ready, out.a.ready) | ||||||
|       val hintBitsAtA = handleA && in.a.bits.opcode === TLMessages.Hint |  | ||||||
|       val hint = Wire(out.d) |  | ||||||
|  |  | ||||||
|       hint.valid  := in.a.valid &&  hintBitsAtA |         hint.bits := edgeIn.HintAck(in.a.bits, UInt(0)) | ||||||
|       out.a.valid := in.a.valid && !hintBitsAtA |         out.a.bits := in.a.bits | ||||||
|       in.a.ready := Mux(hintBitsAtA, hint.ready, out.a.ready) |  | ||||||
|  |  | ||||||
|       hint.bits := edgeIn.HintAck(in.a.bits, UInt(0)) |         TLArbiter(TLArbiter.lowestIndexFirst)(in.d, (edgeOut.numBeats1(out.d.bits), out.d), (UInt(0), Queue(hint, 1))) | ||||||
|       out.a.bits := in.a.bits |       } else { | ||||||
|  |         out.a.valid := in.a.valid | ||||||
|  |         in.a.ready := out.a.ready | ||||||
|  |         out.a.bits := in.a.bits | ||||||
|  |  | ||||||
|       TLArbiter(TLArbiter.lowestIndexFirst)(in.d, (edgeOut.numBeats1(out.d.bits), out.d), (UInt(0), Queue(hint, 1))) |         in.d.valid := out.d.valid | ||||||
|     } else { |         out.d.ready := in.d.ready | ||||||
|       out.a.valid := in.a.valid |         in.d.bits := out.d.bits | ||||||
|       in.a.ready := out.a.ready |       } | ||||||
|       out.a.bits := in.a.bits |  | ||||||
|  |  | ||||||
|       in.d.valid := out.d.valid |       if (supportClients && !(passthrough && smartClients)) { | ||||||
|       out.d.ready := in.d.ready |         val handleB = if (passthrough) !edgeIn.client.supportsHint(out.b.bits.source, edgeOut.size(out.b.bits)) else Bool(true) | ||||||
|       in.d.bits := out.d.bits |         val hintBitsAtB = handleB && out.b.bits.opcode === TLMessages.Hint | ||||||
|     } |         val hint = Wire(in.c) | ||||||
|  |  | ||||||
|     if (supportClients && !(passthrough && smartClients)) { |         hint.valid := out.b.valid &&  hintBitsAtB | ||||||
|       val handleB = if (passthrough) !edgeIn.client.supportsHint(out.b.bits.source, edgeOut.size(out.b.bits)) else Bool(true) |         in.b.valid := out.b.valid && !hintBitsAtB | ||||||
|       val hintBitsAtB = handleB && out.b.bits.opcode === TLMessages.Hint |         out.b.ready := Mux(hintBitsAtB, hint.ready, in.b.ready) | ||||||
|       val hint = Wire(in.c) |  | ||||||
|  |  | ||||||
|       hint.valid := out.b.valid &&  hintBitsAtB |         hint.bits := edgeOut.HintAck(out.b.bits) | ||||||
|       in.b.valid := out.b.valid && !hintBitsAtB |         in.b.bits := out.b.bits | ||||||
|       out.b.ready := Mux(hintBitsAtB, hint.ready, in.b.ready) |  | ||||||
|  |  | ||||||
|       hint.bits := edgeOut.HintAck(out.b.bits) |         TLArbiter(TLArbiter.lowestIndexFirst)(out.c, (edgeIn.numBeats1(in.c.bits), in.c), (UInt(0), Queue(hint, 1))) | ||||||
|       in.b.bits := out.b.bits |       } else if (bce) { | ||||||
|  |         in.b.valid := out.b.valid | ||||||
|  |         out.b.ready := in.b.ready | ||||||
|  |         in.b.bits := out.b.bits | ||||||
|  |  | ||||||
|       TLArbiter(TLArbiter.lowestIndexFirst)(out.c, (edgeIn.numBeats1(in.c.bits), in.c), (UInt(0), Queue(hint, 1))) |         out.c.valid := in.c.valid | ||||||
|     } else if (bce) { |         in.c.ready := out.c.ready | ||||||
|       in.b.valid := out.b.valid |         out.c.bits := in.c.bits | ||||||
|       out.b.ready := in.b.ready |       } else { | ||||||
|       in.b.bits := out.b.bits |         in.b.valid := Bool(false) | ||||||
|  |         in.c.ready := Bool(true) | ||||||
|  |         out.b.ready := Bool(true) | ||||||
|  |         out.c.valid := Bool(false) | ||||||
|  |       } | ||||||
|  |  | ||||||
|       out.c.valid := in.c.valid |       if (bce) { | ||||||
|       in.c.ready := out.c.ready |         // Pass E through unchanged | ||||||
|       out.c.bits := in.c.bits |         out.e.valid := in.e.valid | ||||||
|     } else { |         in.e.ready := out.e.ready | ||||||
|       in.b.valid := Bool(false) |         out.e.bits := in.e.bits | ||||||
|       in.c.ready := Bool(true) |       } else { | ||||||
|       out.b.ready := Bool(true) |         in.e.ready := Bool(true) | ||||||
|       out.c.valid := Bool(false) |         out.e.valid := Bool(false) | ||||||
|     } |       } | ||||||
|  |  | ||||||
|     if (bce) { |  | ||||||
|       // Pass E through unchanged |  | ||||||
|       out.e.valid := in.e.valid |  | ||||||
|       in.e.ready := out.e.ready |  | ||||||
|       out.e.bits := in.e.bits |  | ||||||
|     } else { |  | ||||||
|       in.e.ready := Bool(true) |  | ||||||
|       out.e.valid := Bool(false) |  | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -81,16 +81,16 @@ object IntImp extends NodeImp[IntSourcePortParameters, IntSinkPortParameters, In | |||||||
|  |  | ||||||
| case class IntIdentityNode() extends IdentityNode(IntImp) | case class IntIdentityNode() extends IdentityNode(IntImp) | ||||||
| case class IntSourceNode(num: Int) extends SourceNode(IntImp)( | case class IntSourceNode(num: Int) extends SourceNode(IntImp)( | ||||||
|   IntSourcePortParameters(Seq(IntSourceParameters(num))), (if (num == 0) 0 else 1) to 1) |   if (num == 0) Seq() else Seq(IntSourcePortParameters(Seq(IntSourceParameters(num))))) | ||||||
| case class IntSinkNode() extends SinkNode(IntImp)( | case class IntSinkNode() extends SinkNode(IntImp)( | ||||||
|   IntSinkPortParameters(Seq(IntSinkParameters()))) |   Seq(IntSinkPortParameters(Seq(IntSinkParameters())))) | ||||||
|  |  | ||||||
| case class IntAdapterNode( | case class IntNexusNode( | ||||||
|   sourceFn:       Seq[IntSourcePortParameters] => IntSourcePortParameters, |   sourceFn:       Seq[IntSourcePortParameters] => IntSourcePortParameters, | ||||||
|   sinkFn:         Seq[IntSinkPortParameters]   => IntSinkPortParameters, |   sinkFn:         Seq[IntSinkPortParameters]   => IntSinkPortParameters, | ||||||
|   numSourcePorts: Range.Inclusive = 1 to 1, |   numSourcePorts: Range.Inclusive = 0 to 128, | ||||||
|   numSinkPorts:   Range.Inclusive = 1 to 1) |   numSinkPorts:   Range.Inclusive = 0 to 128) | ||||||
|   extends InteriorNode(IntImp)(sourceFn, sinkFn, numSourcePorts, numSinkPorts) |   extends NexusNode(IntImp)(sourceFn, sinkFn, numSourcePorts, numSinkPorts) | ||||||
|  |  | ||||||
| case class IntOutputNode() extends OutputNode(IntImp) | case class IntOutputNode() extends OutputNode(IntImp) | ||||||
| case class IntInputNode() extends InputNode(IntImp) | case class IntInputNode() extends InputNode(IntImp) | ||||||
| @@ -103,9 +103,7 @@ case class IntInternalInputNode(num: Int) extends InternalInputNode(IntImp)(Seq( | |||||||
|  |  | ||||||
| class IntXbar()(implicit p: Parameters) extends LazyModule | class IntXbar()(implicit p: Parameters) extends LazyModule | ||||||
| { | { | ||||||
|   val intnode = IntAdapterNode( |   val intnode = IntNexusNode( | ||||||
|     numSourcePorts = 0 to 128, |  | ||||||
|     numSinkPorts   = 0 to 128, |  | ||||||
|     sinkFn         = { _ => IntSinkPortParameters(Seq(IntSinkParameters())) }, |     sinkFn         = { _ => IntSinkPortParameters(Seq(IntSinkParameters())) }, | ||||||
|     sourceFn       = { seq => |     sourceFn       = { seq => | ||||||
|       IntSourcePortParameters((seq zip seq.map(_.num).scanLeft(0)(_+_).init).map { |       IntSourcePortParameters((seq zip seq.map(_.num).scanLeft(0)(_+_).init).map { | ||||||
|   | |||||||
| @@ -7,7 +7,7 @@ import chisel3.internal.sourceinfo.{SourceInfo, SourceLine} | |||||||
| import config._ | import config._ | ||||||
| import diplomacy._ | import diplomacy._ | ||||||
|  |  | ||||||
| case class TLMonitorArgs(gen: () => TLBundleSnoop, edge: () => TLEdge, sourceInfo: SourceInfo, p: Parameters) | case class TLMonitorArgs(edge: () => Seq[TLEdge], sourceInfo: SourceInfo, p: Parameters) | ||||||
|  |  | ||||||
| abstract class TLMonitorBase(args: TLMonitorArgs) extends LazyModule()(args.p) | abstract class TLMonitorBase(args: TLMonitorArgs) extends LazyModule()(args.p) | ||||||
| { | { | ||||||
| @@ -16,11 +16,12 @@ abstract class TLMonitorBase(args: TLMonitorArgs) extends LazyModule()(args.p) | |||||||
|   def legalize(bundle: TLBundleSnoop, edge: TLEdge, reset: Bool): Unit |   def legalize(bundle: TLBundleSnoop, edge: TLEdge, reset: Bool): Unit | ||||||
|  |  | ||||||
|   lazy val module = new LazyModuleImp(this) { |   lazy val module = new LazyModuleImp(this) { | ||||||
|  |     val edges = args.edge() | ||||||
|     val io = new Bundle { |     val io = new Bundle { | ||||||
|       val in = args.gen().asInput |       val in = Vec(edges.size, new TLBundleSnoop(TLBundleParameters.union(edges.map(_.bundle)))).flip | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     legalize(io.in, args.edge(), reset) |     (edges zip io.in).foreach { case (e, in) => legalize(in, e, reset) } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -24,44 +24,47 @@ object TLImp extends NodeImp[TLClientPortParameters, TLManagerPortParameters, TL | |||||||
|   override def labelI(ei: TLEdgeIn)  = (ei.manager.beatBytes * 8).toString |   override def labelI(ei: TLEdgeIn)  = (ei.manager.beatBytes * 8).toString | ||||||
|   override def labelO(eo: TLEdgeOut) = (eo.manager.beatBytes * 8).toString |   override def labelO(eo: TLEdgeOut) = (eo.manager.beatBytes * 8).toString | ||||||
|  |  | ||||||
|   def connect(bo: => TLBundle, bi: => TLBundle, ei: => TLEdgeIn)(implicit p: Parameters, sourceInfo: SourceInfo): (Option[LazyModule], () => Unit) = { |   override def connect(bindings: () => Seq[(TLEdgeIn, TLBundle, TLBundle)])(implicit p: Parameters, sourceInfo: SourceInfo): (Option[LazyModule], () => Unit) = { | ||||||
|     val monitor = p(TLMonitorBuilder)(TLMonitorArgs(() => new TLBundleSnoop(bo.params), () => ei, sourceInfo, p)) |     val monitor = p(TLMonitorBuilder)(TLMonitorArgs(() => bindings().map(_._1), sourceInfo, p)) | ||||||
|     (monitor, () => { |     (monitor, () => { | ||||||
|       bi <> bo |       val eval = bindings () | ||||||
|       monitor.foreach { _.module.io.in := TLBundleSnoop(bo) } |       monitor.foreach { m => (eval zip m.module.io.in) foreach { case ((_,i,o), m) => m := TLBundleSnoop(o,i) } } | ||||||
|       if (p(TLCombinationalCheck)) { |       eval.foreach { case (_, bi, bo) => | ||||||
|         // It is forbidden for valid to depend on ready in TL2 |         bi <> bo | ||||||
|         // If someone did that, then this will create a detectable combinational loop |         if (p(TLCombinationalCheck)) { | ||||||
|         bo.a.ready := bi.a.ready && bo.a.valid |           // It is forbidden for valid to depend on ready in TL2 | ||||||
|         bi.b.ready := bo.b.ready && bi.b.valid |           // If someone did that, then this will create a detectable combinational loop | ||||||
|         bo.c.ready := bi.c.ready && bo.c.valid |           bo.a.ready := bi.a.ready && bo.a.valid | ||||||
|         bi.d.ready := bo.d.ready && bi.d.valid |           bi.b.ready := bo.b.ready && bi.b.valid | ||||||
|         bo.e.ready := bi.e.ready && bo.e.valid |           bo.c.ready := bi.c.ready && bo.c.valid | ||||||
|       } |           bi.d.ready := bo.d.ready && bi.d.valid | ||||||
|       if (p(TLCombinationalCheck)) { |           bo.e.ready := bi.e.ready && bo.e.valid | ||||||
|         // Randomly stall the transfers |         } | ||||||
|         val allow = LFSRNoiseMaker(5) |         if (p(TLCombinationalCheck)) { | ||||||
|         bi.a.valid := bo.a.valid && allow(0) |           // Randomly stall the transfers | ||||||
|         bo.a.ready := bi.a.ready && allow(0) |           val allow = LFSRNoiseMaker(5) | ||||||
|         bo.b.valid := bi.b.valid && allow(1) |           bi.a.valid := bo.a.valid && allow(0) | ||||||
|         bi.b.ready := bo.b.ready && allow(1) |           bo.a.ready := bi.a.ready && allow(0) | ||||||
|         bi.c.valid := bo.c.valid && allow(2) |           bo.b.valid := bi.b.valid && allow(1) | ||||||
|         bo.c.ready := bi.c.ready && allow(2) |           bi.b.ready := bo.b.ready && allow(1) | ||||||
|         bo.d.valid := bi.d.valid && allow(3) |           bi.c.valid := bo.c.valid && allow(2) | ||||||
|         bi.d.ready := bo.d.ready && allow(3) |           bo.c.ready := bi.c.ready && allow(2) | ||||||
|         bi.e.valid := bo.e.valid && allow(4) |           bo.d.valid := bi.d.valid && allow(3) | ||||||
|         bo.e.ready := bi.e.ready && allow(4) |           bi.d.ready := bo.d.ready && allow(3) | ||||||
|         // Inject garbage whenever not valid |           bi.e.valid := bo.e.valid && allow(4) | ||||||
|         val bits_a = bo.a.bits.fromBits(LFSRNoiseMaker(bo.a.bits.asUInt.getWidth)) |           bo.e.ready := bi.e.ready && allow(4) | ||||||
|         val bits_b = bi.b.bits.fromBits(LFSRNoiseMaker(bi.b.bits.asUInt.getWidth)) |           // Inject garbage whenever not valid | ||||||
|         val bits_c = bo.c.bits.fromBits(LFSRNoiseMaker(bo.c.bits.asUInt.getWidth)) |           val bits_a = bo.a.bits.fromBits(LFSRNoiseMaker(bo.a.bits.asUInt.getWidth)) | ||||||
|         val bits_d = bi.d.bits.fromBits(LFSRNoiseMaker(bi.d.bits.asUInt.getWidth)) |           val bits_b = bi.b.bits.fromBits(LFSRNoiseMaker(bi.b.bits.asUInt.getWidth)) | ||||||
|         val bits_e = bo.e.bits.fromBits(LFSRNoiseMaker(bo.e.bits.asUInt.getWidth)) |           val bits_c = bo.c.bits.fromBits(LFSRNoiseMaker(bo.c.bits.asUInt.getWidth)) | ||||||
|         when (!bi.a.valid) { bi.a.bits := bits_a } |           val bits_d = bi.d.bits.fromBits(LFSRNoiseMaker(bi.d.bits.asUInt.getWidth)) | ||||||
|         when (!bo.b.valid) { bo.b.bits := bits_b } |           val bits_e = bo.e.bits.fromBits(LFSRNoiseMaker(bo.e.bits.asUInt.getWidth)) | ||||||
|         when (!bi.c.valid) { bi.c.bits := bits_c } |           when (!bi.a.valid) { bi.a.bits := bits_a } | ||||||
|         when (!bo.d.valid) { bo.d.bits := bits_d } |           when (!bo.b.valid) { bo.b.bits := bits_b } | ||||||
|         when (!bi.e.valid) { bi.e.bits := bits_e } |           when (!bi.c.valid) { bi.c.bits := bits_c } | ||||||
|  |           when (!bo.d.valid) { bo.d.bits := bits_d } | ||||||
|  |           when (!bi.e.valid) { bi.e.bits := bits_e } | ||||||
|  |         } | ||||||
|       } |       } | ||||||
|     }) |     }) | ||||||
|   } |   } | ||||||
| @@ -86,29 +89,33 @@ object TLImp extends NodeImp[TLClientPortParameters, TLManagerPortParameters, TL | |||||||
|  |  | ||||||
| // Nodes implemented inside modules | // Nodes implemented inside modules | ||||||
| case class TLIdentityNode() extends IdentityNode(TLImp) | case class TLIdentityNode() extends IdentityNode(TLImp) | ||||||
| case class TLClientNode(portParams: TLClientPortParameters, numPorts: Range.Inclusive = 1 to 1) | case class TLClientNode(portParams: Seq[TLClientPortParameters]) extends SourceNode(TLImp)(portParams) | ||||||
|   extends SourceNode(TLImp)(portParams, numPorts) | case class TLManagerNode(portParams: Seq[TLManagerPortParameters]) extends SinkNode(TLImp)(portParams) | ||||||
| case class TLManagerNode(portParams: TLManagerPortParameters, numPorts: Range.Inclusive = 1 to 1) |  | ||||||
|   extends SinkNode(TLImp)(portParams, numPorts) |  | ||||||
|  |  | ||||||
| object TLClientNode | object TLClientNode | ||||||
| { | { | ||||||
|   def apply(params: TLClientParameters) = |   def apply(params: TLClientParameters) = | ||||||
|     new TLClientNode(TLClientPortParameters(Seq(params)), 1 to 1) |     new TLClientNode(Seq(TLClientPortParameters(Seq(params)))) | ||||||
| } | } | ||||||
|  |  | ||||||
| object TLManagerNode | object TLManagerNode | ||||||
| { | { | ||||||
|   def apply(beatBytes: Int, params: TLManagerParameters) = |   def apply(beatBytes: Int, params: TLManagerParameters) = | ||||||
|     new TLManagerNode(TLManagerPortParameters(Seq(params), beatBytes, minLatency = 0), 1 to 1) |     new TLManagerNode(Seq(TLManagerPortParameters(Seq(params), beatBytes, minLatency = 0))) | ||||||
| } | } | ||||||
|  |  | ||||||
| case class TLAdapterNode( | case class TLAdapterNode( | ||||||
|  |   clientFn:  TLClientPortParameters  => TLClientPortParameters, | ||||||
|  |   managerFn: TLManagerPortParameters => TLManagerPortParameters, | ||||||
|  |   num:       Range.Inclusive = 0 to 999) | ||||||
|  |   extends AdapterNode(TLImp)(clientFn, managerFn, num) | ||||||
|  |  | ||||||
|  | case class TLNexusNode( | ||||||
|   clientFn:        Seq[TLClientPortParameters]  => TLClientPortParameters, |   clientFn:        Seq[TLClientPortParameters]  => TLClientPortParameters, | ||||||
|   managerFn:       Seq[TLManagerPortParameters] => TLManagerPortParameters, |   managerFn:       Seq[TLManagerPortParameters] => TLManagerPortParameters, | ||||||
|   numClientPorts:  Range.Inclusive = 1 to 1, |   numClientPorts:  Range.Inclusive = 1 to 999, | ||||||
|   numManagerPorts: Range.Inclusive = 1 to 1) |   numManagerPorts: Range.Inclusive = 1 to 999) | ||||||
|   extends InteriorNode(TLImp)(clientFn, managerFn, numClientPorts, numManagerPorts) |   extends NexusNode(TLImp)(clientFn, managerFn, numClientPorts, numManagerPorts) | ||||||
|  |  | ||||||
| // Nodes passed from an inner module | // Nodes passed from an inner module | ||||||
| case class TLOutputNode() extends OutputNode(TLImp) | case class TLOutputNode() extends OutputNode(TLImp) | ||||||
| @@ -155,10 +162,6 @@ object TLAsyncImp extends NodeImp[TLAsyncClientPortParameters, TLAsyncManagerPor | |||||||
|   override def labelI(ei: TLAsyncEdgeParameters) = ei.manager.depth.toString |   override def labelI(ei: TLAsyncEdgeParameters) = ei.manager.depth.toString | ||||||
|   override def labelO(eo: TLAsyncEdgeParameters) = eo.manager.depth.toString |   override def labelO(eo: TLAsyncEdgeParameters) = eo.manager.depth.toString | ||||||
|  |  | ||||||
|   def connect(bo: => TLAsyncBundle, bi: => TLAsyncBundle, ei: => TLAsyncEdgeParameters)(implicit p: Parameters, sourceInfo: SourceInfo): (Option[LazyModule], () => Unit) = { |  | ||||||
|     (None, () => { bi <> bo }) |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   override def mixO(pd: TLAsyncClientPortParameters, node: OutwardNode[TLAsyncClientPortParameters, TLAsyncManagerPortParameters, TLAsyncBundle]): TLAsyncClientPortParameters  = |   override def mixO(pd: TLAsyncClientPortParameters, node: OutwardNode[TLAsyncClientPortParameters, TLAsyncManagerPortParameters, TLAsyncBundle]): TLAsyncClientPortParameters  = | ||||||
|    pd.copy(base = pd.base.copy(clients  = pd.base.clients.map  { c => c.copy (nodePath = node +: c.nodePath) })) |    pd.copy(base = pd.base.copy(clients  = pd.base.clients.map  { c => c.copy (nodePath = node +: c.nodePath) })) | ||||||
|   override def mixI(pu: TLAsyncManagerPortParameters, node: InwardNode[TLAsyncClientPortParameters, TLAsyncManagerPortParameters, TLAsyncBundle]): TLAsyncManagerPortParameters = |   override def mixI(pu: TLAsyncManagerPortParameters, node: InwardNode[TLAsyncClientPortParameters, TLAsyncManagerPortParameters, TLAsyncBundle]): TLAsyncManagerPortParameters = | ||||||
| @@ -169,17 +172,15 @@ case class TLAsyncIdentityNode() extends IdentityNode(TLAsyncImp) | |||||||
| case class TLAsyncOutputNode() extends OutputNode(TLAsyncImp) | case class TLAsyncOutputNode() extends OutputNode(TLAsyncImp) | ||||||
| case class TLAsyncInputNode() extends InputNode(TLAsyncImp) | case class TLAsyncInputNode() extends InputNode(TLAsyncImp) | ||||||
|  |  | ||||||
| case class TLAsyncSourceNode(sync: Int) extends MixedNode(TLImp, TLAsyncImp)( | case class TLAsyncSourceNode(sync: Int) | ||||||
|   dFn = { case (1, Seq(p)) => Seq(TLAsyncClientPortParameters(p)) }, |   extends MixedAdapterNode(TLImp, TLAsyncImp)( | ||||||
|   uFn = { case (1, Seq(p)) => Seq(p.base.copy(minLatency = sync+1)) }, // discard cycles in other clock domain |     dFn = { p => TLAsyncClientPortParameters(p) }, | ||||||
|   numPO = 1 to 1, |     uFn = { p => p.base.copy(minLatency = sync+1) }) // discard cycles in other clock domain | ||||||
|   numPI = 1 to 1) |  | ||||||
|  |  | ||||||
| case class TLAsyncSinkNode(depth: Int, sync: Int) extends MixedNode(TLAsyncImp, TLImp)( | case class TLAsyncSinkNode(depth: Int, sync: Int) | ||||||
|   dFn = { case (1, Seq(p)) => Seq(p.base.copy(minLatency = sync+1)) }, |   extends MixedAdapterNode(TLAsyncImp, TLImp)( | ||||||
|   uFn = { case (1, Seq(p)) => Seq(TLAsyncManagerPortParameters(depth, p)) }, |     dFn = { p => p.base.copy(minLatency = sync+1) }, | ||||||
|   numPO = 1 to 1, |     uFn = { p => TLAsyncManagerPortParameters(depth, p) }) | ||||||
|   numPI = 1 to 1) |  | ||||||
|  |  | ||||||
| object TLRationalImp extends NodeImp[TLClientPortParameters, TLManagerPortParameters, TLEdgeParameters, TLEdgeParameters, TLRationalBundle] | object TLRationalImp extends NodeImp[TLClientPortParameters, TLManagerPortParameters, TLEdgeParameters, TLEdgeParameters, TLRationalBundle] | ||||||
| { | { | ||||||
| @@ -191,10 +192,6 @@ object TLRationalImp extends NodeImp[TLClientPortParameters, TLManagerPortParame | |||||||
|  |  | ||||||
|   def colour = "#00ff00" // green |   def colour = "#00ff00" // green | ||||||
|  |  | ||||||
|   def connect(bo: => TLRationalBundle, bi: => TLRationalBundle, ei: => TLEdgeParameters)(implicit p: Parameters, sourceInfo: SourceInfo): (Option[LazyModule], () => Unit) = { |  | ||||||
|     (None, () => { bi <> bo }) |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   override def mixO(pd: TLClientPortParameters, node: OutwardNode[TLClientPortParameters, TLManagerPortParameters, TLRationalBundle]): TLClientPortParameters  = |   override def mixO(pd: TLClientPortParameters, node: OutwardNode[TLClientPortParameters, TLManagerPortParameters, TLRationalBundle]): TLClientPortParameters  = | ||||||
|    pd.copy(clients  = pd.clients.map  { c => c.copy (nodePath = node +: c.nodePath) }) |    pd.copy(clients  = pd.clients.map  { c => c.copy (nodePath = node +: c.nodePath) }) | ||||||
|   override def mixI(pu: TLManagerPortParameters, node: InwardNode[TLClientPortParameters, TLManagerPortParameters, TLRationalBundle]): TLManagerPortParameters = |   override def mixI(pu: TLManagerPortParameters, node: InwardNode[TLClientPortParameters, TLManagerPortParameters, TLRationalBundle]): TLManagerPortParameters = | ||||||
| @@ -205,14 +202,12 @@ case class TLRationalIdentityNode() extends IdentityNode(TLRationalImp) | |||||||
| case class TLRationalOutputNode() extends OutputNode(TLRationalImp) | case class TLRationalOutputNode() extends OutputNode(TLRationalImp) | ||||||
| case class TLRationalInputNode() extends InputNode(TLRationalImp) | case class TLRationalInputNode() extends InputNode(TLRationalImp) | ||||||
|  |  | ||||||
| case class TLRationalSourceNode() extends MixedNode(TLImp, TLRationalImp)( | case class TLRationalSourceNode() | ||||||
|   dFn = { case (_, s) => s }, |   extends MixedAdapterNode(TLImp, TLRationalImp)( | ||||||
|   uFn = { case (_, s) => s.map(p => p.copy(minLatency = 1)) }, // discard cycles from other clock domain |     dFn = { p => p }, | ||||||
|   numPO = 0 to 999, |     uFn = { p => p.copy(minLatency = 1) }) // discard cycles from other clock domain | ||||||
|   numPI = 0 to 999) |  | ||||||
|  |  | ||||||
| case class TLRationalSinkNode() extends MixedNode(TLRationalImp, TLImp)( | case class TLRationalSinkNode() | ||||||
|   dFn = { case (_, s) => s.map(p => p.copy(minLatency = 1)) }, |   extends MixedAdapterNode(TLRationalImp, TLImp)( | ||||||
|   uFn = { case (_, s) => s }, |     dFn = { p => p.copy(minLatency = 1) }, | ||||||
|   numPO = 0 to 999, |     uFn = { p => p }) | ||||||
|   numPI = 0 to 999) |  | ||||||
|   | |||||||
| @@ -5,6 +5,7 @@ package uncore.tilelink2 | |||||||
| import Chisel._ | import Chisel._ | ||||||
| import config._ | import config._ | ||||||
| import diplomacy._ | import diplomacy._ | ||||||
|  | import util.GenericParameterizedBundle | ||||||
|  |  | ||||||
| // We detect concurrent puts that put memory into an undefined state. | // We detect concurrent puts that put memory into an undefined state. | ||||||
| // put0, put0Ack, put1, put1Ack => ok: defined | // put0, put0Ack, put1, put1Ack => ok: defined | ||||||
| @@ -31,268 +32,271 @@ class TLRAMModel(log: String = "")(implicit p: Parameters) extends LazyModule | |||||||
|       val out = node.bundleOut |       val out = node.bundleOut | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // !!! support multiple clients via clock division |     ((io.in zip io.out) zip (node.edgesIn zip node.edgesOut)) foreach { case ((in, out), (edgeIn, edgeOut)) => | ||||||
|     require (io.out.size == 1) |       val edge         = edgeIn | ||||||
|  |       val endAddress   = edge.manager.maxAddress + 1 | ||||||
|  |       val endSourceId  = edge.client.endSourceId | ||||||
|  |       val maxTransfer  = edge.manager.maxTransfer | ||||||
|  |       val beatBytes    = edge.manager.beatBytes | ||||||
|  |       val endAddressHi = (endAddress / beatBytes).intValue | ||||||
|  |       val maxLgBeats   = log2Up(maxTransfer/beatBytes) | ||||||
|  |       val shift        = log2Ceil(beatBytes) | ||||||
|  |       val decTrees     = log2Up(maxTransfer/beatBytes) | ||||||
|  |       val addressBits  = log2Up(endAddress) | ||||||
|  |       val countBits    = log2Up(endSourceId) | ||||||
|  |       val sizeBits     = edge.bundle.sizeBits | ||||||
|  |  | ||||||
|     val in = io.in(0) |       // Reset control logic | ||||||
|     val out = io.out(0) |       val wipeIndex = RegInit(UInt(0, width = log2Ceil(endAddressHi) + 1)) | ||||||
|  |       val wipe = !wipeIndex(log2Ceil(endAddressHi)) | ||||||
|  |       wipeIndex := wipeIndex + wipe.asUInt | ||||||
|  |  | ||||||
|     val edge         = node.edgesIn(0) |       // Block traffic while wiping Mems | ||||||
|     val endAddress   = edge.manager.maxAddress + 1 |       in.a.ready := out.a.ready && !wipe | ||||||
|     val endSourceId  = edge.client.endSourceId |       out.a.valid := in.a.valid && !wipe | ||||||
|     val maxTransfer  = edge.manager.maxTransfer |       out.a.bits  := in.a.bits | ||||||
|     val beatBytes    = edge.manager.beatBytes |       out.d.ready := in.d.ready && !wipe | ||||||
|     val endAddressHi = (endAddress / beatBytes).intValue |       in.d.valid := out.d.valid && !wipe | ||||||
|     val maxLgBeats   = log2Up(maxTransfer/beatBytes) |       in.d.bits  := out.d.bits | ||||||
|     val shift        = log2Ceil(beatBytes) |  | ||||||
|     val decTrees     = log2Up(maxTransfer/beatBytes) |  | ||||||
|     val addressBits  = log2Up(endAddress) |  | ||||||
|     val countBits    = log2Up(endSourceId) |  | ||||||
|     val sizeBits     = edge.bundle.sizeBits |  | ||||||
|  |  | ||||||
|     // Reset control logic |       // BCE unsupported | ||||||
|     val wipeIndex = RegInit(UInt(0, width = log2Ceil(endAddressHi) + 1)) |       in.b.valid := Bool(false) | ||||||
|     val wipe = !wipeIndex(log2Ceil(endAddressHi)) |       out.c.valid := Bool(false) | ||||||
|     wipeIndex := wipeIndex + wipe.asUInt |       out.e.valid := Bool(false) | ||||||
|  |       out.b.ready := Bool(true) | ||||||
|  |       in.c.ready := Bool(true) | ||||||
|  |       in.e.ready := Bool(true) | ||||||
|  |  | ||||||
|     // Block traffic while wiping Mems |       val params = TLRAMModel.MonitorParameters(addressBits, sizeBits) | ||||||
|     in.a.ready := out.a.ready && !wipe |  | ||||||
|     out.a.valid := in.a.valid && !wipe |  | ||||||
|     out.a.bits  := in.a.bits |  | ||||||
|     out.d.ready := in.d.ready && !wipe |  | ||||||
|     in.d.valid := out.d.valid && !wipe |  | ||||||
|     in.d.bits  := out.d.bits |  | ||||||
|  |  | ||||||
|     // BCE unsupported |       // Infer as simple dual port BRAM/M10k with write-first/new-data semantics (bypass needed) | ||||||
|     in.b.valid := Bool(false) |       val shadow = Seq.fill(beatBytes) { Mem(endAddressHi, new TLRAMModel.ByteMonitor(params)) } | ||||||
|     out.c.valid := Bool(false) |       val inc_bytes = Seq.fill(beatBytes) { Mem(endAddressHi, UInt(width = countBits)) } | ||||||
|     out.e.valid := Bool(false) |       val dec_bytes = Seq.fill(beatBytes) { Mem(endAddressHi, UInt(width = countBits)) } | ||||||
|     out.b.ready := Bool(true) |       val inc_trees = Seq.tabulate(decTrees) { i => Mem(endAddressHi >> (i+1), UInt(width = countBits)) } | ||||||
|     in.c.ready := Bool(true) |       val dec_trees = Seq.tabulate(decTrees) { i => Mem(endAddressHi >> (i+1), UInt(width = countBits)) } | ||||||
|     in.e.ready := Bool(true) |  | ||||||
|  |  | ||||||
|     class ByteMonitor extends Bundle { |       val shadow_wen = Wire(init = Fill(beatBytes, wipe)) | ||||||
|       val valid = Bool() |       val inc_bytes_wen = Wire(init = Fill(beatBytes, wipe)) | ||||||
|       val value = UInt(width = 8) |       val dec_bytes_wen = Wire(init = Fill(beatBytes, wipe)) | ||||||
|     } |       val inc_trees_wen = Wire(init = Fill(decTrees, wipe)) | ||||||
|     class FlightMonitor extends Bundle { |       val dec_trees_wen = Wire(init = Fill(decTrees, wipe)) | ||||||
|       val base    = UInt(width = addressBits) |  | ||||||
|       val size    = UInt(width = sizeBits) |  | ||||||
|       val opcode  = UInt(width = 3) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // Infer as simple dual port BRAM/M10k with write-first/new-data semantics (bypass needed) |       // This must be registers b/c we build a CAM from it | ||||||
|     val shadow = Seq.fill(beatBytes) { Mem(endAddressHi, new ByteMonitor) } |       val flight = Reg(Vec(endSourceId, new TLRAMModel.FlightMonitor(params))) | ||||||
|     val inc_bytes = Seq.fill(beatBytes) { Mem(endAddressHi, UInt(width = countBits)) } |       val valid = Reg(Vec(endSourceId, Bool())) | ||||||
|     val dec_bytes = Seq.fill(beatBytes) { Mem(endAddressHi, UInt(width = countBits)) } |  | ||||||
|     val inc_trees = Seq.tabulate(decTrees) { i => Mem(endAddressHi >> (i+1), UInt(width = countBits)) } |  | ||||||
|     val dec_trees = Seq.tabulate(decTrees) { i => Mem(endAddressHi >> (i+1), UInt(width = countBits)) } |  | ||||||
|  |  | ||||||
|     val shadow_wen = Wire(init = Fill(beatBytes, wipe)) |       // We want to cross flight data from A to D in the same cycle (for combinational TL2 devices) | ||||||
|     val inc_bytes_wen = Wire(init = Fill(beatBytes, wipe)) |       val a_flight = Wire(new TLRAMModel.FlightMonitor(params)) | ||||||
|     val dec_bytes_wen = Wire(init = Fill(beatBytes, wipe)) |       a_flight.base   := edge.address(in.a.bits) | ||||||
|     val inc_trees_wen = Wire(init = Fill(decTrees, wipe)) |       a_flight.size   := edge.size(in.a.bits) | ||||||
|     val dec_trees_wen = Wire(init = Fill(decTrees, wipe)) |       a_flight.opcode := in.a.bits.opcode | ||||||
|  |  | ||||||
|     // This must be registers b/c we build a CAM from it |       when (in.a.fire()) { flight(in.a.bits.source) := a_flight } | ||||||
|     val flight = Reg(Vec(endSourceId, new FlightMonitor)) |       val bypass = if (edge.manager.minLatency > 0) Bool(false) else in.a.valid && in.a.bits.source === out.d.bits.source | ||||||
|     val valid = Reg(Vec(endSourceId, Bool())) |       val d_flight = RegNext(Mux(bypass, a_flight, flight(out.d.bits.source))) | ||||||
|  |  | ||||||
|     // We want to cross flight data from A to D in the same cycle (for combinational TL2 devices) |       // Process A access requests | ||||||
|     val a_flight = Wire(new FlightMonitor) |       val a = Reg(next = in.a.bits) | ||||||
|     a_flight.base   := edge.address(in.a.bits) |       val a_fire = Reg(next = in.a.fire(), init = Bool(false)) | ||||||
|     a_flight.size   := edge.size(in.a.bits) |       val (a_first, a_last, _, a_address_inc) = edge.addr_inc(a, a_fire) | ||||||
|     a_flight.opcode := in.a.bits.opcode |       val a_size = edge.size(a) | ||||||
|  |       val a_sizeOH = UIntToOH(a_size) | ||||||
|  |       val a_address = a.address | a_address_inc | ||||||
|  |       val a_addr_hi = edge.addr_hi(a_address) | ||||||
|  |       val a_base = edge.address(a) | ||||||
|  |       val a_mask = edge.mask(a_base, a_size) | ||||||
|  |       val a_fifo = edge.manager.hasFifoIdFast(a_base) | ||||||
|  |  | ||||||
|     when (in.a.fire()) { flight(in.a.bits.source) := a_flight } |       // Grab the concurrency state we need | ||||||
|     val bypass = if (edge.manager.minLatency > 0) Bool(false) else in.a.valid && in.a.bits.source === out.d.bits.source |       val a_inc_bytes = inc_bytes.map(_.read(a_addr_hi)) | ||||||
|     val d_flight = RegNext(Mux(bypass, a_flight, flight(out.d.bits.source))) |       val a_dec_bytes = dec_bytes.map(_.read(a_addr_hi)) | ||||||
|  |       val a_inc_trees = inc_trees.zipWithIndex.map{ case (m, i) => m.read(a_addr_hi >> (i+1)) } | ||||||
|  |       val a_dec_trees = dec_trees.zipWithIndex.map{ case (m, i) => m.read(a_addr_hi >> (i+1)) } | ||||||
|  |       val a_inc_tree = a_inc_trees.fold(UInt(0))(_ + _) | ||||||
|  |       val a_dec_tree = a_dec_trees.fold(UInt(0))(_ + _) | ||||||
|  |       val a_inc = a_inc_bytes.map(_ + a_inc_tree) | ||||||
|  |       val a_dec = a_dec_bytes.map(_ + a_dec_tree) | ||||||
|  |  | ||||||
|     // Process A access requests |       when (a_fire) { | ||||||
|     val a = Reg(next = in.a.bits) |         // Record the request so we can handle it's response | ||||||
|     val a_fire = Reg(next = in.a.fire(), init = Bool(false)) |         assert (a.opcode =/= TLMessages.Acquire) | ||||||
|     val (a_first, a_last, _, a_address_inc) = edge.addr_inc(a, a_fire) |  | ||||||
|     val a_size = edge.size(a) |  | ||||||
|     val a_sizeOH = UIntToOH(a_size) |  | ||||||
|     val a_address = a.address | a_address_inc |  | ||||||
|     val a_addr_hi = edge.addr_hi(a_address) |  | ||||||
|     val a_base = edge.address(a) |  | ||||||
|     val a_mask = edge.mask(a_base, a_size) |  | ||||||
|     val a_fifo = edge.manager.hasFifoIdFast(a_base) |  | ||||||
|  |  | ||||||
|     // Grab the concurrency state we need |         // Mark the operation as valid | ||||||
|     val a_inc_bytes = inc_bytes.map(_.read(a_addr_hi)) |         valid(a.source) := Bool(true) | ||||||
|     val a_dec_bytes = dec_bytes.map(_.read(a_addr_hi)) |  | ||||||
|     val a_inc_trees = inc_trees.zipWithIndex.map{ case (m, i) => m.read(a_addr_hi >> (i+1)) } |  | ||||||
|     val a_dec_trees = dec_trees.zipWithIndex.map{ case (m, i) => m.read(a_addr_hi >> (i+1)) } |  | ||||||
|     val a_inc_tree = a_inc_trees.fold(UInt(0))(_ + _) |  | ||||||
|     val a_dec_tree = a_dec_trees.fold(UInt(0))(_ + _) |  | ||||||
|     val a_inc = a_inc_bytes.map(_ + a_inc_tree) |  | ||||||
|     val a_dec = a_dec_bytes.map(_ + a_dec_tree) |  | ||||||
|  |  | ||||||
|     when (a_fire) { |         // Increase the per-byte flight counter for the whole transaction | ||||||
|       // Record the request so we can handle it's response |         when (a_first && a.opcode =/= TLMessages.Hint && a.opcode =/= TLMessages.Get) { | ||||||
|       assert (a.opcode =/= TLMessages.Acquire) |           when (a_size <= UInt(shift)) { | ||||||
|  |             inc_bytes_wen := a_mask | ||||||
|       // Mark the operation as valid |           } | ||||||
|       valid(a.source) := Bool(true) |           inc_trees_wen := a_sizeOH >> (shift+1) | ||||||
|  |  | ||||||
|       // Increase the per-byte flight counter for the whole transaction |  | ||||||
|       when (a_first && a.opcode =/= TLMessages.Hint && a.opcode =/= TLMessages.Get) { |  | ||||||
|         when (a_size <= UInt(shift)) { |  | ||||||
|           inc_bytes_wen := a_mask |  | ||||||
|         } |         } | ||||||
|         inc_trees_wen := a_sizeOH >> (shift+1) |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       when (a.opcode === TLMessages.PutFullData || a.opcode === TLMessages.PutPartialData || |         when (a.opcode === TLMessages.PutFullData || a.opcode === TLMessages.PutPartialData || | ||||||
|             a.opcode === TLMessages.ArithmeticData || a.opcode === TLMessages.LogicalData) { |               a.opcode === TLMessages.ArithmeticData || a.opcode === TLMessages.LogicalData) { | ||||||
|         shadow_wen := a.mask |           shadow_wen := a.mask | ||||||
|         for (i <- 0 until beatBytes) { |           for (i <- 0 until beatBytes) { | ||||||
|           val busy = a_inc(i) - a_dec(i) - (!a_first).asUInt |             val busy = a_inc(i) - a_dec(i) - (!a_first).asUInt | ||||||
|           val byte = a.data(8*(i+1)-1, 8*i) |             val byte = a.data(8*(i+1)-1, 8*i) | ||||||
|           when (a.mask(i)) { |             when (a.mask(i)) { | ||||||
|             printf(log + " ") |               printf(log + " ") | ||||||
|             when (a.opcode === TLMessages.PutFullData) { printf("PF") } |               when (a.opcode === TLMessages.PutFullData) { printf("PF") } | ||||||
|             when (a.opcode === TLMessages.PutPartialData) { printf("PP") } |               when (a.opcode === TLMessages.PutPartialData) { printf("PP") } | ||||||
|             when (a.opcode === TLMessages.ArithmeticData) { printf("A ") } |               when (a.opcode === TLMessages.ArithmeticData) { printf("A ") } | ||||||
|             when (a.opcode === TLMessages.LogicalData) { printf("L ") } |               when (a.opcode === TLMessages.LogicalData) { printf("L ") } | ||||||
|             printf(" 0x%x := 0x%x #%d %x\n", a_addr_hi << shift | UInt(i), byte, busy, a.param) |               printf(" 0x%x := 0x%x #%d %x\n", a_addr_hi << shift | UInt(i), byte, busy, a.param) | ||||||
|  |             } | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
|       } |  | ||||||
|  |  | ||||||
|       when (a.opcode === TLMessages.Get) { |         when (a.opcode === TLMessages.Get) { | ||||||
|         printf(log + " G  0x%x - 0%x\n", a_base, a_base | UIntToOH1(a_size, addressBits)) |           printf(log + " G  0x%x - 0%x\n", a_base, a_base | UIntToOH1(a_size, addressBits)) | ||||||
|       } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     val a_waddr = Mux(wipe, wipeIndex, a_addr_hi) |  | ||||||
|     for (i <- 0 until beatBytes) { |  | ||||||
|       val data = Wire(new ByteMonitor) |  | ||||||
|       val busy = a_inc(i) =/= a_dec(i) + (!a_first).asUInt |  | ||||||
|       val amo = a.opcode === TLMessages.ArithmeticData || a.opcode === TLMessages.LogicalData |  | ||||||
|       data.valid := Mux(wipe, Bool(false), (!busy || a_fifo) && !amo) |  | ||||||
|       // !!! calculate the AMO? |  | ||||||
|       data.value := a.data(8*(i+1)-1, 8*i) |  | ||||||
|       when (shadow_wen(i)) { |  | ||||||
|         shadow(i).write(a_waddr, data) |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     for (i <- 0 until beatBytes) { |  | ||||||
|       val data = Mux(wipe, UInt(0), a_inc_bytes(i) + UInt(1)) |  | ||||||
|       when (inc_bytes_wen(i)) { |  | ||||||
|         inc_bytes(i).write(a_waddr, data) |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     for (i <- 0 until inc_trees.size) { |  | ||||||
|       val data = Mux(wipe, UInt(0), a_inc_trees(i) + UInt(1)) |  | ||||||
|       when (inc_trees_wen(i)) { |  | ||||||
|         inc_trees(i).write(a_waddr >> (i+1), data) |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // Process D access responses |  | ||||||
|     val d = RegNext(out.d.bits) |  | ||||||
|     val d_fire = Reg(next = out.d.fire(), init = Bool(false)) |  | ||||||
|     val (d_first, d_last, _, d_address_inc) = edge.addr_inc(d, d_fire) |  | ||||||
|     val d_size = edge.size(d) |  | ||||||
|     val d_sizeOH = UIntToOH(d_size) |  | ||||||
|     val d_base = d_flight.base |  | ||||||
|     val d_address = d_base | d_address_inc |  | ||||||
|     val d_addr_hi = edge.addr_hi(d_address) |  | ||||||
|     val d_mask = edge.mask(d_base, d_size) |  | ||||||
|     val d_fifo = edge.manager.hasFifoIdFast(d_flight.base) |  | ||||||
|  |  | ||||||
|     // Grab the concurrency state we need |  | ||||||
|     val d_inc_bytes = inc_bytes.map(_.read(d_addr_hi)) |  | ||||||
|     val d_dec_bytes = dec_bytes.map(_.read(d_addr_hi)) |  | ||||||
|     val d_inc_trees = inc_trees.zipWithIndex.map{ case (m, i) => m.read(d_addr_hi >> (i+1)) } |  | ||||||
|     val d_dec_trees = dec_trees.zipWithIndex.map{ case (m, i) => m.read(d_addr_hi >> (i+1)) } |  | ||||||
|     val d_inc_tree = d_inc_trees.fold(UInt(0))(_ + _) |  | ||||||
|     val d_dec_tree = d_dec_trees.fold(UInt(0))(_ + _) |  | ||||||
|     val d_inc = d_inc_bytes.map(_ + d_inc_tree) |  | ||||||
|     val d_dec = d_dec_bytes.map(_ + d_dec_tree) |  | ||||||
|     val d_shadow = shadow.map(_.read(d_addr_hi)) |  | ||||||
|     val d_valid = valid(d.source) |  | ||||||
|  |  | ||||||
|     when (d_fire) { |  | ||||||
|       // Check the response is correct |  | ||||||
|       assert (d_size === d_flight.size) |  | ||||||
|       // addr_lo is allowed to differ |  | ||||||
|  |  | ||||||
|       when (d_flight.opcode === TLMessages.Hint) { |  | ||||||
|         assert (d.opcode === TLMessages.HintAck) |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       // Decrease the per-byte flight counter for the whole transaction |  | ||||||
|       when (d_last && d_flight.opcode =/= TLMessages.Hint && d_flight.opcode =/= TLMessages.Get) { |  | ||||||
|         when (d_size <= UInt(shift)) { |  | ||||||
|           dec_bytes_wen := d_mask |  | ||||||
|         } |  | ||||||
|         dec_trees_wen := d_sizeOH >> (shift+1) |  | ||||||
|         // NOTE: D channel carries uninterrupted multibeast op, so updating on last is fine |  | ||||||
|         for (i <- 0 until endSourceId) { |  | ||||||
|           // Does this modification overlap a Get? => wipe it's valid |  | ||||||
|           val f_base = flight(i).base |  | ||||||
|           val f_size = flight(i).size |  | ||||||
|           val f_bits = UIntToOH1(f_size, addressBits) |  | ||||||
|           val d_bits = UIntToOH1(d_size, addressBits) |  | ||||||
|           val overlap = ~(~(f_base ^ d_base) | (f_bits | d_bits)) === UInt(0) |  | ||||||
|           when (overlap) { valid(i) := Bool(false) } |  | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       when (d_flight.opcode === TLMessages.PutFullData || d_flight.opcode === TLMessages.PutPartialData) { |       val a_waddr = Mux(wipe, wipeIndex, a_addr_hi) | ||||||
|         assert (d.opcode === TLMessages.AccessAck) |       for (i <- 0 until beatBytes) { | ||||||
|         printf(log + " ") |         val data = Wire(new TLRAMModel.ByteMonitor(params)) | ||||||
|         when (d_flight.opcode === TLMessages.PutFullData) { printf("pf") } |         val busy = a_inc(i) =/= a_dec(i) + (!a_first).asUInt | ||||||
|         when (d_flight.opcode === TLMessages.PutPartialData) { printf("pp") } |         val amo = a.opcode === TLMessages.ArithmeticData || a.opcode === TLMessages.LogicalData | ||||||
|         printf(" 0x%x - 0x%x\n", d_base, d_base | UIntToOH1(d_size, addressBits)) |         data.valid := Mux(wipe, Bool(false), (!busy || a_fifo) && !amo) | ||||||
|  |         // !!! calculate the AMO? | ||||||
|  |         data.value := a.data(8*(i+1)-1, 8*i) | ||||||
|  |         when (shadow_wen(i)) { | ||||||
|  |           shadow(i).write(a_waddr, data) | ||||||
|  |         } | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       when (d_flight.opcode === TLMessages.Get || d_flight.opcode === TLMessages.ArithmeticData || d_flight.opcode === TLMessages.LogicalData) { |       for (i <- 0 until beatBytes) { | ||||||
|         assert (d.opcode === TLMessages.AccessAckData) |         val data = Mux(wipe, UInt(0), a_inc_bytes(i) + UInt(1)) | ||||||
|         for (i <- 0 until beatBytes) { |         when (inc_bytes_wen(i)) { | ||||||
|           val got = d.data(8*(i+1)-1, 8*i) |           inc_bytes(i).write(a_waddr, data) | ||||||
|           val shadow = Wire(init = d_shadow(i)) |         } | ||||||
|           when (d_mask(i)) { |       } | ||||||
|             val d_addr = d_addr_hi << shift | UInt(i) |  | ||||||
|             printf(log + " ") |       for (i <- 0 until inc_trees.size) { | ||||||
|             when (d_flight.opcode === TLMessages.Get) { printf("g ") } |         val data = Mux(wipe, UInt(0), a_inc_trees(i) + UInt(1)) | ||||||
|             when (d_flight.opcode === TLMessages.ArithmeticData) { printf("a ") } |         when (inc_trees_wen(i)) { | ||||||
|             when (d_flight.opcode === TLMessages.LogicalData) { printf("l ") } |           inc_trees(i).write(a_waddr >> (i+1), data) | ||||||
|             printf(" 0x%x := 0x%x", d_addr, got) |         } | ||||||
|             when (!shadow.valid) { |       } | ||||||
|               printf(", undefined (uninitialized or prior overlapping puts)\n") |  | ||||||
|             } .elsewhen (d_inc(i) =/= d_dec(i)) { |       // Process D access responses | ||||||
|               printf(", undefined (concurrent incomplete puts #%d)\n", d_inc(i) - d_dec(i)) |       val d = RegNext(out.d.bits) | ||||||
|             } .elsewhen (!d_fifo && !d_valid) { |       val d_fire = Reg(next = out.d.fire(), init = Bool(false)) | ||||||
|               printf(", undefined (concurrent completed put)\n") |       val (d_first, d_last, _, d_address_inc) = edge.addr_inc(d, d_fire) | ||||||
|             } .otherwise { |       val d_size = edge.size(d) | ||||||
|               printf("\n") |       val d_sizeOH = UIntToOH(d_size) | ||||||
|               assert (shadow.value === got) |       val d_base = d_flight.base | ||||||
|  |       val d_address = d_base | d_address_inc | ||||||
|  |       val d_addr_hi = edge.addr_hi(d_address) | ||||||
|  |       val d_mask = edge.mask(d_base, d_size) | ||||||
|  |       val d_fifo = edge.manager.hasFifoIdFast(d_flight.base) | ||||||
|  |  | ||||||
|  |       // Grab the concurrency state we need | ||||||
|  |       val d_inc_bytes = inc_bytes.map(_.read(d_addr_hi)) | ||||||
|  |       val d_dec_bytes = dec_bytes.map(_.read(d_addr_hi)) | ||||||
|  |       val d_inc_trees = inc_trees.zipWithIndex.map{ case (m, i) => m.read(d_addr_hi >> (i+1)) } | ||||||
|  |       val d_dec_trees = dec_trees.zipWithIndex.map{ case (m, i) => m.read(d_addr_hi >> (i+1)) } | ||||||
|  |       val d_inc_tree = d_inc_trees.fold(UInt(0))(_ + _) | ||||||
|  |       val d_dec_tree = d_dec_trees.fold(UInt(0))(_ + _) | ||||||
|  |       val d_inc = d_inc_bytes.map(_ + d_inc_tree) | ||||||
|  |       val d_dec = d_dec_bytes.map(_ + d_dec_tree) | ||||||
|  |       val d_shadow = shadow.map(_.read(d_addr_hi)) | ||||||
|  |       val d_valid = valid(d.source) | ||||||
|  |  | ||||||
|  |       when (d_fire) { | ||||||
|  |         // Check the response is correct | ||||||
|  |         assert (d_size === d_flight.size) | ||||||
|  |         // addr_lo is allowed to differ | ||||||
|  |  | ||||||
|  |         when (d_flight.opcode === TLMessages.Hint) { | ||||||
|  |           assert (d.opcode === TLMessages.HintAck) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // Decrease the per-byte flight counter for the whole transaction | ||||||
|  |         when (d_last && d_flight.opcode =/= TLMessages.Hint && d_flight.opcode =/= TLMessages.Get) { | ||||||
|  |           when (d_size <= UInt(shift)) { | ||||||
|  |             dec_bytes_wen := d_mask | ||||||
|  |           } | ||||||
|  |           dec_trees_wen := d_sizeOH >> (shift+1) | ||||||
|  |           // NOTE: D channel carries uninterrupted multibeast op, so updating on last is fine | ||||||
|  |           for (i <- 0 until endSourceId) { | ||||||
|  |             // Does this modification overlap a Get? => wipe it's valid | ||||||
|  |             val f_base = flight(i).base | ||||||
|  |             val f_size = flight(i).size | ||||||
|  |             val f_bits = UIntToOH1(f_size, addressBits) | ||||||
|  |             val d_bits = UIntToOH1(d_size, addressBits) | ||||||
|  |             val overlap = ~(~(f_base ^ d_base) | (f_bits | d_bits)) === UInt(0) | ||||||
|  |             when (overlap) { valid(i) := Bool(false) } | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         when (d_flight.opcode === TLMessages.PutFullData || d_flight.opcode === TLMessages.PutPartialData) { | ||||||
|  |           assert (d.opcode === TLMessages.AccessAck) | ||||||
|  |           printf(log + " ") | ||||||
|  |           when (d_flight.opcode === TLMessages.PutFullData) { printf("pf") } | ||||||
|  |           when (d_flight.opcode === TLMessages.PutPartialData) { printf("pp") } | ||||||
|  |           printf(" 0x%x - 0x%x\n", d_base, d_base | UIntToOH1(d_size, addressBits)) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         when (d_flight.opcode === TLMessages.Get || d_flight.opcode === TLMessages.ArithmeticData || d_flight.opcode === TLMessages.LogicalData) { | ||||||
|  |           assert (d.opcode === TLMessages.AccessAckData) | ||||||
|  |           for (i <- 0 until beatBytes) { | ||||||
|  |             val got = d.data(8*(i+1)-1, 8*i) | ||||||
|  |             val shadow = Wire(init = d_shadow(i)) | ||||||
|  |             when (d_mask(i)) { | ||||||
|  |               val d_addr = d_addr_hi << shift | UInt(i) | ||||||
|  |               printf(log + " ") | ||||||
|  |               when (d_flight.opcode === TLMessages.Get) { printf("g ") } | ||||||
|  |               when (d_flight.opcode === TLMessages.ArithmeticData) { printf("a ") } | ||||||
|  |               when (d_flight.opcode === TLMessages.LogicalData) { printf("l ") } | ||||||
|  |               printf(" 0x%x := 0x%x", d_addr, got) | ||||||
|  |               when (!shadow.valid) { | ||||||
|  |                 printf(", undefined (uninitialized or prior overlapping puts)\n") | ||||||
|  |               } .elsewhen (d_inc(i) =/= d_dec(i)) { | ||||||
|  |                 printf(", undefined (concurrent incomplete puts #%d)\n", d_inc(i) - d_dec(i)) | ||||||
|  |               } .elsewhen (!d_fifo && !d_valid) { | ||||||
|  |                 printf(", undefined (concurrent completed put)\n") | ||||||
|  |               } .otherwise { | ||||||
|  |                 printf("\n") | ||||||
|  |                 assert (shadow.value === got) | ||||||
|  |               } | ||||||
|             } |             } | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     } |  | ||||||
|  |  | ||||||
|     val d_waddr = Mux(wipe, wipeIndex, d_addr_hi) |       val d_waddr = Mux(wipe, wipeIndex, d_addr_hi) | ||||||
|     for (i <- 0 until beatBytes) { |       for (i <- 0 until beatBytes) { | ||||||
|       val data = Mux(wipe, UInt(0), d_dec_bytes(i) + UInt(1)) |         val data = Mux(wipe, UInt(0), d_dec_bytes(i) + UInt(1)) | ||||||
|       when (dec_bytes_wen(i)) { |         when (dec_bytes_wen(i)) { | ||||||
|         dec_bytes(i).write(d_waddr, data) |           dec_bytes(i).write(d_waddr, data) | ||||||
|  |         } | ||||||
|       } |       } | ||||||
|     } |  | ||||||
|  |  | ||||||
|     for (i <- 0 until dec_trees.size) { |       for (i <- 0 until dec_trees.size) { | ||||||
|       val data = Mux(wipe, UInt(0), d_dec_trees(i) + UInt(1)) |         val data = Mux(wipe, UInt(0), d_dec_trees(i) + UInt(1)) | ||||||
|       when (dec_trees_wen(i)) { |         when (dec_trees_wen(i)) { | ||||||
|         dec_trees(i).write(d_waddr >> (i+1), data) |           dec_trees(i).write(d_waddr >> (i+1), data) | ||||||
|  |         } | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | object TLRAMModel | ||||||
|  | { | ||||||
|  |   case class MonitorParameters(addressBits: Int, sizeBits: Int) | ||||||
|  |  | ||||||
|  |   class ByteMonitor(params: MonitorParameters) extends GenericParameterizedBundle(params) { | ||||||
|  |     val valid = Bool() | ||||||
|  |     val value = UInt(width = 8) | ||||||
|  |   } | ||||||
|  |   class FlightMonitor(params: MonitorParameters) extends GenericParameterizedBundle(params) { | ||||||
|  |     val base    = UInt(width = params.addressBits) | ||||||
|  |     val size    = UInt(width = params.sizeBits) | ||||||
|  |     val opcode  = UInt(width = 3) | ||||||
|  |   } | ||||||
|  | } | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ import regmapper._ | |||||||
| import scala.math.{min,max} | import scala.math.{min,max} | ||||||
|  |  | ||||||
| class TLRegisterNode(address: AddressSet, concurrency: Int = 0, beatBytes: Int = 4, undefZero: Boolean = true, executable: Boolean = false) | class TLRegisterNode(address: AddressSet, concurrency: Int = 0, beatBytes: Int = 4, undefZero: Boolean = true, executable: Boolean = false) | ||||||
|   extends TLManagerNode(TLManagerPortParameters( |   extends TLManagerNode(Seq(TLManagerPortParameters( | ||||||
|     Seq(TLManagerParameters( |     Seq(TLManagerParameters( | ||||||
|       address            = Seq(address), |       address            = Seq(address), | ||||||
|       executable         = executable, |       executable         = executable, | ||||||
| @@ -18,7 +18,7 @@ class TLRegisterNode(address: AddressSet, concurrency: Int = 0, beatBytes: Int = | |||||||
|       supportsPutFull    = TransferSizes(1, beatBytes), |       supportsPutFull    = TransferSizes(1, beatBytes), | ||||||
|       fifoId             = Some(0))), // requests are handled in order |       fifoId             = Some(0))), // requests are handled in order | ||||||
|     beatBytes  = beatBytes, |     beatBytes  = beatBytes, | ||||||
|     minLatency = min(concurrency, 1))) // the Queue adds at most one cycle |     minLatency = min(concurrency, 1)))) // the Queue adds at most one cycle | ||||||
| { | { | ||||||
|   require (address.contiguous) |   require (address.contiguous) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -8,7 +8,7 @@ import diplomacy._ | |||||||
|  |  | ||||||
| class TLRAM(address: AddressSet, executable: Boolean = true, beatBytes: Int = 4)(implicit p: Parameters) extends LazyModule | class TLRAM(address: AddressSet, executable: Boolean = true, beatBytes: Int = 4)(implicit p: Parameters) extends LazyModule | ||||||
| { | { | ||||||
|   val node = TLManagerNode(TLManagerPortParameters( |   val node = TLManagerNode(Seq(TLManagerPortParameters( | ||||||
|     Seq(TLManagerParameters( |     Seq(TLManagerParameters( | ||||||
|       address            = List(address), |       address            = List(address), | ||||||
|       regionType         = RegionType.UNCACHED, |       regionType         = RegionType.UNCACHED, | ||||||
| @@ -18,7 +18,7 @@ class TLRAM(address: AddressSet, executable: Boolean = true, beatBytes: Int = 4) | |||||||
|       supportsPutFull    = TransferSizes(1, beatBytes), |       supportsPutFull    = TransferSizes(1, beatBytes), | ||||||
|       fifoId             = Some(0))), // requests are handled in order |       fifoId             = Some(0))), // requests are handled in order | ||||||
|     beatBytes  = beatBytes, |     beatBytes  = beatBytes, | ||||||
|     minLatency = 1)) // no bypass needed for this device |     minLatency = 1))) // no bypass needed for this device | ||||||
|  |  | ||||||
|   // We require the address range to include an entire beat (for the write mask) |   // We require the address range to include an entire beat (for the write mask) | ||||||
|   require ((address.mask & (beatBytes-1)) == beatBytes-1) |   require ((address.mask & (beatBytes-1)) == beatBytes-1) | ||||||
|   | |||||||
| @@ -15,8 +15,8 @@ class TLSourceShrinker(maxInFlight: Int)(implicit p: Parameters) extends LazyMod | |||||||
|   private val client = TLClientParameters(sourceId = IdRange(0, maxInFlight)) |   private val client = TLClientParameters(sourceId = IdRange(0, maxInFlight)) | ||||||
|   val node = TLAdapterNode( |   val node = TLAdapterNode( | ||||||
|     // We erase all client information since we crush the source Ids |     // We erase all client information since we crush the source Ids | ||||||
|     clientFn  = { case _ => TLClientPortParameters(clients = Seq(client)) }, |     clientFn  = { _ => TLClientPortParameters(clients = Seq(client)) }, | ||||||
|     managerFn = { case Seq(mp) => mp }) |     managerFn = { mp => mp }) | ||||||
|  |  | ||||||
|   lazy val module = new LazyModuleImp(this) { |   lazy val module = new LazyModuleImp(this) { | ||||||
|     val io = new Bundle { |     val io = new Bundle { | ||||||
| @@ -24,54 +24,51 @@ class TLSourceShrinker(maxInFlight: Int)(implicit p: Parameters) extends LazyMod | |||||||
|       val out = node.bundleOut |       val out = node.bundleOut | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     val edgeIn = node.edgesIn(0) |     ((io.in zip io.out) zip (node.edgesIn zip node.edgesOut)) foreach { case ((in, out), (edgeIn, edgeOut)) => | ||||||
|     val edgeOut = node.edgesOut(0) |       // Acquires cannot pass this adapter; it makes Probes impossible | ||||||
|     val in = io.in(0) |       require (!edgeIn.client.anySupportProbe ||  | ||||||
|     val out = io.out(0) |                !edgeOut.manager.anySupportAcquireB) | ||||||
|  |  | ||||||
|     // Acquires cannot pass this adapter; it makes Probes impossible |       out.b.ready := Bool(true) | ||||||
|     require (!edgeIn.client.anySupportProbe ||  |       out.c.valid := Bool(false) | ||||||
|              !edgeOut.manager.anySupportAcquireB) |       out.e.valid := Bool(false) | ||||||
|  |       in.b.valid := Bool(false) | ||||||
|  |       in.c.ready := Bool(true) | ||||||
|  |       in.e.ready := Bool(true) | ||||||
|  |  | ||||||
|     out.b.ready := Bool(true) |       if (maxInFlight >= edgeIn.client.endSourceId) { | ||||||
|     out.c.valid := Bool(false) |         out.a <> in.a | ||||||
|     out.e.valid := Bool(false) |         in.d <> out.d | ||||||
|     in.b.valid := Bool(false) |       } else { | ||||||
|     in.c.ready := Bool(true) |         // State tracking | ||||||
|     in.e.ready := Bool(true) |         val sourceIdMap = Mem(maxInFlight, in.a.bits.source) | ||||||
|  |         val allocated = RegInit(UInt(0, width = maxInFlight)) | ||||||
|  |         val nextFreeOH = ~(leftOR(~allocated) << 1) & ~allocated | ||||||
|  |         val nextFree = OHToUInt(nextFreeOH) | ||||||
|  |         val full = allocated.andR() | ||||||
|  |  | ||||||
|     if (maxInFlight >= edgeIn.client.endSourceId) { |         val a_first = edgeIn.first(in.a) | ||||||
|       out.a <> in.a |         val d_last  = edgeIn.last(in.d) | ||||||
|       in.d <> out.d |  | ||||||
|     } else { |  | ||||||
|       // State tracking |  | ||||||
|       val sourceIdMap = Mem(maxInFlight, in.a.bits.source) |  | ||||||
|       val allocated = RegInit(UInt(0, width = maxInFlight)) |  | ||||||
|       val nextFreeOH = ~(leftOR(~allocated) << 1) & ~allocated |  | ||||||
|       val nextFree = OHToUInt(nextFreeOH) |  | ||||||
|       val full = allocated.andR() |  | ||||||
|  |  | ||||||
|       val a_first = edgeIn.first(in.a) |         val block = a_first && full | ||||||
|       val d_last  = edgeIn.last(in.d) |         in.a.ready := out.a.ready && !block | ||||||
|  |         out.a.valid := in.a.valid && !block | ||||||
|  |         out.a.bits := in.a.bits | ||||||
|  |         out.a.bits.source := holdUnless(nextFree, a_first) | ||||||
|  |  | ||||||
|       val block = a_first && full |         in.d <> out.d | ||||||
|       in.a.ready := out.a.ready && !block |         in.d.bits.source := sourceIdMap(out.d.bits.source) | ||||||
|       out.a.valid := in.a.valid && !block |  | ||||||
|       out.a.bits := in.a.bits |  | ||||||
|       out.a.bits.source := holdUnless(nextFree, a_first) |  | ||||||
|  |  | ||||||
|       in.d <> out.d |         when (a_first && in.a.fire()) { | ||||||
|       in.d.bits.source := sourceIdMap(out.d.bits.source) |           sourceIdMap(nextFree) := in.a.bits.source | ||||||
|  |         } | ||||||
|  |  | ||||||
|       when (a_first && in.a.fire()) { |         val alloc = a_first && in.a.fire() | ||||||
|         sourceIdMap(nextFree) := in.a.bits.source |         val free = d_last && in.d.fire() | ||||||
|  |         val alloc_id = Mux(alloc, nextFreeOH, UInt(0)) | ||||||
|  |         val free_id = Mux(free, UIntToOH(out.d.bits.source), UInt(0)) | ||||||
|  |         allocated := (allocated | alloc_id) & ~free_id | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       val alloc = a_first && in.a.fire() |  | ||||||
|       val free = d_last && in.d.fire() |  | ||||||
|       val alloc_id = Mux(alloc, nextFreeOH, UInt(0)) |  | ||||||
|       val free_id = Mux(free, UIntToOH(out.d.bits.source), UInt(0)) |  | ||||||
|       allocated := (allocated | alloc_id) & ~free_id |  | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -10,12 +10,12 @@ import uncore.ahb._ | |||||||
| import scala.math.{min, max} | import scala.math.{min, max} | ||||||
| import AHBParameters._ | import AHBParameters._ | ||||||
|  |  | ||||||
| case class TLToAHBNode() extends MixedNode(TLImp, AHBImp)( | case class TLToAHBNode() extends MixedAdapterNode(TLImp, AHBImp)( | ||||||
|   dFn = { case (1, Seq(TLClientPortParameters(clients, unsafeAtomics, minLatency))) => |   dFn = { case TLClientPortParameters(clients, unsafeAtomics, minLatency) => | ||||||
|     val masters = clients.map { case c => AHBMasterParameters(nodePath = c.nodePath) } |     val masters = clients.map { case c => AHBMasterParameters(nodePath = c.nodePath) } | ||||||
|     Seq(AHBMasterPortParameters(masters)) |     AHBMasterPortParameters(masters) | ||||||
|   }, |   }, | ||||||
|   uFn = { case (1, Seq(AHBSlavePortParameters(slaves, beatBytes))) => |   uFn = { case AHBSlavePortParameters(slaves, beatBytes) => | ||||||
|     val managers = slaves.map { case s => |     val managers = slaves.map { case s => | ||||||
|       TLManagerParameters( |       TLManagerParameters( | ||||||
|         address            = s.address, |         address            = s.address, | ||||||
| @@ -26,10 +26,8 @@ case class TLToAHBNode() extends MixedNode(TLImp, AHBImp)( | |||||||
|         supportsPutFull    = s.supportsWrite, // but not PutPartial |         supportsPutFull    = s.supportsWrite, // but not PutPartial | ||||||
|         fifoId             = Some(0)) // a common FIFO domain |         fifoId             = Some(0)) // a common FIFO domain | ||||||
|     } |     } | ||||||
|     Seq(TLManagerPortParameters(managers, beatBytes, 1, 1)) |     TLManagerPortParameters(managers, beatBytes, 1, 1) | ||||||
|   }, |   }) | ||||||
|   numPO = 1 to 1, |  | ||||||
|   numPI = 1 to 1) |  | ||||||
|  |  | ||||||
| class TLToAHB(combinational: Boolean = true)(implicit p: Parameters) extends LazyModule | class TLToAHB(combinational: Boolean = true)(implicit p: Parameters) extends LazyModule | ||||||
| { | { | ||||||
| @@ -41,91 +39,89 @@ class TLToAHB(combinational: Boolean = true)(implicit p: Parameters) extends Laz | |||||||
|       val out = node.bundleOut |       val out = node.bundleOut | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     val in = io.in(0) |     ((io.in zip io.out) zip (node.edgesIn zip node.edgesOut)) foreach { case ((in, out), (edgeIn, edgeOut)) => | ||||||
|     val out = io.out(0) |       val beatBytes = edgeOut.slave.beatBytes | ||||||
|     val edgeIn  = node.edgesIn(0) |       val maxTransfer = edgeOut.slave.maxTransfer | ||||||
|     val edgeOut = node.edgesOut(0) |       val lgMax = log2Ceil(maxTransfer) | ||||||
|     val beatBytes = edgeOut.slave.beatBytes |       val lgBytes = log2Ceil(beatBytes) | ||||||
|     val maxTransfer = edgeOut.slave.maxTransfer |  | ||||||
|     val lgMax = log2Ceil(maxTransfer) |  | ||||||
|     val lgBytes = log2Ceil(beatBytes) |  | ||||||
|  |  | ||||||
|     // AHB has no cache coherence |       // AHB has no cache coherence | ||||||
|     in.b.valid := Bool(false) |       in.b.valid := Bool(false) | ||||||
|     in.c.ready := Bool(true) |       in.c.ready := Bool(true) | ||||||
|     in.e.ready := Bool(true) |       in.e.ready := Bool(true) | ||||||
|  |  | ||||||
|     // We need a skidpad to capture D output: |       // We need a skidpad to capture D output: | ||||||
|     // We cannot know if the D response will be accepted until we have |       // We cannot know if the D response will be accepted until we have | ||||||
|     // presented it on D as valid.  We also can't back-pressure AHB in the |       // presented it on D as valid.  We also can't back-pressure AHB in the | ||||||
|     // data phase.  Therefore, we must have enough space to save the data |       // data phase.  Therefore, we must have enough space to save the data | ||||||
|     // phase result.  Whenever we have a queued response, we can not allow |       // phase result.  Whenever we have a queued response, we can not allow | ||||||
|     // AHB to present new responses, so we must quash the address phase. |       // AHB to present new responses, so we must quash the address phase. | ||||||
|     val d = Wire(in.d) |       val d = Wire(in.d) | ||||||
|     in.d <> Queue(d, 1, flow = true) |       in.d <> Queue(d, 1, flow = true) | ||||||
|     val a_quash = in.d.valid && !in.d.ready |       val a_quash = in.d.valid && !in.d.ready | ||||||
|  |  | ||||||
|     // Record what is coming out in d_phase |       // Record what is coming out in d_phase | ||||||
|     val d_valid   = RegInit(Bool(false)) |       val d_valid   = RegInit(Bool(false)) | ||||||
|     val d_hasData = Reg(Bool()) |       val d_hasData = Reg(Bool()) | ||||||
|     val d_error   = Reg(Bool()) |       val d_error   = Reg(Bool()) | ||||||
|     val d_addr_lo = Reg(UInt(width = lgBytes)) |       val d_addr_lo = Reg(UInt(width = lgBytes)) | ||||||
|     val d_source  = Reg(UInt()) |       val d_source  = Reg(UInt()) | ||||||
|     val d_size    = Reg(UInt()) |       val d_size    = Reg(UInt()) | ||||||
|  |  | ||||||
|     when (out.hreadyout) { d_error := d_error || out.hresp } |       when (out.hreadyout) { d_error := d_error || out.hresp } | ||||||
|     when (d.fire()) { d_valid := Bool(false) } |       when (d.fire()) { d_valid := Bool(false) } | ||||||
|  |  | ||||||
|     d.valid := d_valid && out.hreadyout |       d.valid := d_valid && out.hreadyout | ||||||
|     d.bits  := edgeIn.AccessAck(d_addr_lo, UInt(0), d_source, d_size, out.hrdata, out.hresp || d_error) |       d.bits  := edgeIn.AccessAck(d_addr_lo, UInt(0), d_source, d_size, out.hrdata, out.hresp || d_error) | ||||||
|     d.bits.opcode := Mux(d_hasData, TLMessages.AccessAckData, TLMessages.AccessAck) |       d.bits.opcode := Mux(d_hasData, TLMessages.AccessAckData, TLMessages.AccessAck) | ||||||
|  |  | ||||||
|     // We need an irrevocable input for AHB to stall on read bursts |       // We need an irrevocable input for AHB to stall on read bursts | ||||||
|     // We also need the values to NOT change when valid goes low => 1 entry only |       // We also need the values to NOT change when valid goes low => 1 entry only | ||||||
|     val a = Queue(in.a, 1, flow = combinational, pipe = !combinational) |       val a = Queue(in.a, 1, flow = combinational, pipe = !combinational) | ||||||
|     val a_valid = a.valid && !a_quash |       val a_valid = a.valid && !a_quash | ||||||
|  |  | ||||||
|     // This is lot like TLEdge.firstlast, but counts beats also for single-beat TL types |       // This is lot like TLEdge.firstlast, but counts beats also for single-beat TL types | ||||||
|     val a_size = edgeIn.size(a.bits) |       val a_size = edgeIn.size(a.bits) | ||||||
|     val a_beats1 = UIntToOH1(a_size, lgMax) >> lgBytes |       val a_beats1 = UIntToOH1(a_size, lgMax) >> lgBytes | ||||||
|     val a_counter = RegInit(UInt(0, width = log2Up(maxTransfer/beatBytes))) |       val a_counter = RegInit(UInt(0, width = log2Up(maxTransfer/beatBytes))) | ||||||
|     val a_counter1 = a_counter - UInt(1) |       val a_counter1 = a_counter - UInt(1) | ||||||
|     val a_first = a_counter === UInt(0) |       val a_first = a_counter === UInt(0) | ||||||
|     val a_last = a_counter === UInt(1) || a_beats1 === UInt(0) |       val a_last = a_counter === UInt(1) || a_beats1 === UInt(0) | ||||||
|     val a_offset = (a_beats1 & ~a_counter1) << lgBytes |       val a_offset = (a_beats1 & ~a_counter1) << lgBytes | ||||||
|     val a_hasData = edgeIn.hasData(a.bits) |       val a_hasData = edgeIn.hasData(a.bits) | ||||||
|  |  | ||||||
|     // Expand no-data A-channel requests into multiple beats |       // Expand no-data A-channel requests into multiple beats | ||||||
|     a.ready := (a_hasData || a_last) && out.hreadyout && !a_quash |       a.ready := (a_hasData || a_last) && out.hreadyout && !a_quash | ||||||
|     when (a_valid && out.hreadyout) { |       when (a_valid && out.hreadyout) { | ||||||
|       a_counter := Mux(a_first, a_beats1, a_counter1) |         a_counter := Mux(a_first, a_beats1, a_counter1) | ||||||
|       d_valid := !a_hasData || a_last |         d_valid := !a_hasData || a_last | ||||||
|       // Record what will be in the data phase |         // Record what will be in the data phase | ||||||
|       when (a_first) { |         when (a_first) { | ||||||
|         d_hasData := !a_hasData |           d_hasData := !a_hasData | ||||||
|         d_error   := Bool(false) |           d_error   := Bool(false) | ||||||
|         d_addr_lo := a.bits.address |           d_addr_lo := a.bits.address | ||||||
|         d_source  := a.bits.source |           d_source  := a.bits.source | ||||||
|         d_size    := a.bits.size |           d_size    := a.bits.size | ||||||
|  |         } | ||||||
|       } |       } | ||||||
|  |  | ||||||
|  |       // Transform TL size into AHB hsize+hburst | ||||||
|  |       val a_size_bits = a_size.getWidth | ||||||
|  |       val a_sizeDelta = Cat(UInt(0, width = 1), a_size) - UInt(lgBytes+1) | ||||||
|  |       val a_singleBeat = a_sizeDelta(a_size_bits) | ||||||
|  |       val a_logBeats1 = a_sizeDelta(a_size_bits-1, 0) | ||||||
|  |  | ||||||
|  |       out.hmastlock := Bool(false) // for now | ||||||
|  |       out.htrans    := Mux(a_valid, Mux(a_first, TRANS_NONSEQ, TRANS_SEQ), Mux(a_first, TRANS_IDLE, TRANS_BUSY)) | ||||||
|  |       out.hsel      := a_valid || !a_first | ||||||
|  |       out.hready    := out.hreadyout | ||||||
|  |       out.hwrite    := a_hasData | ||||||
|  |       out.haddr     := a.bits.address | a_offset | ||||||
|  |       out.hsize     := Mux(a_singleBeat, a.bits.size, UInt(lgBytes)) | ||||||
|  |       out.hburst    := Mux(a_singleBeat, BURST_SINGLE, (a_logBeats1<<1) | UInt(1)) | ||||||
|  |       out.hprot     := PROT_DEFAULT | ||||||
|  |       out.hwdata    := RegEnable(a.bits.data, a.fire()) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // Transform TL size into AHB hsize+hburst |  | ||||||
|     val a_size_bits = a_size.getWidth |  | ||||||
|     val a_sizeDelta = Cat(UInt(0, width = 1), a_size) - UInt(lgBytes+1) |  | ||||||
|     val a_singleBeat = a_sizeDelta(a_size_bits) |  | ||||||
|     val a_logBeats1 = a_sizeDelta(a_size_bits-1, 0) |  | ||||||
|  |  | ||||||
|     out.hmastlock := Bool(false) // for now |  | ||||||
|     out.htrans    := Mux(a_valid, Mux(a_first, TRANS_NONSEQ, TRANS_SEQ), Mux(a_first, TRANS_IDLE, TRANS_BUSY)) |  | ||||||
|     out.hsel      := a_valid || !a_first |  | ||||||
|     out.hready    := out.hreadyout |  | ||||||
|     out.hwrite    := a_hasData |  | ||||||
|     out.haddr     := a.bits.address | a_offset |  | ||||||
|     out.hsize     := Mux(a_singleBeat, a.bits.size, UInt(lgBytes)) |  | ||||||
|     out.hburst    := Mux(a_singleBeat, BURST_SINGLE, (a_logBeats1<<1) | UInt(1)) |  | ||||||
|     out.hprot     := PROT_DEFAULT |  | ||||||
|     out.hwdata    := RegEnable(a.bits.data, a.fire()) |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -10,12 +10,12 @@ import uncore.apb._ | |||||||
| import scala.math.{min, max} | import scala.math.{min, max} | ||||||
| import APBParameters._ | import APBParameters._ | ||||||
|  |  | ||||||
| case class TLToAPBNode() extends MixedNode(TLImp, APBImp)( | case class TLToAPBNode() extends MixedAdapterNode(TLImp, APBImp)( | ||||||
|   dFn = { case (1, Seq(TLClientPortParameters(clients, unsafeAtomics, minLatency))) => |   dFn = { case TLClientPortParameters(clients, unsafeAtomics, minLatency) => | ||||||
|     val masters = clients.map { case c => APBMasterParameters(nodePath = c.nodePath) } |     val masters = clients.map { case c => APBMasterParameters(nodePath = c.nodePath) } | ||||||
|     Seq(APBMasterPortParameters(masters)) |     APBMasterPortParameters(masters) | ||||||
|   }, |   }, | ||||||
|   uFn = { case (1, Seq(APBSlavePortParameters(slaves, beatBytes))) => |   uFn = { case APBSlavePortParameters(slaves, beatBytes) => | ||||||
|     val managers = slaves.map { case s => |     val managers = slaves.map { case s => | ||||||
|       TLManagerParameters( |       TLManagerParameters( | ||||||
|         address            = s.address, |         address            = s.address, | ||||||
| @@ -27,10 +27,8 @@ case class TLToAPBNode() extends MixedNode(TLImp, APBImp)( | |||||||
|         supportsPutFull    = if (s.supportsWrite) TransferSizes(1, beatBytes) else TransferSizes.none, |         supportsPutFull    = if (s.supportsWrite) TransferSizes(1, beatBytes) else TransferSizes.none, | ||||||
|         fifoId             = Some(0)) // a common FIFO domain |         fifoId             = Some(0)) // a common FIFO domain | ||||||
|     } |     } | ||||||
|     Seq(TLManagerPortParameters(managers, beatBytes, 1, 0)) |     TLManagerPortParameters(managers, beatBytes, 1, 0) | ||||||
|   }, |   }) | ||||||
|   numPO = 1 to 1, |  | ||||||
|   numPI = 1 to 1) |  | ||||||
|  |  | ||||||
| class TLToAPB(combinational: Boolean = true)(implicit p: Parameters) extends LazyModule | class TLToAPB(combinational: Boolean = true)(implicit p: Parameters) extends LazyModule | ||||||
| { | { | ||||||
| @@ -42,51 +40,49 @@ class TLToAPB(combinational: Boolean = true)(implicit p: Parameters) extends Laz | |||||||
|       val out = node.bundleOut |       val out = node.bundleOut | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     val in = io.in(0) |     ((io.in zip io.out) zip (node.edgesIn zip node.edgesOut)) foreach { case ((in, out), (edgeIn, edgeOut)) => | ||||||
|     val out = io.out(0) |       val beatBytes = edgeOut.slave.beatBytes | ||||||
|     val edgeIn  = node.edgesIn(0) |       val lgBytes = log2Ceil(beatBytes) | ||||||
|     val edgeOut = node.edgesOut(0) |  | ||||||
|     val beatBytes = edgeOut.slave.beatBytes |  | ||||||
|     val lgBytes = log2Ceil(beatBytes) |  | ||||||
|  |  | ||||||
|     // APB has no cache coherence |       // APB has no cache coherence | ||||||
|     in.b.valid := Bool(false) |       in.b.valid := Bool(false) | ||||||
|     in.c.ready := Bool(true) |       in.c.ready := Bool(true) | ||||||
|     in.e.ready := Bool(true) |       in.e.ready := Bool(true) | ||||||
|  |  | ||||||
|     // We need a skidpad to capture D output: |       // We need a skidpad to capture D output: | ||||||
|     // We cannot know if the D response will be accepted until we have |       // We cannot know if the D response will be accepted until we have | ||||||
|     // presented it on D as valid.  We also can't back-pressure APB in the |       // presented it on D as valid.  We also can't back-pressure APB in the | ||||||
|     // data phase.  Therefore, we must have enough space to save the data |       // data phase.  Therefore, we must have enough space to save the data | ||||||
|     // phase result.  Whenever we have a queued response, we can not allow |       // phase result.  Whenever we have a queued response, we can not allow | ||||||
|     // APB to present new responses, so we must quash the address phase. |       // APB to present new responses, so we must quash the address phase. | ||||||
|     val d = Wire(in.d) |       val d = Wire(in.d) | ||||||
|     in.d <> Queue(d, 1, flow = true) |       in.d <> Queue(d, 1, flow = true) | ||||||
|  |  | ||||||
|     // We need an irrevocable input for APB to stall |       // We need an irrevocable input for APB to stall | ||||||
|     val a = Queue(in.a, 1, flow = combinational, pipe = !combinational) |       val a = Queue(in.a, 1, flow = combinational, pipe = !combinational) | ||||||
|  |  | ||||||
|     val a_enable = RegInit(Bool(false)) |       val a_enable = RegInit(Bool(false)) | ||||||
|     val a_sel    = a.valid && RegNext(!in.d.valid || in.d.ready) |       val a_sel    = a.valid && RegNext(!in.d.valid || in.d.ready) | ||||||
|     val a_write  = edgeIn.hasData(a.bits) |       val a_write  = edgeIn.hasData(a.bits) | ||||||
|  |  | ||||||
|     when (a_sel)    { a_enable := Bool(true) } |       when (a_sel)    { a_enable := Bool(true) } | ||||||
|     when (d.fire()) { a_enable := Bool(false) } |       when (d.fire()) { a_enable := Bool(false) } | ||||||
|  |  | ||||||
|     out.psel    := a_sel |       out.psel    := a_sel | ||||||
|     out.penable := a_enable |       out.penable := a_enable | ||||||
|     out.pwrite  := a_write |       out.pwrite  := a_write | ||||||
|     out.paddr   := a.bits.address |       out.paddr   := a.bits.address | ||||||
|     out.pprot   := PROT_DEFAULT |       out.pprot   := PROT_DEFAULT | ||||||
|     out.pwdata  := a.bits.data |       out.pwdata  := a.bits.data | ||||||
|     out.pstrb   := Mux(a_write, a.bits.mask, UInt(0)) |       out.pstrb   := Mux(a_write, a.bits.mask, UInt(0)) | ||||||
|  |  | ||||||
|     a.ready := a_enable && out.pready |       a.ready := a_enable && out.pready | ||||||
|     d.valid := a_enable && out.pready |       d.valid := a_enable && out.pready | ||||||
|     assert (!d.valid || d.ready) |       assert (!d.valid || d.ready) | ||||||
|  |  | ||||||
|     d.bits := edgeIn.AccessAck(a.bits, UInt(0), out.prdata, out.pslverr) |       d.bits := edgeIn.AccessAck(a.bits, UInt(0), out.prdata, out.pslverr) | ||||||
|     d.bits.opcode := Mux(a_write, TLMessages.AccessAck, TLMessages.AccessAckData) |       d.bits.opcode := Mux(a_write, TLMessages.AccessAck, TLMessages.AccessAckData) | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -10,16 +10,16 @@ import util.PositionalMultiQueue | |||||||
| import uncore.axi4._ | import uncore.axi4._ | ||||||
| import scala.math.{min, max} | import scala.math.{min, max} | ||||||
|  |  | ||||||
| case class TLToAXI4Node(idBits: Int) extends MixedNode(TLImp, AXI4Imp)( | case class TLToAXI4Node(idBits: Int) extends MixedAdapterNode(TLImp, AXI4Imp)( | ||||||
|   dFn = { case (1, _) => |   dFn = { _ => | ||||||
|     // We must erase all client information, because we crush their source Ids |     // We must erase all client information, because we crush their source Ids | ||||||
|     val masters = Seq( |     val masters = Seq( | ||||||
|       AXI4MasterParameters( |       AXI4MasterParameters( | ||||||
|         id      = IdRange(0, 1 << idBits), |         id      = IdRange(0, 1 << idBits), | ||||||
|         aligned = true)) |         aligned = true)) | ||||||
|     Seq(AXI4MasterPortParameters(masters)) |     AXI4MasterPortParameters(masters) | ||||||
|   }, |   }, | ||||||
|   uFn = { case (1, Seq(p)) => Seq(TLManagerPortParameters( |   uFn = { p => TLManagerPortParameters( | ||||||
|     managers = p.slaves.map { case s => |     managers = p.slaves.map { case s => | ||||||
|       TLManagerParameters( |       TLManagerParameters( | ||||||
|         address            = s.address, |         address            = s.address, | ||||||
| @@ -31,10 +31,8 @@ case class TLToAXI4Node(idBits: Int) extends MixedNode(TLImp, AXI4Imp)( | |||||||
|         supportsPutPartial = s.supportsWrite)}, |         supportsPutPartial = s.supportsWrite)}, | ||||||
|         // AXI4 is NEVER fifo in TL sense (R+W are independent) |         // AXI4 is NEVER fifo in TL sense (R+W are independent) | ||||||
|       beatBytes = p.beatBytes, |       beatBytes = p.beatBytes, | ||||||
|       minLatency = p.minLatency)) |       minLatency = p.minLatency) | ||||||
|   }, |   }) | ||||||
|   numPO = 1 to 1, |  | ||||||
|   numPI = 1 to 1) |  | ||||||
|  |  | ||||||
| class TLToAXI4(idBits: Int, combinational: Boolean = true)(implicit p: Parameters) extends LazyModule | class TLToAXI4(idBits: Int, combinational: Boolean = true)(implicit p: Parameters) extends LazyModule | ||||||
| { | { | ||||||
| @@ -46,185 +44,182 @@ class TLToAXI4(idBits: Int, combinational: Boolean = true)(implicit p: Parameter | |||||||
|       val out = node.bundleOut |       val out = node.bundleOut | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     val in = io.in(0) |     ((io.in zip io.out) zip (node.edgesIn zip node.edgesOut)) foreach { case ((in, out), (edgeIn, edgeOut)) => | ||||||
|     val out = io.out(0) |       val slaves  = edgeOut.slave.slaves | ||||||
|  |  | ||||||
|     val edgeIn  = node.edgesIn(0) |       // All pairs of slaves must promise that they will never interleave data | ||||||
|     val edgeOut = node.edgesOut(0) |       require (slaves(0).interleavedId.isDefined) | ||||||
|     val slaves  = edgeOut.slave.slaves |       slaves.foreach { s => require (s.interleavedId == slaves(0).interleavedId) } | ||||||
|  |  | ||||||
|     // All pairs of slaves must promise that they will never interleave data |       // We need to ensure that a slave does not stall trying to send B while we need to receive R | ||||||
|     require (slaves(0).interleavedId.isDefined) |       // Since R&W have independent flow control, it is possible for a W to cut in-line and get into | ||||||
|     slaves.foreach { s => require (s.interleavedId == slaves(0).interleavedId) } |       // a slave's buffers, preventing us from getting all the R responses we need to release D for B. | ||||||
|  |       // This risk is compounded by an AXI fragmentation. Even a slave which responds completely to | ||||||
|  |       // AR before working on AW might have an AW slipped between two AR fragments. | ||||||
|  |       val out_b = Queue.irrevocable(out.b, entries=edgeIn.client.endSourceId, flow=combinational) | ||||||
|  |  | ||||||
|     // We need to ensure that a slave does not stall trying to send B while we need to receive R |       // We need to keep the following state from A => D: (addr_lo, size, source) | ||||||
|     // Since R&W have independent flow control, it is possible for a W to cut in-line and get into |       // All of those fields could potentially require 0 bits (argh. Chisel.) | ||||||
|     // a slave's buffers, preventing us from getting all the R responses we need to release D for B. |       // We will pack as many of the lowest bits of state as fit into the AXI ID. | ||||||
|     // This risk is compounded by an AXI fragmentation. Even a slave which responds completely to |       // Any bits left-over must be put into a bank of Queues. | ||||||
|     // AR before working on AW might have an AW slipped between two AR fragments. |       // The Queues are indexed by as many of the source bits as fit into the AXI ID. | ||||||
|     val out_b = Queue.irrevocable(out.b, entries=edgeIn.client.endSourceId, flow=combinational) |       // The Queues are deep enough that every source has guaranteed space in its Queue. | ||||||
|  |  | ||||||
|     // We need to keep the following state from A => D: (addr_lo, size, source) |       val sourceBits = log2Ceil(edgeIn.client.endSourceId) | ||||||
|     // All of those fields could potentially require 0 bits (argh. Chisel.) |       val sizeBits = log2Ceil(edgeIn.maxLgSize+1) | ||||||
|     // We will pack as many of the lowest bits of state as fit into the AXI ID. |       val addrBits = log2Ceil(edgeIn.manager.beatBytes) | ||||||
|     // Any bits left-over must be put into a bank of Queues. |       val stateBits = addrBits + sizeBits + sourceBits // could be 0 | ||||||
|     // The Queues are indexed by as many of the source bits as fit into the AXI ID. |  | ||||||
|     // The Queues are deep enough that every source has guaranteed space in its Queue. |  | ||||||
|  |  | ||||||
|     val sourceBits = log2Ceil(edgeIn.client.endSourceId) |       val a_address = edgeIn.address(in.a.bits) | ||||||
|     val sizeBits = log2Ceil(edgeIn.maxLgSize+1) |       val a_addr_lo = edgeIn.addr_lo(a_address) | ||||||
|     val addrBits = log2Ceil(edgeIn.manager.beatBytes) |       val a_source  = in.a.bits.source | ||||||
|     val stateBits = addrBits + sizeBits + sourceBits // could be 0 |       val a_size    = edgeIn.size(in.a.bits) | ||||||
|  |       val a_isPut   = edgeIn.hasData(in.a.bits) | ||||||
|  |       val a_last    = edgeIn.last(in.a) | ||||||
|  |  | ||||||
|     val a_address = edgeIn.address(in.a.bits) |       // Make sure the fields are within the bounds we assumed | ||||||
|     val a_addr_lo = edgeIn.addr_lo(a_address) |       assert (a_source  < UInt(BigInt(1) << sourceBits)) | ||||||
|     val a_source  = in.a.bits.source |       assert (a_size    < UInt(BigInt(1) << sizeBits)) | ||||||
|     val a_size    = edgeIn.size(in.a.bits) |       assert (a_addr_lo < UInt(BigInt(1) << addrBits)) | ||||||
|     val a_isPut   = edgeIn.hasData(in.a.bits) |  | ||||||
|     val a_last    = edgeIn.last(in.a) |  | ||||||
|  |  | ||||||
|     // Make sure the fields are within the bounds we assumed |       // Carefully pack/unpack fields into the state we send | ||||||
|     assert (a_source  < UInt(BigInt(1) << sourceBits)) |       val baseEnd = 0 | ||||||
|     assert (a_size    < UInt(BigInt(1) << sizeBits)) |       val (sourceEnd, sourceOff) = (sourceBits + baseEnd,   baseEnd) | ||||||
|     assert (a_addr_lo < UInt(BigInt(1) << addrBits)) |       val (sizeEnd,   sizeOff)   = (sizeBits   + sourceEnd, sourceEnd) | ||||||
|  |       val (addrEnd,   addrOff)   = (addrBits   + sizeEnd,   sizeEnd) | ||||||
|  |       require (addrEnd == stateBits) | ||||||
|  |  | ||||||
|     // Carefully pack/unpack fields into the state we send |       val a_state = (a_source << sourceOff) | (a_size << sizeOff) | (a_addr_lo << addrOff) | ||||||
|     val baseEnd = 0 |       val a_id = if (idBits == 0) UInt(0) else a_state | ||||||
|     val (sourceEnd, sourceOff) = (sourceBits + baseEnd,   baseEnd) |  | ||||||
|     val (sizeEnd,   sizeOff)   = (sizeBits   + sourceEnd, sourceEnd) |  | ||||||
|     val (addrEnd,   addrOff)   = (addrBits   + sizeEnd,   sizeEnd) |  | ||||||
|     require (addrEnd == stateBits) |  | ||||||
|  |  | ||||||
|     val a_state = (a_source << sourceOff) | (a_size << sizeOff) | (a_addr_lo << addrOff) |       val r_state = Wire(UInt(width = stateBits)) | ||||||
|     val a_id = if (idBits == 0) UInt(0) else a_state |       val r_source  = if (sourceBits > 0) r_state(sourceEnd-1, sourceOff) else UInt(0) | ||||||
|  |       val r_size    = if (sizeBits   > 0) r_state(sizeEnd  -1, sizeOff)   else UInt(0) | ||||||
|  |       val r_addr_lo = if (addrBits   > 0) r_state(addrEnd  -1, addrOff)   else UInt(0) | ||||||
|  |  | ||||||
|     val r_state = Wire(UInt(width = stateBits)) |       val b_state = Wire(UInt(width = stateBits)) | ||||||
|     val r_source  = if (sourceBits > 0) r_state(sourceEnd-1, sourceOff) else UInt(0) |       val b_source  = if (sourceBits > 0) b_state(sourceEnd-1, sourceOff) else UInt(0) | ||||||
|     val r_size    = if (sizeBits   > 0) r_state(sizeEnd  -1, sizeOff)   else UInt(0) |       val b_size    = if (sizeBits   > 0) b_state(sizeEnd  -1, sizeOff)   else UInt(0) | ||||||
|     val r_addr_lo = if (addrBits   > 0) r_state(addrEnd  -1, addrOff)   else UInt(0) |       val b_addr_lo = if (addrBits   > 0) b_state(addrEnd  -1, addrOff)   else UInt(0) | ||||||
|  |  | ||||||
|     val b_state = Wire(UInt(width = stateBits)) |       val r_last = out.r.bits.last | ||||||
|     val b_source  = if (sourceBits > 0) b_state(sourceEnd-1, sourceOff) else UInt(0) |       val r_id = out.r.bits.id | ||||||
|     val b_size    = if (sizeBits   > 0) b_state(sizeEnd  -1, sizeOff)   else UInt(0) |       val b_id = out_b.bits.id | ||||||
|     val b_addr_lo = if (addrBits   > 0) b_state(addrEnd  -1, addrOff)   else UInt(0) |  | ||||||
|  |  | ||||||
|     val r_last = out.r.bits.last |       if (stateBits <= idBits) { // No need for any state tracking | ||||||
|     val r_id = out.r.bits.id |         r_state := r_id | ||||||
|     val b_id = out_b.bits.id |         b_state := b_id | ||||||
|  |       } else { | ||||||
|  |         val bankIndexBits = min(sourceBits, idBits) | ||||||
|  |         val posBits = max(0, sourceBits - idBits) | ||||||
|  |         val implicitBits = max(idBits, sourceBits) | ||||||
|  |         val bankBits = stateBits - implicitBits | ||||||
|  |         val numBanks = min(1 << bankIndexBits, edgeIn.client.endSourceId) | ||||||
|  |         def bankEntries(i: Int) = (edgeIn.client.endSourceId+numBanks-i-1) / numBanks | ||||||
|  |  | ||||||
|     if (stateBits <= idBits) { // No need for any state tracking |         val banks = Seq.tabulate(numBanks) { i => | ||||||
|       r_state := r_id |           // We know there can only be as many outstanding requests as TL sources | ||||||
|       b_state := b_id |           // However, AXI read and write queues are not mutually FIFO. | ||||||
|     } else { |           // Therefore, we want to pop them individually, but share the storage. | ||||||
|       val bankIndexBits = min(sourceBits, idBits) |           val bypass = combinational && edgeOut.slave.minLatency == 0 | ||||||
|       val posBits = max(0, sourceBits - idBits) |           PositionalMultiQueue(UInt(width=max(1,bankBits)), positions=bankEntries(i), ways=2, combinational=bypass) | ||||||
|       val implicitBits = max(idBits, sourceBits) |         } | ||||||
|       val bankBits = stateBits - implicitBits |  | ||||||
|       val numBanks = min(1 << bankIndexBits, edgeIn.client.endSourceId) |  | ||||||
|       def bankEntries(i: Int) = (edgeIn.client.endSourceId+numBanks-i-1) / numBanks |  | ||||||
|  |  | ||||||
|       val banks = Seq.tabulate(numBanks) { i => |         val a_bankPosition = if (posBits == 0) UInt(0) else a_source(sourceBits-1, idBits) | ||||||
|         // We know there can only be as many outstanding requests as TL sources |         val a_bankIndex = if (bankIndexBits == 0) UInt(0) else a_source(bankIndexBits-1, 0) | ||||||
|         // However, AXI read and write queues are not mutually FIFO. |         val r_bankIndex = if (bankIndexBits == 0) UInt(0) else r_id(bankIndexBits-1, 0) | ||||||
|         // Therefore, we want to pop them individually, but share the storage. |         val b_bankIndex = if (bankIndexBits == 0) UInt(0) else b_id(bankIndexBits-1, 0) | ||||||
|         val bypass = combinational && edgeOut.slave.minLatency == 0 |         val a_bankSelect = UIntToOH(a_bankIndex, numBanks) | ||||||
|         PositionalMultiQueue(UInt(width=max(1,bankBits)), positions=bankEntries(i), ways=2, combinational=bypass) |         val r_bankSelect = UIntToOH(r_bankIndex, numBanks) | ||||||
|  |         val b_bankSelect = UIntToOH(b_bankIndex, numBanks) | ||||||
|  |  | ||||||
|  |         banks.zipWithIndex.foreach { case (q, i) => | ||||||
|  |           // Push a_state into the banks | ||||||
|  |           q.io.enq.valid := in.a.fire() && a_last && a_bankSelect(i) | ||||||
|  |           q.io.enq.bits.pos  := a_bankPosition | ||||||
|  |           q.io.enq.bits.data := a_state >> implicitBits | ||||||
|  |           q.io.enq.bits.way  := Mux(a_isPut, UInt(0), UInt(1)) | ||||||
|  |           // Pop the bank's ways | ||||||
|  |           q.io.deq(0).ready := out_b.fire() && b_bankSelect(i) | ||||||
|  |           q.io.deq(1).ready := out.r.fire() && r_bankSelect(i) && r_last | ||||||
|  |           // The FIFOs must be valid when we're ready to pop them... | ||||||
|  |           assert (q.io.deq(0).valid || !q.io.deq(0).ready) | ||||||
|  |           assert (q.io.deq(1).valid || !q.io.deq(1).ready) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         val b_bankData = Vec(banks.map(_.io.deq(0).bits.data))(b_bankIndex) | ||||||
|  |         val b_bankPos  = Vec(banks.map(_.io.deq(0).bits.pos ))(b_bankIndex) | ||||||
|  |         val r_bankData = Vec(banks.map(_.io.deq(1).bits.data))(r_bankIndex) | ||||||
|  |         val r_bankPos  = Vec(banks.map(_.io.deq(1).bits.pos ))(r_bankIndex) | ||||||
|  |  | ||||||
|  |         def optCat(x: (Boolean, UInt)*) = { Cat(x.toList.filter(_._1).map(_._2)) } | ||||||
|  |         b_state := optCat((bankBits > 0, b_bankData), (posBits > 0, b_bankPos), (idBits > 0, b_id)) | ||||||
|  |         r_state := optCat((bankBits > 0, r_bankData), (posBits > 0, r_bankPos), (idBits > 0, r_id)) | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       val a_bankPosition = if (posBits == 0) UInt(0) else a_source(sourceBits-1, idBits) |       // We need these Queues because AXI4 queues are irrevocable | ||||||
|       val a_bankIndex = if (bankIndexBits == 0) UInt(0) else a_source(bankIndexBits-1, 0) |       val depth = if (combinational) 1 else 2 | ||||||
|       val r_bankIndex = if (bankIndexBits == 0) UInt(0) else r_id(bankIndexBits-1, 0) |       val out_arw = Wire(Decoupled(new AXI4BundleARW(out.params))) | ||||||
|       val b_bankIndex = if (bankIndexBits == 0) UInt(0) else b_id(bankIndexBits-1, 0) |       val out_w = Wire(out.w) | ||||||
|       val a_bankSelect = UIntToOH(a_bankIndex, numBanks) |       out.w <> Queue.irrevocable(out_w, entries=depth, flow=combinational) | ||||||
|       val r_bankSelect = UIntToOH(r_bankIndex, numBanks) |       val queue_arw = Queue.irrevocable(out_arw, entries=depth, flow=combinational) | ||||||
|       val b_bankSelect = UIntToOH(b_bankIndex, numBanks) |  | ||||||
|  |  | ||||||
|       banks.zipWithIndex.foreach { case (q, i) => |       // Fan out the ARW channel to AR and AW | ||||||
|         // Push a_state into the banks |       out.ar.bits := queue_arw.bits | ||||||
|         q.io.enq.valid := in.a.fire() && a_last && a_bankSelect(i) |       out.aw.bits := queue_arw.bits | ||||||
|         q.io.enq.bits.pos  := a_bankPosition |       out.ar.valid := queue_arw.valid && !queue_arw.bits.wen | ||||||
|         q.io.enq.bits.data := a_state >> implicitBits |       out.aw.valid := queue_arw.valid &&  queue_arw.bits.wen | ||||||
|         q.io.enq.bits.way  := Mux(a_isPut, UInt(0), UInt(1)) |       queue_arw.ready := Mux(queue_arw.bits.wen, out.aw.ready, out.ar.ready) | ||||||
|         // Pop the bank's ways |  | ||||||
|         q.io.deq(0).ready := out_b.fire() && b_bankSelect(i) |  | ||||||
|         q.io.deq(1).ready := out.r.fire() && r_bankSelect(i) && r_last |  | ||||||
|         // The FIFOs must be valid when we're ready to pop them... |  | ||||||
|         assert (q.io.deq(0).valid || !q.io.deq(0).ready) |  | ||||||
|         assert (q.io.deq(1).valid || !q.io.deq(1).ready) |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       val b_bankData = Vec(banks.map(_.io.deq(0).bits.data))(b_bankIndex) |       val beatBytes = edgeIn.manager.beatBytes | ||||||
|       val b_bankPos  = Vec(banks.map(_.io.deq(0).bits.pos ))(b_bankIndex) |       val maxSize   = UInt(log2Ceil(beatBytes)) | ||||||
|       val r_bankData = Vec(banks.map(_.io.deq(1).bits.data))(r_bankIndex) |       val doneAW    = RegInit(Bool(false)) | ||||||
|       val r_bankPos  = Vec(banks.map(_.io.deq(1).bits.pos ))(r_bankIndex) |       when (in.a.fire()) { doneAW := !a_last } | ||||||
|  |  | ||||||
|       def optCat(x: (Boolean, UInt)*) = { Cat(x.toList.filter(_._1).map(_._2)) } |       val arw = out_arw.bits | ||||||
|       b_state := optCat((bankBits > 0, b_bankData), (posBits > 0, b_bankPos), (idBits > 0, b_id)) |       arw.wen   := a_isPut | ||||||
|       r_state := optCat((bankBits > 0, r_bankData), (posBits > 0, r_bankPos), (idBits > 0, r_id)) |       arw.id    := a_id // truncated | ||||||
|  |       arw.addr  := a_address | ||||||
|  |       arw.len   := UIntToOH1(a_size, AXI4Parameters.lenBits + log2Ceil(beatBytes)) >> log2Ceil(beatBytes) | ||||||
|  |       arw.size  := Mux(a_size >= maxSize, maxSize, a_size) | ||||||
|  |       arw.burst := AXI4Parameters.BURST_INCR | ||||||
|  |       arw.lock  := UInt(0) // not exclusive (LR/SC unsupported b/c no forward progress guarantee) | ||||||
|  |       arw.cache := UInt(0) // do not allow AXI to modify our transactions | ||||||
|  |       arw.prot  := AXI4Parameters.PROT_PRIVILEDGED | ||||||
|  |       arw.qos   := UInt(0) // no QoS | ||||||
|  |  | ||||||
|  |       in.a.ready := Mux(a_isPut, (doneAW || out_arw.ready) && out_w.ready, out_arw.ready) | ||||||
|  |       out_arw.valid := in.a.valid && Mux(a_isPut, !doneAW && out_w.ready, Bool(true)) | ||||||
|  |  | ||||||
|  |       out_w.valid := in.a.valid && a_isPut && (doneAW || out_arw.ready) | ||||||
|  |       out_w.bits.data := in.a.bits.data | ||||||
|  |       out_w.bits.strb := in.a.bits.mask | ||||||
|  |       out_w.bits.last := a_last | ||||||
|  |  | ||||||
|  |       // R and B => D arbitration | ||||||
|  |       val r_holds_d = RegInit(Bool(false)) | ||||||
|  |       when (out.r.fire()) { r_holds_d := !out.r.bits.last } | ||||||
|  |       // Give R higher priority than B | ||||||
|  |       val r_wins = out.r.valid || r_holds_d | ||||||
|  |  | ||||||
|  |       out.r.ready := in.d.ready | ||||||
|  |       out_b.ready := in.d.ready && !r_wins | ||||||
|  |       in.d.valid := Mux(r_wins, out.r.valid, out_b.valid) | ||||||
|  |  | ||||||
|  |       val r_error = out.r.bits.resp =/= AXI4Parameters.RESP_OKAY | ||||||
|  |       val b_error = out_b.bits.resp =/= AXI4Parameters.RESP_OKAY | ||||||
|  |  | ||||||
|  |       val r_d = edgeIn.AccessAck(r_addr_lo, UInt(0), r_source, r_size, UInt(0), r_error) | ||||||
|  |       val b_d = edgeIn.AccessAck(b_addr_lo, UInt(0), b_source, b_size, b_error) | ||||||
|  |  | ||||||
|  |       in.d.bits := Mux(r_wins, r_d, b_d) | ||||||
|  |       in.d.bits.data := out.r.bits.data // avoid a costly Mux | ||||||
|  |  | ||||||
|  |       // Tie off unused channels | ||||||
|  |       in.b.valid := Bool(false) | ||||||
|  |       in.c.ready := Bool(true) | ||||||
|  |       in.e.ready := Bool(true) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // We need these Queues because AXI4 queues are irrevocable |  | ||||||
|     val depth = if (combinational) 1 else 2 |  | ||||||
|     val out_arw = Wire(Decoupled(new AXI4BundleARW(out.params))) |  | ||||||
|     val out_w = Wire(out.w) |  | ||||||
|     out.w <> Queue.irrevocable(out_w, entries=depth, flow=combinational) |  | ||||||
|     val queue_arw = Queue.irrevocable(out_arw, entries=depth, flow=combinational) |  | ||||||
|  |  | ||||||
|     // Fan out the ARW channel to AR and AW |  | ||||||
|     out.ar.bits := queue_arw.bits |  | ||||||
|     out.aw.bits := queue_arw.bits |  | ||||||
|     out.ar.valid := queue_arw.valid && !queue_arw.bits.wen |  | ||||||
|     out.aw.valid := queue_arw.valid &&  queue_arw.bits.wen |  | ||||||
|     queue_arw.ready := Mux(queue_arw.bits.wen, out.aw.ready, out.ar.ready) |  | ||||||
|  |  | ||||||
|     val beatBytes = edgeIn.manager.beatBytes |  | ||||||
|     val maxSize   = UInt(log2Ceil(beatBytes)) |  | ||||||
|     val doneAW    = RegInit(Bool(false)) |  | ||||||
|     when (in.a.fire()) { doneAW := !a_last } |  | ||||||
|  |  | ||||||
|     val arw = out_arw.bits |  | ||||||
|     arw.wen   := a_isPut |  | ||||||
|     arw.id    := a_id // truncated |  | ||||||
|     arw.addr  := a_address |  | ||||||
|     arw.len   := UIntToOH1(a_size, AXI4Parameters.lenBits + log2Ceil(beatBytes)) >> log2Ceil(beatBytes) |  | ||||||
|     arw.size  := Mux(a_size >= maxSize, maxSize, a_size) |  | ||||||
|     arw.burst := AXI4Parameters.BURST_INCR |  | ||||||
|     arw.lock  := UInt(0) // not exclusive (LR/SC unsupported b/c no forward progress guarantee) |  | ||||||
|     arw.cache := UInt(0) // do not allow AXI to modify our transactions |  | ||||||
|     arw.prot  := AXI4Parameters.PROT_PRIVILEDGED |  | ||||||
|     arw.qos   := UInt(0) // no QoS |  | ||||||
|  |  | ||||||
|     in.a.ready := Mux(a_isPut, (doneAW || out_arw.ready) && out_w.ready, out_arw.ready) |  | ||||||
|     out_arw.valid := in.a.valid && Mux(a_isPut, !doneAW && out_w.ready, Bool(true)) |  | ||||||
|  |  | ||||||
|     out_w.valid := in.a.valid && a_isPut && (doneAW || out_arw.ready) |  | ||||||
|     out_w.bits.data := in.a.bits.data |  | ||||||
|     out_w.bits.strb := in.a.bits.mask |  | ||||||
|     out_w.bits.last := a_last |  | ||||||
|  |  | ||||||
|     // R and B => D arbitration |  | ||||||
|     val r_holds_d = RegInit(Bool(false)) |  | ||||||
|     when (out.r.fire()) { r_holds_d := !out.r.bits.last } |  | ||||||
|     // Give R higher priority than B |  | ||||||
|     val r_wins = out.r.valid || r_holds_d |  | ||||||
|  |  | ||||||
|     out.r.ready := in.d.ready |  | ||||||
|     out_b.ready := in.d.ready && !r_wins |  | ||||||
|     in.d.valid := Mux(r_wins, out.r.valid, out_b.valid) |  | ||||||
|  |  | ||||||
|     val r_error = out.r.bits.resp =/= AXI4Parameters.RESP_OKAY |  | ||||||
|     val b_error = out_b.bits.resp =/= AXI4Parameters.RESP_OKAY |  | ||||||
|  |  | ||||||
|     val r_d = edgeIn.AccessAck(r_addr_lo, UInt(0), r_source, r_size, UInt(0), r_error) |  | ||||||
|     val b_d = edgeIn.AccessAck(b_addr_lo, UInt(0), b_source, b_size, b_error) |  | ||||||
|  |  | ||||||
|     in.d.bits := Mux(r_wins, r_d, b_d) |  | ||||||
|     in.d.bits.data := out.r.bits.data // avoid a costly Mux |  | ||||||
|  |  | ||||||
|     // Tie off unused channels |  | ||||||
|     in.b.valid := Bool(false) |  | ||||||
|     in.c.ready := Bool(true) |  | ||||||
|     in.e.ready := Bool(true) |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -12,8 +12,8 @@ import scala.math.{min,max} | |||||||
| class TLWidthWidget(innerBeatBytes: Int)(implicit p: Parameters) extends LazyModule | class TLWidthWidget(innerBeatBytes: Int)(implicit p: Parameters) extends LazyModule | ||||||
| { | { | ||||||
|   val node = TLAdapterNode( |   val node = TLAdapterNode( | ||||||
|     clientFn  = { case Seq(c) => c }, |     clientFn  = { case c => c }, | ||||||
|     managerFn = { case Seq(m) => m.copy(beatBytes = innerBeatBytes) }) |     managerFn = { case m => m.copy(beatBytes = innerBeatBytes) }) | ||||||
|  |  | ||||||
|   lazy val module = new LazyModuleImp(this) { |   lazy val module = new LazyModuleImp(this) { | ||||||
|     val io = new Bundle { |     val io = new Bundle { | ||||||
| @@ -139,27 +139,24 @@ class TLWidthWidget(innerBeatBytes: Int)(implicit p: Parameters) extends LazyMod | |||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     val edgeOut = node.edgesOut(0) |     ((io.in zip io.out) zip (node.edgesIn zip node.edgesOut)) foreach { case ((in, out), (edgeIn, edgeOut)) => | ||||||
|     val edgeIn = node.edgesIn(0) |       splice(edgeIn,  in.a,  edgeOut, out.a) | ||||||
|     val in = io.in(0) |       splice(edgeOut, out.d, edgeIn,  in.d) | ||||||
|     val out = io.out(0) |  | ||||||
|  |  | ||||||
|     splice(edgeIn,  in.a,  edgeOut, out.a) |       if (edgeOut.manager.anySupportAcquireB && edgeIn.client.anySupportProbe) { | ||||||
|     splice(edgeOut, out.d, edgeIn,  in.d) |         splice(edgeOut, out.b, edgeIn,  in.b) | ||||||
|  |         splice(edgeIn,  in.c,  edgeOut, out.c) | ||||||
|     if (edgeOut.manager.anySupportAcquireB && edgeIn.client.anySupportProbe) { |         in.e.ready := out.e.ready | ||||||
|       splice(edgeOut, out.b, edgeIn,  in.b) |         out.e.valid := in.e.valid | ||||||
|       splice(edgeIn,  in.c,  edgeOut, out.c) |         out.e.bits := in.e.bits | ||||||
|       in.e.ready := out.e.ready |       } else { | ||||||
|       out.e.valid := in.e.valid |         in.b.valid := Bool(false) | ||||||
|       out.e.bits := in.e.bits |         in.c.ready := Bool(true) | ||||||
|     } else { |         in.e.ready := Bool(true) | ||||||
|       in.b.valid := Bool(false) |         out.b.ready := Bool(true) | ||||||
|       in.c.ready := Bool(true) |         out.c.valid := Bool(false) | ||||||
|       in.e.ready := Bool(true) |         out.e.valid := Bool(false) | ||||||
|       out.b.ready := Bool(true) |       } | ||||||
|       out.c.valid := Bool(false) |  | ||||||
|       out.e.valid := Bool(false) |  | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -34,7 +34,7 @@ class TLXbar(policy: TLArbiter.Policy = TLArbiter.lowestIndexFirst)(implicit p: | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   val node = TLAdapterNode( |   val node = TLNexusNode( | ||||||
|     numClientPorts  = 1 to 32, |     numClientPorts  = 1 to 32, | ||||||
|     numManagerPorts = 1 to 32, |     numManagerPorts = 1 to 32, | ||||||
|     clientFn  = { seq => |     clientFn  = { seq => | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user