| @@ -15,7 +15,7 @@ case class AXI4ToTLNode() extends MixedNode(AXI4Imp, TLImp)( | |||||||
|         nodePath = m.nodePath) |         nodePath = m.nodePath) | ||||||
|     })) |     })) | ||||||
|   }, |   }, | ||||||
|   uFn = { case (1, Seq(TLManagerPortParameters(managers, beatBytes, _))) => |   uFn = { case (1, Seq(TLManagerPortParameters(managers, beatBytes, _, _))) => | ||||||
|     Seq(AXI4SlavePortParameters(beatBytes = beatBytes, slaves = managers.map { m => |     Seq(AXI4SlavePortParameters(beatBytes = beatBytes, slaves = managers.map { m => | ||||||
|       AXI4SlaveParameters( |       AXI4SlaveParameters( | ||||||
|         address       = m.address, |         address       = m.address, | ||||||
|   | |||||||
							
								
								
									
										297
									
								
								src/main/scala/uncore/tilelink2/Broadcast.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										297
									
								
								src/main/scala/uncore/tilelink2/Broadcast.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,297 @@ | |||||||
|  | // See LICENSE for license details. | ||||||
|  |  | ||||||
|  | package uncore.tilelink2 | ||||||
|  |  | ||||||
|  | import Chisel._ | ||||||
|  | import diplomacy._ | ||||||
|  | import scala.math.{min,max} | ||||||
|  |  | ||||||
|  | class TLBroadcast(lineBytes: Int, numTrackers: Int = 4) extends LazyModule | ||||||
|  | { | ||||||
|  |   require (lineBytes > 0 && isPow2(lineBytes)) | ||||||
|  |   require (numTrackers > 0) | ||||||
|  |  | ||||||
|  |   val node = TLAdapterNode( | ||||||
|  |     clientFn  = { case Seq(cp) => | ||||||
|  |       cp.copy(clients = Seq(TLClientParameters( | ||||||
|  |         sourceId = IdRange(0, 1 << log2Ceil(cp.endSourceId*4))))) | ||||||
|  |     }, | ||||||
|  |     managerFn = { case Seq(mp) => | ||||||
|  |       mp.copy( | ||||||
|  |         endSinkId  = numTrackers, | ||||||
|  |         minLatency = 1, | ||||||
|  |         managers   = mp.managers.map { m => | ||||||
|  |           // We are the last level manager | ||||||
|  |           require (m.regionType != RegionType.CACHED) | ||||||
|  |           require (m.regionType != RegionType.TRACKED) | ||||||
|  |           require (!m.supportsAcquire) | ||||||
|  |           // We only manage addresses which are uncached | ||||||
|  |           if (m.regionType == RegionType.UNCACHED) { | ||||||
|  |             // The device had better support line transfers | ||||||
|  |             require (!m.supportsPutFull    || m.supportsPutFull   .contains(lineBytes)) | ||||||
|  |             require (!m.supportsPutPartial || m.supportsPutPartial.contains(lineBytes)) | ||||||
|  |             require (!m.supportsGet        || m.supportsGet       .contains(lineBytes)) | ||||||
|  |             m.copy( | ||||||
|  |               regionType         = RegionType.TRACKED, | ||||||
|  |               supportsAcquire    = TransferSizes(1, lineBytes), | ||||||
|  |               // truncate supported accesses to lineBytes (we only ever probe for one line) | ||||||
|  |               supportsPutFull    = TransferSizes(m.supportsPutFull   .min, min(m.supportsPutFull   .max, lineBytes)), | ||||||
|  |               supportsPutPartial = TransferSizes(m.supportsPutPartial.min, min(m.supportsPutPartial.max, lineBytes)), | ||||||
|  |               supportsGet        = TransferSizes(m.supportsGet       .min, min(m.supportsGet       .max, lineBytes)), | ||||||
|  |               fifoId             = None // trackers do not respond in FIFO order! | ||||||
|  |             ) | ||||||
|  |           } else { | ||||||
|  |             m | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       ) | ||||||
|  |     } | ||||||
|  |   ) | ||||||
|  |  | ||||||
|  |   lazy val module = new LazyModuleImp(this) { | ||||||
|  |     val io = new Bundle { | ||||||
|  |       val in  = node.bundleIn | ||||||
|  |       val out = node.bundleOut | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     val in = io.in(0) | ||||||
|  |     val out = io.out(0) | ||||||
|  |     val edgeIn = node.edgesIn(0) | ||||||
|  |     val edgeOut = node.edgesOut(0) | ||||||
|  |     val clients = edgeIn.client.clients | ||||||
|  |     val managers = edgeOut.manager.managers | ||||||
|  |     val lineShift = log2Ceil(lineBytes) | ||||||
|  |  | ||||||
|  |     import TLBroadcastConstants._ | ||||||
|  |  | ||||||
|  |     require (lineBytes >= edgeOut.manager.beatBytes) | ||||||
|  |     // For the probe walker, we need to identify all the caches | ||||||
|  |     val caches = clients.filter(_.supportsProbe).map(_.sourceId) | ||||||
|  |     val cache_targets = Vec(caches.map(c => UInt(c.start))) | ||||||
|  |  | ||||||
|  |     // Create the request tracker queues | ||||||
|  |     val trackers = Seq.tabulate(numTrackers) { id => | ||||||
|  |       Module(new TLBroadcastTracker(id, lineBytes, log2Up(caches.size), edgeIn, edgeOut)).io | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // We always accept E | ||||||
|  |     in.e.ready := Bool(true) | ||||||
|  |     (trackers zip UIntToOH(in.e.bits.sink).toBools) foreach { case (tracker, select) => | ||||||
|  |       tracker.e_last := select && in.e.fire() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Depending on the high source bits, we might transform D | ||||||
|  |     val d_high = 1 << log2Ceil(edgeIn.client.endSourceId) | ||||||
|  |     val d_what = out.d.bits.source(d_high+1, d_high) | ||||||
|  |     val d_drop = d_what === DROP | ||||||
|  |     val d_hasData = edgeOut.hasData(out.d.bits) | ||||||
|  |     val d_normal = Wire(in.d) | ||||||
|  |     val d_trackerOH = Vec(trackers.map { t => !t.idle && t.source === d_normal.bits.source }).asUInt | ||||||
|  |  | ||||||
|  |     out.d.ready := d_normal.ready || d_drop | ||||||
|  |     d_normal.valid := out.d.valid && !d_drop | ||||||
|  |     d_normal.bits := out.d.bits // truncates source | ||||||
|  |     when (d_what(1)) { // TRANSFORM_* | ||||||
|  |       d_normal.bits.opcode := Mux(d_hasData, TLMessages.GrantData, TLMessages.ReleaseAck) | ||||||
|  |       d_normal.bits.param  := Mux(d_hasData, Mux(d_what(0), TLPermissions.toT, TLPermissions.toB), UInt(0)) | ||||||
|  |     } | ||||||
|  |     d_normal.bits.sink := OHToUInt(d_trackerOH) | ||||||
|  |     assert (!d_normal.valid || d_trackerOH.orR()) | ||||||
|  |  | ||||||
|  |     // A tracker response is anything neither dropped nor a ReleaseAck | ||||||
|  |     val d_response = d_hasData || !d_what(1) | ||||||
|  |     val (_, d_last, _) = edgeIn.firstlast(d_normal) | ||||||
|  |     (trackers zip d_trackerOH.toBools) foreach { case (tracker, select) => | ||||||
|  |       tracker.d_last := select && d_normal.fire() && d_response && d_last | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Incoming C can be: | ||||||
|  |     // ProbeAck     => decrement tracker, drop  | ||||||
|  |     // ProbeAckData => decrement tracker, send out A as PutFull(DROP) | ||||||
|  |     // ReleaseData  =>                    send out A as PutFull(TRANSFORM) | ||||||
|  |     // Release      => send out D as ReleaseAck | ||||||
|  |  | ||||||
|  |     val c_probeack     = in.c.bits.opcode === TLMessages.ProbeAck | ||||||
|  |     val c_probeackdata = in.c.bits.opcode === TLMessages.ProbeAckData | ||||||
|  |     val c_releasedata  = in.c.bits.opcode === TLMessages.ReleaseData | ||||||
|  |     val c_release      = in.c.bits.opcode === TLMessages.Release | ||||||
|  |  | ||||||
|  |     // Decrement the tracker's outstanding probe counter | ||||||
|  |     val c_decrement = in.c.fire() && (c_probeack || c_probeackdata) | ||||||
|  |     val (_, c_last, _) = edgeIn.firstlast(in.c) | ||||||
|  |     trackers foreach { tracker => | ||||||
|  |       tracker.probeack := c_decrement && c_last && tracker.line === (in.c.bits.address >> lineShift) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     val releaseack = Wire(in.d) | ||||||
|  |     val putfull = Wire(out.a) | ||||||
|  |  | ||||||
|  |     in.c.ready := c_probeack || Mux(c_release, releaseack.ready, putfull.ready) | ||||||
|  |  | ||||||
|  |     releaseack.valid := in.c.valid && c_release | ||||||
|  |     releaseack.bits  := edgeIn.ReleaseAck(in.c.bits.address, UInt(0), in.c.bits.source, in.c.bits.size) | ||||||
|  |  | ||||||
|  |     val put_what = Mux(c_releasedata, TRANSFORM_B, DROP) | ||||||
|  |     putfull.valid := in.c.valid && (c_probeackdata || c_releasedata) | ||||||
|  |     putfull.bits := edgeOut.Put(Cat(put_what, in.c.bits.source), in.c.bits.address, in.c.bits.size, in.c.bits.data)._2 | ||||||
|  |  | ||||||
|  |     // Combine ReleaseAck or the modified D | ||||||
|  |     TLArbiter(TLArbiter.lowestIndexFirst)(in.d, (UInt(0), releaseack), (edgeOut.numBeats1(d_normal.bits), d_normal)) | ||||||
|  |     // Combine the PutFull with the trackers | ||||||
|  |     TLArbiter(TLArbiter.lowestIndexFirst)(out.a,  | ||||||
|  |       ((edgeOut.numBeats1(putfull.bits), putfull) +: | ||||||
|  |        trackers.map { t => (edgeOut.numBeats1(t.out_a.bits), t.out_a) }):_*) | ||||||
|  |  | ||||||
|  |     // The Probe FSM walks all caches and probes them | ||||||
|  |     val probe_todo = RegInit(UInt(0, width = caches.size)) | ||||||
|  |     val probe_line = Reg(UInt()) | ||||||
|  |     val probe_perms = Reg(UInt(width = 2)) | ||||||
|  |     val probe_next = probe_todo & ~(leftOR(probe_todo) << 1) | ||||||
|  |     val probe_busy = probe_todo.orR() | ||||||
|  |     val probe_target = Mux1H(probe_next, cache_targets) | ||||||
|  |  | ||||||
|  |     // Probe whatever the FSM wants to do next | ||||||
|  |     in.b.valid := probe_busy | ||||||
|  |     in.b.bits := edgeIn.Probe(probe_line << lineShift, probe_target, UInt(lineShift), probe_perms)._2 | ||||||
|  |     when (in.b.fire()) { probe_todo := probe_todo & ~probe_next } | ||||||
|  |  | ||||||
|  |     // Which cache does a request come from? | ||||||
|  |     val a_cache = Vec(caches.map(_.contains(in.a.bits.source))).asUInt | ||||||
|  |     val (a_first, _, _) = edgeIn.firstlast(in.a) | ||||||
|  |  | ||||||
|  |     // To accept a request from A, the probe FSM must be idle and there must be a matching tracker | ||||||
|  |     val freeTrackers = Vec(trackers.map { t => t.idle }).asUInt | ||||||
|  |     val freeTracker = freeTrackers.orR() | ||||||
|  |     val matchTrackers = Vec(trackers.map { t => t.line === in.a.bits.address >> lineShift }).asUInt | ||||||
|  |     val matchTracker = matchTrackers.orR() | ||||||
|  |     val allocTracker = freeTrackers & ~(leftOR(freeTrackers) << 1) | ||||||
|  |     val selectTracker = Mux(matchTracker, matchTrackers, allocTracker) | ||||||
|  |  | ||||||
|  |     val trackerReady = Vec(trackers.map(_.in_a.ready)).asUInt | ||||||
|  |     in.a.ready := (!a_first || !probe_busy) && (selectTracker & trackerReady).orR() | ||||||
|  |     (trackers zip selectTracker.toBools) foreach { case (t, select) => | ||||||
|  |       t.in_a.valid := in.a.valid && select && (!a_first || !probe_busy) | ||||||
|  |       t.in_a.bits := in.a.bits | ||||||
|  |       t.in_a_first := a_first | ||||||
|  |       t.probe := Mux(a_cache.orR(), UInt(caches.size-1), UInt(caches.size)) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     when (in.a.fire() && a_first) { | ||||||
|  |       probe_todo  := ~a_cache // probe all but the cache who poked us | ||||||
|  |       probe_line  := in.a.bits.address >> lineShift | ||||||
|  |       probe_perms := MuxLookup(in.a.bits.opcode, Wire(UInt(width = 2)), Array( | ||||||
|  |         TLMessages.PutFullData    -> TLPermissions.toN, | ||||||
|  |         TLMessages.PutPartialData -> TLPermissions.toN, | ||||||
|  |         TLMessages.ArithmeticData -> TLPermissions.toN, | ||||||
|  |         TLMessages.LogicalData    -> TLPermissions.toN, | ||||||
|  |         TLMessages.Get            -> TLPermissions.toB, | ||||||
|  |         TLMessages.Hint           -> MuxLookup(in.a.bits.param, Wire(UInt(width = 2)), Array( | ||||||
|  |           TLHints.PREFETCH_READ   -> TLPermissions.toB, | ||||||
|  |           TLHints.PREFETCH_WRITE  -> TLPermissions.toN)), | ||||||
|  |         TLMessages.Acquire        -> MuxLookup(in.a.bits.param, Wire(UInt(width = 2)), Array( | ||||||
|  |           TLPermissions.NtoB      -> TLPermissions.toB, | ||||||
|  |           TLPermissions.NtoT      -> TLPermissions.toN, | ||||||
|  |           TLPermissions.BtoT      -> TLPermissions.toN)))) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // The outer TL connections may not be cached | ||||||
|  |     out.b.ready := Bool(true) | ||||||
|  |     out.c.valid := Bool(false) | ||||||
|  |     out.e.valid := Bool(false) | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class TLBroadcastTracker(id: Int, lineBytes: Int, probeCountBits: Int, edgeIn: TLEdgeIn, edgeOut: TLEdgeOut) extends Module | ||||||
|  | { | ||||||
|  |   val io = new Bundle { | ||||||
|  |     val in_a_first = Bool(INPUT) | ||||||
|  |     val in_a  = Decoupled(new TLBundleA(edgeIn.bundle)).flip | ||||||
|  |     val out_a = Decoupled(new TLBundleA(edgeOut.bundle)) | ||||||
|  |     val probe = UInt(INPUT, width = probeCountBits) | ||||||
|  |     val probeack = Bool(INPUT) | ||||||
|  |     val d_last = Bool(INPUT) | ||||||
|  |     val e_last = Bool(INPUT) | ||||||
|  |     val source = UInt(OUTPUT) // the source awaiting D response | ||||||
|  |     val line = UInt(OUTPUT)   // the line waiting for probes | ||||||
|  |     val idle = Bool(OUTPUT) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   val lineShift = log2Ceil(lineBytes) | ||||||
|  |   import TLBroadcastConstants._ | ||||||
|  |  | ||||||
|  |   // Only one operation can be inflight per line, because we need to be sure | ||||||
|  |   // we send the request after all the probes we sent and before all the next probes | ||||||
|  |   val idle    = RegInit(Bool(true)) | ||||||
|  |   val opcode  = Reg(io.in_a.bits.opcode) | ||||||
|  |   val param   = Reg(io.in_a.bits.param) | ||||||
|  |   val size    = Reg(io.in_a.bits.size) | ||||||
|  |   val source  = Reg(io.in_a.bits.source) | ||||||
|  |   val address = RegInit(UInt(id << lineShift, width = io.in_a.bits.address.getWidth)) | ||||||
|  |   val count   = Reg(UInt(width = probeCountBits)) | ||||||
|  |  | ||||||
|  |   when (io.in_a.fire() && io.in_a_first) { | ||||||
|  |     assert (idle) | ||||||
|  |     idle    := Bool(false) | ||||||
|  |     opcode  := io.in_a.bits.opcode | ||||||
|  |     param   := io.in_a.bits.param | ||||||
|  |     size    := io.in_a.bits.size | ||||||
|  |     source  := io.in_a.bits.source | ||||||
|  |     address := io.in_a.bits.address | ||||||
|  |     count   := io.probe | ||||||
|  |   } | ||||||
|  |   when (io.d_last) { | ||||||
|  |     assert (!idle) | ||||||
|  |     idle := opcode =/= TLMessages.Acquire | ||||||
|  |   } | ||||||
|  |   when (io.e_last) { | ||||||
|  |     assert (!idle) | ||||||
|  |     idle := Bool(true) | ||||||
|  |   } | ||||||
|  |   when (io.probeack) { | ||||||
|  |     assert (count > UInt(0)) | ||||||
|  |     count := count - UInt(1) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   io.idle := idle | ||||||
|  |   io.source := source | ||||||
|  |   io.line := address >> lineShift | ||||||
|  |  | ||||||
|  |   class Data extends Bundle { | ||||||
|  |     val mask = io.in_a.bits.mask.cloneType | ||||||
|  |     val data = io.in_a.bits.data.cloneType | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   val i_data = Wire(Decoupled(new Data)) | ||||||
|  |   val o_data = Queue(i_data, lineBytes / edgeIn.manager.beatBytes) | ||||||
|  |  | ||||||
|  |   io.in_a.ready := (idle || !io.in_a_first) && i_data.ready | ||||||
|  |   i_data.valid := (idle || !io.in_a_first) && io.in_a.valid | ||||||
|  |   i_data.bits.mask := io.in_a.bits.mask | ||||||
|  |   i_data.bits.data := io.in_a.bits.data | ||||||
|  |  | ||||||
|  |   val probe_done = count === UInt(0) | ||||||
|  |   val acquire = opcode === TLMessages.Acquire | ||||||
|  |  | ||||||
|  |   val transform = MuxLookup(param, Wire(UInt(width = 2)), Array( | ||||||
|  |     TLPermissions.NtoB -> TRANSFORM_B, | ||||||
|  |     TLPermissions.NtoT -> TRANSFORM_T, | ||||||
|  |     TLPermissions.BtoT -> TRANSFORM_T)) | ||||||
|  |  | ||||||
|  |   o_data.ready := io.out_a.ready && probe_done | ||||||
|  |   io.out_a.valid := o_data.valid && probe_done | ||||||
|  |   io.out_a.bits.opcode  := Mux(acquire, TLMessages.Get, opcode) | ||||||
|  |   io.out_a.bits.param   := Mux(acquire, UInt(0), param) | ||||||
|  |   io.out_a.bits.size    := size | ||||||
|  |   io.out_a.bits.source  := Cat(Mux(acquire, transform, PASS), source) | ||||||
|  |   io.out_a.bits.address := address | ||||||
|  |   io.out_a.bits.mask    := o_data.bits.mask | ||||||
|  |   io.out_a.bits.data    := o_data.bits.data | ||||||
|  | } | ||||||
|  |  | ||||||
|  | object TLBroadcastConstants | ||||||
|  | { | ||||||
|  |   val TRANSFORM_T = UInt(3) | ||||||
|  |   val TRANSFORM_B = UInt(2) | ||||||
|  |   val DROP        = UInt(1) | ||||||
|  |   val PASS        = UInt(0) | ||||||
|  | } | ||||||
| @@ -88,6 +88,12 @@ object TLAtomics | |||||||
|   def isLogical(x: UInt) = x <= SWAP |   def isLogical(x: UInt) = x <= SWAP | ||||||
| } | } | ||||||
|  |  | ||||||
|  | object TLHints | ||||||
|  | { | ||||||
|  |   val PREFETCH_READ  = UInt(0) | ||||||
|  |   val PREFETCH_WRITE = UInt(1) | ||||||
|  | } | ||||||
|  |  | ||||||
| sealed trait TLChannel extends TLBundleBase { | sealed trait TLChannel extends TLBundleBase { | ||||||
|   val channelName: String |   val channelName: String | ||||||
| } | } | ||||||
|   | |||||||
| @@ -43,7 +43,7 @@ class TLHintHandler(supportManagers: Boolean = true, supportClients: Boolean = f | |||||||
|       out.a.valid := in.a.valid && !hintBitsAtA |       out.a.valid := in.a.valid && !hintBitsAtA | ||||||
|       in.a.ready := Mux(hintBitsAtA, hint.ready, out.a.ready) |       in.a.ready := Mux(hintBitsAtA, hint.ready, out.a.ready) | ||||||
|  |  | ||||||
|       hint.bits := edgeIn.HintAck(in.a.bits, edgeOut.manager.findIdStartFast(address)) |       hint.bits := edgeIn.HintAck(in.a.bits, UInt(0)) | ||||||
|       out.a.bits := in.a.bits |       out.a.bits := in.a.bits | ||||||
|  |  | ||||||
|       TLArbiter(TLArbiter.lowestIndexFirst)(in.d, (edgeOut.numBeats1(out.d.bits), out.d), (UInt(0), Queue(hint, 1))) |       TLArbiter(TLArbiter.lowestIndexFirst)(in.d, (edgeOut.numBeats1(out.d.bits), out.d), (UInt(0), Queue(hint, 1))) | ||||||
|   | |||||||
| @@ -216,7 +216,7 @@ class TLMonitor(gen: () => TLBundleSnoop, edge: () => TLEdge, sourceInfo: Source | |||||||
|  |  | ||||||
|     val source_ok = edge.client.contains(bundle.source) |     val source_ok = edge.client.contains(bundle.source) | ||||||
|     val is_aligned = edge.isAligned(bundle.addr_lo, bundle.size) |     val is_aligned = edge.isAligned(bundle.addr_lo, bundle.size) | ||||||
|     val sink_ok = edge.manager.containsById(bundle.sink) |     val sink_ok = bundle.sink < UInt(edge.manager.endSinkId) | ||||||
|  |  | ||||||
|     when (bundle.opcode === TLMessages.ReleaseAck) { |     when (bundle.opcode === TLMessages.ReleaseAck) { | ||||||
|       assert (source_ok, "'D' channel ReleaseAck carries invalid source ID" + extra) |       assert (source_ok, "'D' channel ReleaseAck carries invalid source ID" + extra) | ||||||
| @@ -270,7 +270,8 @@ class TLMonitor(gen: () => TLBundleSnoop, edge: () => TLEdge, sourceInfo: Source | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   def legalizeFormatE(bundle: TLBundleE, edge: TLEdge)(implicit sourceInfo: SourceInfo) { |   def legalizeFormatE(bundle: TLBundleE, edge: TLEdge)(implicit sourceInfo: SourceInfo) { | ||||||
|     assert (edge.manager.containsById(bundle.sink), "'E' channels carries invalid sink ID" + extra) |     val sink_ok = bundle.sink < UInt(edge.manager.endSinkId) | ||||||
|  |     assert (sink_ok, "'E' channels carries invalid sink ID" + extra) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   def legalizeFormat(bundle: TLBundleSnoop, edge: TLEdge)(implicit sourceInfo: SourceInfo) = { |   def legalizeFormat(bundle: TLBundleSnoop, edge: TLEdge)(implicit sourceInfo: SourceInfo) = { | ||||||
|   | |||||||
| @@ -108,7 +108,7 @@ object TLClientNode | |||||||
| object TLManagerNode | object TLManagerNode | ||||||
| { | { | ||||||
|   def apply(beatBytes: Int, params: TLManagerParameters) = |   def apply(beatBytes: Int, params: TLManagerParameters) = | ||||||
|     new TLManagerNode(TLManagerPortParameters(Seq(params), beatBytes, 0), 1 to 1) |     new TLManagerNode(TLManagerPortParameters(Seq(params), beatBytes, minLatency = 0), 1 to 1) | ||||||
| } | } | ||||||
|  |  | ||||||
| case class TLAdapterNode( | case class TLAdapterNode( | ||||||
|   | |||||||
| @@ -8,7 +8,6 @@ import scala.math.max | |||||||
|  |  | ||||||
| case class TLManagerParameters( | case class TLManagerParameters( | ||||||
|   address:            Seq[AddressSet], |   address:            Seq[AddressSet], | ||||||
|   sinkId:             IdRange       = IdRange(0, 1), |  | ||||||
|   regionType:         RegionType.T  = RegionType.GET_EFFECTS, |   regionType:         RegionType.T  = RegionType.GET_EFFECTS, | ||||||
|   executable:         Boolean       = false, // processor can execute from this memory |   executable:         Boolean       = false, // processor can execute from this memory | ||||||
|   nodePath:           Seq[BaseNode] = Seq(), |   nodePath:           Seq[BaseNode] = Seq(), | ||||||
| @@ -59,22 +58,15 @@ case class TLManagerParameters( | |||||||
| case class TLManagerPortParameters( | case class TLManagerPortParameters( | ||||||
|   managers:   Seq[TLManagerParameters], |   managers:   Seq[TLManagerParameters], | ||||||
|   beatBytes:  Int, |   beatBytes:  Int, | ||||||
|  |   endSinkId:  Int = 1, | ||||||
|   minLatency: Int = 0) |   minLatency: Int = 0) | ||||||
| { | { | ||||||
|   require (!managers.isEmpty) |   require (!managers.isEmpty) | ||||||
|   require (isPow2(beatBytes)) |   require (isPow2(beatBytes)) | ||||||
|  |   require (endSinkId > 0) | ||||||
|   require (minLatency >= 0) |   require (minLatency >= 0) | ||||||
|  |  | ||||||
|   // Require disjoint ranges for Ids and addresses |  | ||||||
|   managers.combinations(2).foreach({ case Seq(x,y) => |  | ||||||
|     require (!x.sinkId.overlaps(y.sinkId)) |  | ||||||
|     x.address.foreach({ a => y.address.foreach({ b => |  | ||||||
|       require (!a.overlaps(b)) |  | ||||||
|     })}) |  | ||||||
|   }) |  | ||||||
|  |  | ||||||
|   // Bounds on required sizes |   // Bounds on required sizes | ||||||
|   def endSinkId   = managers.map(_.sinkId.end).max |  | ||||||
|   def maxAddress  = managers.map(_.maxAddress).max |   def maxAddress  = managers.map(_.maxAddress).max | ||||||
|   def maxTransfer = managers.map(_.maxTransfer).max |   def maxTransfer = managers.map(_.maxTransfer).max | ||||||
|    |    | ||||||
| @@ -101,12 +93,6 @@ case class TLManagerPortParameters( | |||||||
|  |  | ||||||
|   // These return Option[TLManagerParameters] for your convenience |   // These return Option[TLManagerParameters] for your convenience | ||||||
|   def find(address: BigInt) = managers.find(_.address.exists(_.contains(address))) |   def find(address: BigInt) = managers.find(_.address.exists(_.contains(address))) | ||||||
|   def findById(id: Int) = managers.find(_.sinkId.contains(id)) |  | ||||||
|  |  | ||||||
|   // Synthesizable lookup methods |  | ||||||
|   def findById(id: UInt) = Vec(managers.map(_.sinkId.contains(id))) |  | ||||||
|   def findIdStartFast(address: UInt) = Mux1H(findFast(address), managers.map(m => UInt(m.sinkId.start))) |  | ||||||
|   def findIdEndFast(address: UInt) = Mux1H(findFast(address), managers.map(m => UInt(m.sinkId.end))) |  | ||||||
|  |  | ||||||
|   // The safe version will check the entire address |   // The safe version will check the entire address | ||||||
|   def findSafe(address: UInt) = Vec(managers.map(_.address.map(_.contains(address)).reduce(_ || _))) |   def findSafe(address: UInt) = Vec(managers.map(_.address.map(_.contains(address)).reduce(_ || _))) | ||||||
| @@ -119,8 +105,6 @@ case class TLManagerPortParameters( | |||||||
|  |  | ||||||
|   // Does this Port manage this ID/address? |   // Does this Port manage this ID/address? | ||||||
|   def containsSafe(address: UInt) = findSafe(address).reduce(_ || _) |   def containsSafe(address: UInt) = findSafe(address).reduce(_ || _) | ||||||
|   // containsFast would be useless; it could always be true |  | ||||||
|   def containsById(id: UInt) = findById(id).reduce(_ || _) |  | ||||||
|  |  | ||||||
|   private def safe_helper(member: TLManagerParameters => TransferSizes)(address: UInt, lgSize: UInt) = { |   private def safe_helper(member: TLManagerParameters => TransferSizes)(address: UInt, lgSize: UInt) = { | ||||||
|     val allSame = managers.map(member(_) == member(managers(0))).reduce(_ && _) |     val allSame = managers.map(member(_) == member(managers(0))).reduce(_ && _) | ||||||
|   | |||||||
| @@ -220,8 +220,6 @@ class TLRAMModel(log: String = "") extends LazyModule | |||||||
|     when (d_fire) { |     when (d_fire) { | ||||||
|       // Check the response is correct |       // Check the response is correct | ||||||
|       assert (d_size === d_flight.size) |       assert (d_size === d_flight.size) | ||||||
|       assert (edge.manager.findIdStartFast(d_flight.base) <= d.sink) |  | ||||||
|       assert (edge.manager.findIdEndFast  (d_flight.base) > d.sink) |  | ||||||
|       // addr_lo is allowed to differ |       // addr_lo is allowed to differ | ||||||
|  |  | ||||||
|       when (d_flight.opcode === TLMessages.Hint) { |       when (d_flight.opcode === TLMessages.Hint) { | ||||||
|   | |||||||
| @@ -19,10 +19,9 @@ case class TLToAXI4Node(idBits: Int) extends MixedNode(TLImp, AXI4Imp)( | |||||||
|     Seq(AXI4MasterPortParameters(masters)) |     Seq(AXI4MasterPortParameters(masters)) | ||||||
|   }, |   }, | ||||||
|   uFn = { case (1, Seq(AXI4SlavePortParameters(slaves, beatBytes))) => |   uFn = { case (1, Seq(AXI4SlavePortParameters(slaves, beatBytes))) => | ||||||
|     val managers = slaves.zipWithIndex.map { case (s, id) => |     val managers = slaves.map { case s => | ||||||
|       TLManagerParameters( |       TLManagerParameters( | ||||||
|         address            = s.address, |         address            = s.address, | ||||||
|         sinkId             = IdRange(id, id+1), |  | ||||||
|         regionType         = s.regionType, |         regionType         = s.regionType, | ||||||
|         executable         = s.executable, |         executable         = s.executable, | ||||||
|         nodePath           = s.nodePath, |         nodePath           = s.nodePath, | ||||||
| @@ -31,7 +30,7 @@ case class TLToAXI4Node(idBits: Int) extends MixedNode(TLImp, AXI4Imp)( | |||||||
|         supportsPutPartial = s.supportsWrite) |         supportsPutPartial = s.supportsWrite) | ||||||
|         // AXI4 is NEVER fifo in TL sense (R+W are independent) |         // AXI4 is NEVER fifo in TL sense (R+W are independent) | ||||||
|     } |     } | ||||||
|     Seq(TLManagerPortParameters(managers, beatBytes, 0)) |     Seq(TLManagerPortParameters(managers, beatBytes, 1, 0)) | ||||||
|   }, |   }, | ||||||
|   numPO = 1 to 1, |   numPO = 1 to 1, | ||||||
|   numPI = 1 to 1) |   numPI = 1 to 1) | ||||||
| @@ -64,7 +63,7 @@ class TLToAXI4(idBits: Int, combinational: Boolean = true) extends LazyModule | |||||||
|     // AR before working on AW might have an AW slipped between two AR fragments. |     // 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) |     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) |     // We need to keep the following state from A => D: (addr_lo, size, source) | ||||||
|     // All of those fields could potentially require 0 bits (argh. Chisel.) |     // 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. |     // We will pack as many of the lowest bits of state as fit into the AXI ID. | ||||||
|     // Any bits left-over must be put into a bank of Queues. |     // Any bits left-over must be put into a bank of Queues. | ||||||
| @@ -72,46 +71,39 @@ class TLToAXI4(idBits: Int, combinational: Boolean = true) extends LazyModule | |||||||
|     // The Queues are deep enough that every source has guaranteed space in its Queue. |     // The Queues are deep enough that every source has guaranteed space in its Queue. | ||||||
|  |  | ||||||
|     val sourceBits = log2Ceil(edgeIn.client.endSourceId) |     val sourceBits = log2Ceil(edgeIn.client.endSourceId) | ||||||
|     val sinkBits = log2Ceil(edgeIn.manager.endSinkId) |  | ||||||
|     val sizeBits = log2Ceil(edgeIn.maxLgSize+1) |     val sizeBits = log2Ceil(edgeIn.maxLgSize+1) | ||||||
|     val addrBits = log2Ceil(edgeIn.manager.beatBytes) |     val addrBits = log2Ceil(edgeIn.manager.beatBytes) | ||||||
|     val stateBits = addrBits + sizeBits + sinkBits + sourceBits // could be 0 |     val stateBits = addrBits + sizeBits + sourceBits // could be 0 | ||||||
|  |  | ||||||
|     val a_address = edgeIn.address(in.a.bits) |     val a_address = edgeIn.address(in.a.bits) | ||||||
|     val a_addr_lo = edgeIn.addr_lo(a_address) |     val a_addr_lo = edgeIn.addr_lo(a_address) | ||||||
|     val a_source  = in.a.bits.source |     val a_source  = in.a.bits.source | ||||||
|     val a_sink    = edgeIn.manager.findIdStartFast(a_address) |  | ||||||
|     val a_size    = edgeIn.size(in.a.bits) |     val a_size    = edgeIn.size(in.a.bits) | ||||||
|     val a_isPut   = edgeIn.hasData(in.a.bits) |     val a_isPut   = edgeIn.hasData(in.a.bits) | ||||||
|     val (_, a_last, _) = edgeIn.firstlast(in.a) |     val (_, a_last, _) = edgeIn.firstlast(in.a) | ||||||
|  |  | ||||||
|     // Make sure the fields are within the bounds we assumed |     // Make sure the fields are within the bounds we assumed | ||||||
|     assert (a_source  < UInt(1 << sourceBits)) |     assert (a_source  < UInt(1 << sourceBits)) | ||||||
|     assert (a_sink    < UInt(1 << sinkBits)) |  | ||||||
|     assert (a_size    < UInt(1 << sizeBits)) |     assert (a_size    < UInt(1 << sizeBits)) | ||||||
|     assert (a_addr_lo < UInt(1 << addrBits)) |     assert (a_addr_lo < UInt(1 << addrBits)) | ||||||
|  |  | ||||||
|     // Carefully pack/unpack fields into the state we send |     // Carefully pack/unpack fields into the state we send | ||||||
|     val baseEnd = 0 |     val baseEnd = 0 | ||||||
|     val (sourceEnd, sourceOff) = (sourceBits + baseEnd,   baseEnd) |     val (sourceEnd, sourceOff) = (sourceBits + baseEnd,   baseEnd) | ||||||
|     val (sinkEnd,   sinkOff)   = (sinkBits   + sourceEnd, sourceEnd) |     val (sizeEnd,   sizeOff)   = (sizeBits   + sourceEnd, sourceEnd) | ||||||
|     val (sizeEnd,   sizeOff)   = (sizeBits   + sinkEnd,   sinkEnd) |  | ||||||
|     val (addrEnd,   addrOff)   = (addrBits   + sizeEnd,   sizeEnd) |     val (addrEnd,   addrOff)   = (addrBits   + sizeEnd,   sizeEnd) | ||||||
|     require (addrEnd == stateBits) |     require (addrEnd == stateBits) | ||||||
|  |  | ||||||
|     val a_state = (a_source << sourceOff) | (a_sink    << sinkOff) | |     val a_state = (a_source << sourceOff) | (a_size << sizeOff) | (a_addr_lo << addrOff) | ||||||
|                   (a_size   << sizeOff)   | (a_addr_lo << addrOff) |  | ||||||
|     val a_id = if (idBits == 0) UInt(0) else a_state |     val a_id = if (idBits == 0) UInt(0) else a_state | ||||||
|  |  | ||||||
|     val r_state = Wire(UInt(width = stateBits)) |     val r_state = Wire(UInt(width = stateBits)) | ||||||
|     val r_source  = if (sourceBits > 0) r_state(sourceEnd-1, sourceOff) else UInt(0) |     val r_source  = if (sourceBits > 0) r_state(sourceEnd-1, sourceOff) else UInt(0) | ||||||
|     val r_sink    = if (sinkBits   > 0) r_state(sinkEnd  -1, sinkOff)   else UInt(0) |  | ||||||
|     val r_size    = if (sizeBits   > 0) r_state(sizeEnd  -1, sizeOff)   else UInt(0) |     val r_size    = if (sizeBits   > 0) r_state(sizeEnd  -1, sizeOff)   else UInt(0) | ||||||
|     val r_addr_lo = if (addrBits   > 0) r_state(addrEnd  -1, addrOff)   else UInt(0) |     val r_addr_lo = if (addrBits   > 0) r_state(addrEnd  -1, addrOff)   else UInt(0) | ||||||
|  |  | ||||||
|     val b_state = Wire(UInt(width = stateBits)) |     val b_state = Wire(UInt(width = stateBits)) | ||||||
|     val b_source  = if (sourceBits > 0) b_state(sourceEnd-1, sourceOff) else UInt(0) |     val b_source  = if (sourceBits > 0) b_state(sourceEnd-1, sourceOff) else UInt(0) | ||||||
|     val b_sink    = if (sinkBits   > 0) b_state(sinkEnd  -1, sinkOff)   else UInt(0) |  | ||||||
|     val b_size    = if (sizeBits   > 0) b_state(sizeEnd  -1, sizeOff)   else UInt(0) |     val b_size    = if (sizeBits   > 0) b_state(sizeEnd  -1, sizeOff)   else UInt(0) | ||||||
|     val b_addr_lo = if (addrBits   > 0) b_state(addrEnd  -1, addrOff)   else UInt(0) |     val b_addr_lo = if (addrBits   > 0) b_state(addrEnd  -1, addrOff)   else UInt(0) | ||||||
|  |  | ||||||
| @@ -221,8 +213,8 @@ class TLToAXI4(idBits: Int, combinational: Boolean = true) extends LazyModule | |||||||
|     val r_error = out.r.bits.resp =/= AXI4Parameters.RESP_OKAY |     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 r_d = edgeIn.AccessAck(r_addr_lo, UInt(0), r_source, r_size, UInt(0), r_error) | ||||||
|     val b_d = edgeIn.AccessAck(b_addr_lo, b_sink, b_source, b_size, b_error) |     val b_d = edgeIn.AccessAck(b_addr_lo, UInt(0), b_source, b_size, b_error) | ||||||
|  |  | ||||||
|     in.d.bits := Mux(r_wins, r_d, b_d) |     in.d.bits := Mux(r_wins, r_d, b_d) | ||||||
|     in.d.bits.data := out.r.bits.data // avoid a costly Mux |     in.d.bits.data := out.r.bits.data // avoid a costly Mux | ||||||
|   | |||||||
| @@ -50,13 +50,14 @@ class TLXbar(policy: TLArbiter.Policy = TLArbiter.lowestIndexFirst) extends Lazy | |||||||
|     }, |     }, | ||||||
|     managerFn = { seq => |     managerFn = { seq => | ||||||
|       val fifoIdFactory = relabeler() |       val fifoIdFactory = relabeler() | ||||||
|  |       val outputIdRanges = mapOutputIds(seq) | ||||||
|       seq(0).copy( |       seq(0).copy( | ||||||
|         minLatency = seq.map(_.minLatency).min, |         minLatency = seq.map(_.minLatency).min, | ||||||
|         managers = (mapOutputIds(seq) zip seq) flatMap { case (range, port) => |         endSinkId = outputIdRanges.map(_.end).max, | ||||||
|  |         managers = (outputIdRanges zip seq) flatMap { case (range, port) => | ||||||
|           require (port.beatBytes == seq(0).beatBytes) |           require (port.beatBytes == seq(0).beatBytes) | ||||||
|           val fifoIdMapper = fifoIdFactory() |           val fifoIdMapper = fifoIdFactory() | ||||||
|           port.managers map { manager => manager.copy( |           port.managers map { manager => manager.copy( | ||||||
|             sinkId = manager.sinkId.shift(range.start), |  | ||||||
|             fifoId = manager.fifoId.map(fifoIdMapper(_)) |             fifoId = manager.fifoId.map(fifoIdMapper(_)) | ||||||
|           )} |           )} | ||||||
|         } |         } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user