From 8005266131516b29d0db977d57caa335c0e4f5bc Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Thu, 13 Oct 2016 14:57:01 -0700 Subject: [PATCH] axi4 Fragmenter: refine sideband FSM for case of last fragment --- src/main/scala/uncore/axi4/Fragmenter.scala | 37 +++++++++++++++------ 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/src/main/scala/uncore/axi4/Fragmenter.scala b/src/main/scala/uncore/axi4/Fragmenter.scala index 7f3490ce..0c260069 100644 --- a/src/main/scala/uncore/axi4/Fragmenter.scala +++ b/src/main/scala/uncore/axi4/Fragmenter.scala @@ -18,7 +18,7 @@ class AXI4Fragmenter(lite: Boolean = false, maxInFlight: Int = 32, combinational def mapSlave(s: AXI4SlaveParameters, beatBytes: Int) = s.copy( supportsWrite = expandTransfer(s.supportsWrite, beatBytes, s.minAlignment), supportsRead = expandTransfer(s.supportsRead, beatBytes, s.minAlignment), - interleavedId = if (lite) Some(0) else s.interleavedId) // we preserve interleaving guarantees + interleavedId = if (lite) Some(0) else s.interleavedId) // see AXI4FragmenterSideband for !lite case def mapMaster(m: AXI4MasterParameters) = m.copy(aligned = true) val node = AXI4AdapterNode( @@ -238,29 +238,46 @@ class AXI4Fragmenter(lite: Boolean = false, maxInFlight: Int = 32, combinational } } +/* 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 state = RegInit(Bool(false)) - val count = RegInit(UInt(0, width = log2Up(maxInFlight))) - val idle = 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 - io.deq.bits := state - io.deq.valid := !idle + val state = RegInit(PASS) + val count = RegInit(UInt(0, width = log2Up(maxInFlight))) + 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 := empty || (state === FIND) || (state === PASS && io.enq.bits) if (flow) { when (io.enq.valid) { io.deq.valid := Bool(true) - when (idle) { io.deq.bits := io.enq.bits } + when (empty) { io.deq.bits := io.enq.bits } } } - io.enq.ready := idle || (state === io.enq.bits) - when (io.enq.fire()) { state := 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) { state := Mux(empty, PASS, WAIT) } } + is(WAIT) { when (last && io.deq.ready) { state := PASS } } + } } object AXI4Fragmenter