From 8e92ac32b711c8fc9917efc585039eba45b97816 Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Tue, 11 Oct 2016 22:26:04 -0700 Subject: [PATCH] tilelink2 ToAXI4: we need a Queue on B to guarantee deadlock freedom --- src/main/scala/uncore/tilelink2/ToAXI4.scala | 21 +++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/main/scala/uncore/tilelink2/ToAXI4.scala b/src/main/scala/uncore/tilelink2/ToAXI4.scala index f68d22a6..163f6acb 100644 --- a/src/main/scala/uncore/tilelink2/ToAXI4.scala +++ b/src/main/scala/uncore/tilelink2/ToAXI4.scala @@ -53,6 +53,13 @@ class TLToAXI4(idBits: Int, combinational: Boolean = true) extends LazyModule require (slaves(0).interleavedId.isDefined) slaves.foreach { s => require (s.interleavedId == slaves(0).interleavedId) } + // We need to ensure that a slave does not stall trying to send B while we need to receive R + // Since R&W have independent flow control, it is possible for a W to cut in-line and get into + // 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 keep the following state from A => D: (addr_lo, size, sink, source) // All of those fields could potentially require 0 bits (argh. Chisel.) // We will pack as many of the lowest bits of state as fit into the AXI ID. @@ -113,7 +120,7 @@ class TLToAXI4(idBits: Int, combinational: Boolean = true) extends LazyModule val r_last = out.r.bits.last val r_id = out.r.bits.id - val b_id = out.b.bits.id + val b_id = out_b.bits.id if (stateBits <= idBits) { // No need for any state tracking r_state := r_id @@ -148,7 +155,7 @@ class TLToAXI4(idBits: Int, combinational: Boolean = true) extends LazyModule 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(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) @@ -169,8 +176,8 @@ class TLToAXI4(idBits: Int, combinational: Boolean = true) extends LazyModule 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, pipe=combinational, flow=combinational) - val queue_arw = Queue.irrevocable(out_arw, entries=depth, pipe=combinational, flow=combinational) + 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 @@ -211,11 +218,11 @@ class TLToAXI4(idBits: Int, combinational: Boolean = true) extends LazyModule 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) + 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 b_error = out_b.bits.resp =/= AXI4Parameters.RESP_OKAY val r_d = edgeIn.AccessAck(r_addr_lo, r_sink, r_source, r_size, UInt(0), r_error) val b_d = edgeIn.AccessAck(b_addr_lo, b_sink, b_source, b_size, b_error)