From 12cb1c2fa553a68a0ef7b584a35416f695996575 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klemens=20Sch=C3=B6lhorn?= Date: Thu, 10 May 2018 21:40:52 +0200 Subject: [PATCH] Implement XilinxML507MIGToTL TL to MIG converter --- .../xilinxml507mig/XilinxML507MIG.scala | 92 ++++++++++++++----- 1 file changed, 68 insertions(+), 24 deletions(-) diff --git a/src/main/scala/devices/xilinx/xilinxml507mig/XilinxML507MIG.scala b/src/main/scala/devices/xilinx/xilinxml507mig/XilinxML507MIG.scala index 96637f2..5c1f016 100644 --- a/src/main/scala/devices/xilinx/xilinxml507mig/XilinxML507MIG.scala +++ b/src/main/scala/devices/xilinx/xilinxml507mig/XilinxML507MIG.scala @@ -34,6 +34,12 @@ class MemoryController extends BlackBox { override def desiredName: String = "memory_controller" } +class ResponseQueueIO extends Bundle { + val read = Bool() + val source = UInt() + val size = UInt() +} + class XilinxML507MIGToTL(c: XilinxML507MIGParams)(implicit p: Parameters) extends LazyModule with HasCrossing { // Corresponds to MIG interface with 64 bit width and a burst length of 4 val width = 256 @@ -58,6 +64,7 @@ class XilinxML507MIGToTL(c: XilinxML507MIGParams)(implicit p: Parameters) extend // We could possibly also support supportsPutPartial, as we need support // for masks anyway because of the possibility of transfers smaller that // the data width (size signal, see below). + // Seems we can: TL$7.3 lazy val module = new LazyModuleImp(this) { val io = IO(new Bundle { @@ -72,7 +79,7 @@ class XilinxML507MIGToTL(c: XilinxML507MIGParams)(implicit p: Parameters) extend // in: TLBundle, edge: TLEdgeIn val (in, edge) = node.in(0) - // Due to the Fragmenter defined above, all messages are 32 bytes or + // Due to the TLFragmenter defined below, all messages are 32 bytes or // smaller. The data signal of the TL channels is also 32 bytes, so // all messages will be transfered in a single beat. // Also, TL guarantees (see TL$4.6) that the payload of a data message @@ -80,34 +87,71 @@ class XilinxML507MIGToTL(c: XilinxML507MIGParams)(implicit p: Parameters) extend // byte data signal, data[7:0] will always have address 0x***00000 and // data[255:247] address 0x***11111. It is also guaranteed that the // mask bits always correctly reflect the active bytes inside the beat - // with respect to the size and address. - // So we can directly forward the mask, (relative) address and possibly - // data to the MIG interface. - // Put requests can be acknowledged as soon as they are latched into - // the write fifo of the MIG (possibly combinatorily). - // For read requests, we have to store the source id and size in a - // queue for later acknowledgment. - // We are ready if both the MIG and the response data queue are not - // full. + // with respect to the size and address. So we can directly forward + // the mask, (relative) address and data to the MIG interface. - // Widths of the A channel: - // addressBits: 32 - // dataBits: 256 - // sourceBits: 6 - // sinkBits: 1 - // sizeBits: 3 + // Save the source, size and type of the requests in a queue so we + // can synthesize the right responses in fifo order. The length also + // determines the maximum number of in-flight requests. + val ack_queue = Module(new Queue(new ResponseQueueIO, 4)) - // source (from): in.a.bits.source - // adresse (to): edgeIn.address(in.a.bits) - // size: edgeIn.size(in.a.bits) - // isPut: edgeIn.hasData(in.a.bits) + // Pass data directly to the controller + controller.io.request_addr := in.a.bits.address(27, 0) & "hFFFFFE0".U + controller.io.request_type := !edge.hasData(in.a.bits) + controller.io.request_data := in.a.bits.data + // TL uses high to indicate valid data while mig uses low + controller.io.request_mask := ~ in.a.bits.mask - // bits kommt von Decoupled: ready, valid + bits + ack_queue.io.enq.bits.read := !edge.hasData(in.a.bits) + ack_queue.io.enq.bits.source := in.a.bits.source + ack_queue.io.enq.bits.size := in.a.bits.size - println("a parameters: " + in.a.bits.params) + // We are ready when the controller and the queue input are ready + in.a.ready := controller.io.request_ready && ack_queue.io.enq.ready + // Both queues only latch data if the other is ready, so that data + // is latched into both queues or not at all + controller.io.request_valid := in.a.valid && ack_queue.io.enq.ready + ack_queue.io.enq.valid := in.a.valid && controller.io.request_ready + + + // We have to buffer the responses from the MIG as it has no internal + // buffer and will output its read responses only for one cycle. To + // avoid losing any responses, this queue *must* be at least as wide + // as the ack queue, so that we can catch all responses, even if the + // ack queue is completely filled with read requests. + val response_queue = Module(new Queue(controller.io.response_data, 4)) + + response_queue.io.enq.bits := controller.io.response_data + response_queue.io.enq.valid := controller.io.response_valid + // MIG does not support delaying a response, so we ignore enq.ready. + // This will result in lost reads and returning wrong data in further + // AccessAckData messages, so this must be avoided (see above). + + // Acks may or may not contain data depending on the request, but we + // can always pass the data, even if it is invalid in the write case, + // because it is ignored for AccessAck responses + val response_read = ack_queue.io.deq.bits.read + in.d.bits.opcode := Mux(response_read, TLMessages.AccessAckData, TLMessages.AccessAck) + in.d.bits.param := UInt(0) // reserved, must be 0 + in.d.bits.size := ack_queue.io.deq.bits.size + in.d.bits.source := ack_queue.io.deq.bits.source + in.d.bits.sink := UInt(0) // ignored + in.d.bits.data := response_queue.io.deq.bits + in.d.bits.error := Bool(false) + + // The data is valid when the ack queue data is valid (write case) or + // when the ack *and* response queues are valid (read case) + in.d.valid := ack_queue.io.deq.valid && (!response_read || + response_queue.io.deq.valid) + // Let the ack queue dequeue when the master is ready (write case) or + // when the master is ready *and* there is a valid response (read case) + ack_queue.io.deq.ready := in.d.ready && (!response_read || + response_queue.io.deq.valid) + // Let the response queue dequeue when the master is ready and there + // is a valid read ack waiting + response_queue.io.deq.ready := in.d.ready && response_read && + ack_queue.io.deq.valid - in.a.ready := Bool(false) - in.d.valid := Bool(false) // Tie off unused channels in.b.valid := Bool(false)