Implement XilinxML507MIGToTL TL to MIG converter
This commit is contained in:
parent
7e53be49f9
commit
12cb1c2fa5
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user