tilelink2 ToAXI4: we need a Queue on B to guarantee deadlock freedom
This commit is contained in:
		| @@ -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) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user