diff --git a/src/main/scala/unleashed/u500ml507devkit/fpga/Memory.scala b/src/main/scala/unleashed/u500ml507devkit/fpga/Memory.scala index 9ebf228..5e4c9c9 100644 --- a/src/main/scala/unleashed/u500ml507devkit/fpga/Memory.scala +++ b/src/main/scala/unleashed/u500ml507devkit/fpga/Memory.scala @@ -18,13 +18,23 @@ case object MemoryML507Key extends Field[MemoryML507Params] trait HasMemoryML507 { this: BaseSubsystem => val memory = LazyModule(new TLMemoryML507(p(MemoryML507Key))) + // The Fragmenter will not fragment messages <= 32 bytes, so all + // slaves have to support this size. 64 byte specifies the maximum + // supported transfer size that the slave side of the fragmenter supports + // against the master (here the main memory bus). Specifying alwaysMin as + // true results in all messages being fragmented to the minimal size + // (32 byte). In TL1 terms, slaves + // correspond roughly to managers and masters to clients (confusingly…). + val fragmenter = TLFragmenter(32, 64, alwaysMin=true) + // TODO: right TL/memory node chain? - memory.node := memBuses.head.toDRAMController(Some("ml507mig"))() + memory.node := fragmenter := memBuses.head.toDRAMController(Some("ml507mig"))() } class TLMemoryML507(c: MemoryML507Params)(implicit p: Parameters) extends LazyModule { - val width = 512 - val beatBytes = width/8 // TODO: To wide? TLFragmenter? fixedSize? + // Corresponds to MIG interface with 64 bit width and a burst length of 4 + val width = 256 + val beatBytes = width/8 // 32 byte (half a cache-line, fragmented) val device = new MemoryDevice val node = TLManagerNode( @@ -41,15 +51,54 @@ class TLMemoryML507(c: MemoryML507Params)(implicit p: Parameters) extends LazyMo beatBytes = beatBytes )) ) + // 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). lazy val module = new LazyModuleImp(this) { - val (in, edge)= node.in(0) + // in: TLBundle, edge: TLEdgeIn + val (in, edge) = node.in(0) + + // Due to the Fragmenter defined above, 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 + // is always aligned to the width of the beat, e.g. in case of a 32 + // 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. + + // Widths of the A channel: + // addressBits: 32 + // dataBits: 256 + // sourceBits: 6 + // sinkBits: 1 + // sizeBits: 3 + + // 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) + + // bits kommt von Decoupled: ready, valid + bits + + println("a parameters: " + in.a.bits.params) + + in.a.ready := Bool(false) + in.d.valid := Bool(false) // Tie off unused channels - in.a.ready := Bool(false) in.b.valid := Bool(false) - in.c.ready := Bool(false) - in.d.valid := Bool(false) - in.e.ready := Bool(false) + in.c.ready := Bool(true) + in.e.ready := Bool(true) } }