From 8169ba64116c3ec6f0552634ca2e1ec29203565c Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Sun, 7 May 2017 13:10:51 -0700 Subject: [PATCH 1/7] axi4: IdIndexer now handles 0-width IDs --- src/main/scala/uncore/axi4/IdIndexer.scala | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/main/scala/uncore/axi4/IdIndexer.scala b/src/main/scala/uncore/axi4/IdIndexer.scala index b7283417..8abf78db 100644 --- a/src/main/scala/uncore/axi4/IdIndexer.scala +++ b/src/main/scala/uncore/axi4/IdIndexer.scala @@ -56,12 +56,22 @@ class AXI4IdIndexer(idBits: Int)(implicit p: Parameters) extends LazyModule val bits = log2Ceil(edgeIn.master.endId) - idBits if (bits > 0) { - out.ar.bits.user.get := Cat(in.ar.bits.user.toList ++ Seq(in.ar.bits.id >> idBits)) - out.aw.bits.user.get := Cat(in.aw.bits.user.toList ++ Seq(in.aw.bits.id >> idBits)) - in.r.bits.user.foreach { _ := out.r.bits.user.get >> bits } - in.b.bits.user.foreach { _ := out.b.bits.user.get >> bits } - in.r.bits.id := Cat(out.r.bits.user.get, out.r.bits.id) - in.b.bits.id := Cat(out.b.bits.user.get, out.b.bits.id) + // (in.aX.bits.id >> idBits).width = bits > 0 + out.ar.bits.user.get := Cat(in.ar.bits.user.toList ++ Seq(in.ar.bits.id >> idBits)) + out.aw.bits.user.get := Cat(in.aw.bits.user.toList ++ Seq(in.aw.bits.id >> idBits)) + // user.isDefined => width > 0 + in.r.bits.user.foreach { _ := out.r.bits.user.get >> bits } + in.b.bits.user.foreach { _ := out.b.bits.user.get >> bits } + // Special care is needed in case of 0 idBits, b/c .id has width 1 still + if (idBits == 0) { + out.ar.bits.id := UInt(0) + out.aw.bits.id := UInt(0) + in.r.bits.id := out.r.bits.user.get + in.b.bits.id := out.b.bits.user.get + } else { + in.r.bits.id := Cat(out.r.bits.user.get, out.r.bits.id) + in.b.bits.id := Cat(out.b.bits.user.get, out.b.bits.id) + } } } } From 4847c3259993eafbc2dcd833298cc14c84d2dcfc Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Mon, 8 May 2017 00:00:40 -0700 Subject: [PATCH 2/7] tilelink: ToAXI4 - must interlock till last beat AXI4 makes no guarantee that bursts are handled atomicly. Thus, you could be part-way through a read burst and suddenly a write cuts ahead and is visible later, violating FIFO. --- src/main/scala/uncore/tilelink2/ToAXI4.scala | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/scala/uncore/tilelink2/ToAXI4.scala b/src/main/scala/uncore/tilelink2/ToAXI4.scala index 75003eda..4ee88236 100644 --- a/src/main/scala/uncore/tilelink2/ToAXI4.scala +++ b/src/main/scala/uncore/tilelink2/ToAXI4.scala @@ -175,16 +175,13 @@ class TLToAXI4(beatBytes: Int, combinational: Boolean = true)(implicit p: Parame val a_sel = UIntToOH(arw.id, edgeOut.master.endId).toBools val d_sel = UIntToOH(Mux(r_wins, out.r.bits.id, out.b.bits.id), edgeOut.master.endId).toBools val d_last = Mux(r_wins, out.r.bits.last, Bool(true)) - val d_first = RegInit(Bool(true)) - when (in.d.fire()) { d_first := d_last } val stalls = ((a_sel zip d_sel) zip idCount) filter { case (_, n) => n > 1 } map { case ((as, ds), n) => val count = RegInit(UInt(0, width = log2Ceil(n + 1))) val write = Reg(Bool()) val idle = count === UInt(0) - // Once we start getting the response, it's safe to already switch R/W val inc = as && out_arw.fire() - val dec = ds && d_first && in.d.fire() + val dec = ds && d_last && in.d.fire() count := count + inc.asUInt - dec.asUInt assert (!dec || count =/= UInt(0)) // underflow From 8fc27b0bf21cfaaf50425a802bdb786f79f8c7ad Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Mon, 8 May 2017 00:03:07 -0700 Subject: [PATCH 3/7] axi4: IdIndexer; a single ID does NOT imply no response interleaving Some slaves may never send R until you process their B. Thus, while there is no read response interleaving, there is still interleaving between R and B, which breaks AXI4ToTL. --- src/main/scala/uncore/axi4/IdIndexer.scala | 4 +--- src/main/scala/uncore/axi4/Parameters.scala | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/scala/uncore/axi4/IdIndexer.scala b/src/main/scala/uncore/axi4/IdIndexer.scala index 8abf78db..e999a200 100644 --- a/src/main/scala/uncore/axi4/IdIndexer.scala +++ b/src/main/scala/uncore/axi4/IdIndexer.scala @@ -34,9 +34,7 @@ class AXI4IdIndexer(idBits: Int)(implicit p: Parameters) extends LazyModule userBits = mp.userBits + max(0, log2Ceil(mp.endId) - idBits), masters = masters) }, - slaveFn = { sp => sp.copy( - slaves = sp.slaves.map(s => s.copy( - interleavedId = if (idBits == 0) Some(0) else s.interleavedId))) + slaveFn = { sp => sp }) lazy val module = new LazyModuleImp(this) { diff --git a/src/main/scala/uncore/axi4/Parameters.scala b/src/main/scala/uncore/axi4/Parameters.scala index 8642613e..6b4a0c2e 100644 --- a/src/main/scala/uncore/axi4/Parameters.scala +++ b/src/main/scala/uncore/axi4/Parameters.scala @@ -15,7 +15,7 @@ case class AXI4SlaveParameters( nodePath: Seq[BaseNode] = Seq(), supportsWrite: TransferSizes = TransferSizes.none, supportsRead: TransferSizes = TransferSizes.none, - interleavedId: Option[Int] = None) // The device will not interleave read responses + interleavedId: Option[Int] = None) // The device will not interleave responses (R+B) { address.foreach { a => require (a.finite) } address.combinations(2).foreach { case Seq(x,y) => require (!x.overlaps(y), s"$x and $y overlap") } From db76ff2d8689f96ba344106d3f07915755f4305b Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Mon, 8 May 2017 00:07:45 -0700 Subject: [PATCH 4/7] axi4: Deinterleaver must gather R also for single ID In order to guarantee that a complete R can be sent without sinking B, the Deinterleaver must do its job even on AXI-Lite. --- src/main/scala/uncore/axi4/Deinterleaver.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/scala/uncore/axi4/Deinterleaver.scala b/src/main/scala/uncore/axi4/Deinterleaver.scala index bc926c69..dcdf68b4 100644 --- a/src/main/scala/uncore/axi4/Deinterleaver.scala +++ b/src/main/scala/uncore/axi4/Deinterleaver.scala @@ -38,8 +38,8 @@ class AXI4Deinterleaver(maxReadBytes: Int)(implicit p: Parameters) extends LazyM out.w <> in.w in.b <> out.b - if (queues == 1) { - // Gracefully do nothing + if (beats <= 1) { + // Nothing to do if only single-beat R in.r <> out.r } else { // Buffer R response @@ -48,7 +48,7 @@ class AXI4Deinterleaver(maxReadBytes: Int)(implicit p: Parameters) extends LazyM // Which ID is being enqueued and dequeued? val locked = RegInit(Bool(false)) - val deq_id = Reg(UInt(width=log2Ceil(queues))) + val deq_id = Reg(UInt(width=log2Up(queues))) val enq_id = out.r.bits.id val deq_OH = UIntToOH(deq_id, queues) val enq_OH = UIntToOH(enq_id, queues) From 3209e588458308d0dfc46a5fd358b5b19e4f9521 Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Mon, 8 May 2017 00:30:40 -0700 Subject: [PATCH 5/7] axi4: SRAM support 0 userBits --- src/main/scala/uncore/axi4/SRAM.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/scala/uncore/axi4/SRAM.scala b/src/main/scala/uncore/axi4/SRAM.scala index e0ae971b..6c53cc02 100644 --- a/src/main/scala/uncore/axi4/SRAM.scala +++ b/src/main/scala/uncore/axi4/SRAM.scala @@ -40,7 +40,7 @@ class AXI4RAM(address: AddressSet, executable: Boolean = true, beatBytes: Int = val w_full = RegInit(Bool(false)) val w_id = Reg(UInt()) - val w_user = Reg(UInt()) + val w_user = Reg(UInt(width = 1 max in.params.userBits)) when (in. b.fire()) { w_full := Bool(false) } when (in.aw.fire()) { w_full := Bool(true) } @@ -65,7 +65,7 @@ class AXI4RAM(address: AddressSet, executable: Boolean = true, beatBytes: Int = val r_full = RegInit(Bool(false)) val r_id = Reg(UInt()) - val r_user = Reg(UInt()) + val r_user = Reg(UInt(width = 1 max in.params.userBits)) when (in. r.fire()) { r_full := Bool(false) } when (in.ar.fire()) { r_full := Bool(true) } From 36f4584bb1d81ba91403b3af3bf48d6647570341 Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Mon, 8 May 2017 00:31:35 -0700 Subject: [PATCH 6/7] axi4: Test AXI4-Lite in regression --- src/main/scala/uncore/axi4/Test.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/scala/uncore/axi4/Test.scala b/src/main/scala/uncore/axi4/Test.scala index 46451146..6eab607d 100644 --- a/src/main/scala/uncore/axi4/Test.scala +++ b/src/main/scala/uncore/axi4/Test.scala @@ -26,8 +26,8 @@ class AXI4LiteFuzzRAM()(implicit p: Parameters) extends LazyModule model.node := fuzz.node xbar.node := TLDelayer(0.1)(TLBuffer(BufferParams.flow)(TLDelayer(0.2)(model.node))) - ram.node := AXI4Fragmenter()(AXI4Deinterleaver(16)(TLToAXI4(4, true )(xbar.node))) - gpio.node := AXI4Fragmenter()(AXI4Deinterleaver(16)(TLToAXI4(4, false)(xbar.node))) + ram.node := AXI4UserYanker()(AXI4IdIndexer(0)(TLToAXI4(4, true )(TLFragmenter(4, 16)(xbar.node)))) + gpio.node := AXI4UserYanker()(AXI4IdIndexer(0)(TLToAXI4(4, false)(TLFragmenter(4, 16)(xbar.node)))) lazy val module = new LazyModuleImp(this) with HasUnitTestIO { io.finished := fuzz.module.io.finished From 2d8a49cc0637b500fd29df9201ecf44e4ce07bbd Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Mon, 8 May 2017 00:56:45 -0700 Subject: [PATCH 7/7] tilelink2: Fragmenter client must request global FIFO --- src/main/scala/uncore/tilelink2/Fragmenter.scala | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/scala/uncore/tilelink2/Fragmenter.scala b/src/main/scala/uncore/tilelink2/Fragmenter.scala index c726d7ff..a1a28591 100644 --- a/src/main/scala/uncore/tilelink2/Fragmenter.scala +++ b/src/main/scala/uncore/tilelink2/Fragmenter.scala @@ -37,11 +37,13 @@ class TLFragmenter(val minSize: Int, val maxSize: Int, val alwaysMin: Boolean = supportsPutFull = expandTransfer(m.supportsPutFull), supportsPutPartial = expandTransfer(m.supportsPutPartial), supportsHint = expandTransfer(m.supportsHint)) - def mapClient(c: TLClientParameters) = c.copy( - sourceId = IdRange(c.sourceId.start << fragmentBits, c.sourceId.end << fragmentBits)) val node = TLAdapterNode( - clientFn = { c => c.copy(clients = c.clients.map(mapClient)) }, + // We require that all the responses are mutually FIFO + // Thus we need to compact all of the masters into one big master + clientFn = { c => c.copy(clients = Seq(TLClientParameters( + sourceId = IdRange(0, c.endSourceId << fragmentBits), + requestFifo = true))) }, managerFn = { m => m.copy(managers = m.managers.map(mapManager)) }) lazy val module = new LazyModuleImp(this) {