axi4: make maxFlight a per-master parameter
This commit is contained in:
		| @@ -13,11 +13,26 @@ class AXI4IdIndexer(idBits: Int)(implicit p: Parameters) extends LazyModule | |||||||
|   require (idBits >= 0) |   require (idBits >= 0) | ||||||
|  |  | ||||||
|   val node = AXI4AdapterNode( |   val node = AXI4AdapterNode( | ||||||
|     masterFn = { mp => mp.copy( |     masterFn = { mp => | ||||||
|  |       // Create one new "master" per ID | ||||||
|  |       val masters = Array.tabulate(1 << idBits) { i => AXI4MasterParameters( | ||||||
|  |          id        = IdRange(i, i+1), | ||||||
|  |          aligned   = true, | ||||||
|  |          maxFlight = Some(0)) | ||||||
|  |       } | ||||||
|  |       // Squash the information from original masters into new ID masters | ||||||
|  |       mp.masters.foreach { m => | ||||||
|  |         for (i <- m.id.start until m.id.end) { | ||||||
|  |           val j = i % (1 << idBits) | ||||||
|  |           val old = masters(j) | ||||||
|  |           masters(j) = old.copy( | ||||||
|  |             aligned   = old.aligned && m.aligned, | ||||||
|  |             maxFlight = old.maxFlight.flatMap { o => m.maxFlight.map { n => o+n } }) | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       mp.copy( | ||||||
|         userBits = mp.userBits + max(0, log2Ceil(mp.endId) - idBits), |         userBits = mp.userBits + max(0, log2Ceil(mp.endId) - idBits), | ||||||
|       masters  = Seq(AXI4MasterParameters( |         masters  = masters) | ||||||
|         id      = IdRange(0, min(mp.endId, 1 << idBits)), |  | ||||||
|         aligned = mp.masters.map(_.aligned).reduce(_ && _)))) |  | ||||||
|     }, |     }, | ||||||
|     slaveFn = { sp => sp.copy( |     slaveFn = { sp => sp.copy( | ||||||
|       slaves = sp.slaves.map(s => s.copy( |       slaves = sp.slaves.map(s => s.copy( | ||||||
|   | |||||||
| @@ -64,19 +64,19 @@ case class AXI4SlavePortParameters( | |||||||
| case class AXI4MasterParameters( | case class AXI4MasterParameters( | ||||||
|   id:        IdRange       = IdRange(0, 1), |   id:        IdRange       = IdRange(0, 1), | ||||||
|   aligned:   Boolean       = false, |   aligned:   Boolean       = false, | ||||||
|  |   maxFlight: Option[Int]   = None, // None = infinite, else is a per-ID cap | ||||||
|   nodePath:  Seq[BaseNode] = Seq()) |   nodePath:  Seq[BaseNode] = Seq()) | ||||||
| { | { | ||||||
|   val name = nodePath.lastOption.map(_.lazyModule.name).getOrElse("disconnected") |   val name = nodePath.lastOption.map(_.lazyModule.name).getOrElse("disconnected") | ||||||
|  |   maxFlight.foreach { m => require (m >= 0) } | ||||||
| } | } | ||||||
|  |  | ||||||
| case class AXI4MasterPortParameters( | case class AXI4MasterPortParameters( | ||||||
|   masters:   Seq[AXI4MasterParameters], |   masters:   Seq[AXI4MasterParameters], | ||||||
|   userBits:  Int = 0, |   userBits:  Int = 0) | ||||||
|   maxFlight: Int = 0) // at most X transactions per ID (0 = unlimited) |  | ||||||
| { | { | ||||||
|   val endId = masters.map(_.id.end).max |   val endId = masters.map(_.id.end).max | ||||||
|   require (userBits >= 0) |   require (userBits >= 0) | ||||||
|   require (maxFlight >= 0) |  | ||||||
|  |  | ||||||
|   // Require disjoint ranges for ids |   // Require disjoint ranges for ids | ||||||
|   masters.combinations(2).foreach { case Seq(x,y) => require (!x.id.overlaps(y.id), s"$x and $y overlap") } |   masters.combinations(2).foreach { case Seq(x,y) => require (!x.id.overlaps(y.id), s"$x and $y overlap") } | ||||||
|   | |||||||
| @@ -9,8 +9,9 @@ import diplomacy._ | |||||||
| import uncore.tilelink2._ | import uncore.tilelink2._ | ||||||
|  |  | ||||||
| case class AXI4ToTLNode() extends MixedAdapterNode(AXI4Imp, TLImp)( | case class AXI4ToTLNode() extends MixedAdapterNode(AXI4Imp, TLImp)( | ||||||
|   dFn = { case AXI4MasterPortParameters(masters, userBits, maxFlight) => |   dFn = { case AXI4MasterPortParameters(masters, userBits) => | ||||||
|     require (maxFlight > 0, "AXI4 must include a maximum transactions per ID to convert to TL") |     masters.foreach { m => require (m.maxFlight.isDefined, "AXI4 must include a transaction maximum per ID to convert to TL") } | ||||||
|  |     val maxFlight = masters.map(_.maxFlight.get).max | ||||||
|     TLClientPortParameters( |     TLClientPortParameters( | ||||||
|       clients = masters.flatMap { m => |       clients = masters.flatMap { m => | ||||||
|         for (id <- m.id.start until m.id.end) |         for (id <- m.id.start until m.id.end) | ||||||
| @@ -50,7 +51,7 @@ class AXI4ToTL()(implicit p: Parameters) extends LazyModule | |||||||
|       val numIds = edgeIn.master.endId |       val numIds = edgeIn.master.endId | ||||||
|       val beatBytes = edgeOut.manager.beatBytes |       val beatBytes = edgeOut.manager.beatBytes | ||||||
|       val countBits = AXI4Parameters.lenBits + (1 << AXI4Parameters.sizeBits) - 1 |       val countBits = AXI4Parameters.lenBits + (1 << AXI4Parameters.sizeBits) - 1 | ||||||
|       val maxFlight = edgeIn.master.maxFlight |       val maxFlight = edgeIn.master.masters.map(_.maxFlight.get).max | ||||||
|       val addedBits = log2Ceil(maxFlight) + 1 |       val addedBits = log2Ceil(maxFlight) + 1 | ||||||
|  |  | ||||||
|       require (edgeIn.master.userBits == 0, "AXI4 user bits cannot be transported by TL") |       require (edgeIn.master.userBits == 0, "AXI4 user bits cannot be transported by TL") | ||||||
|   | |||||||
| @@ -10,12 +10,15 @@ import uncore.tilelink2.UIntToOH1 | |||||||
|  |  | ||||||
| class AXI4UserYanker(capMaxFlight: Option[Int] = None)(implicit p: Parameters) extends LazyModule | class AXI4UserYanker(capMaxFlight: Option[Int] = None)(implicit p: Parameters) extends LazyModule | ||||||
| { | { | ||||||
|   // !!! make maxFlightPerId a cap and maxFlight a per AXI4 Master parameter |  | ||||||
|   val maxFlightPerId = capMaxFlight.getOrElse(8) |  | ||||||
|   require (maxFlightPerId >= 1) |  | ||||||
|  |  | ||||||
|   val node = AXI4AdapterNode( |   val node = AXI4AdapterNode( | ||||||
|     masterFn = { mp => mp.copy(maxFlight = maxFlightPerId, userBits = 0) }, |     masterFn = { mp => mp.copy( | ||||||
|  |       userBits = 0, | ||||||
|  |       masters = mp.masters.map { m => m.copy( | ||||||
|  |         maxFlight = (m.maxFlight, capMaxFlight) match { | ||||||
|  |           case (Some(x), Some(y)) => Some(x min y) | ||||||
|  |           case (Some(x), None)    => Some(x) | ||||||
|  |           case (None,    Some(y)) => Some(y) | ||||||
|  |           case (None,    None)    => None })})}, | ||||||
|     slaveFn = { sp => sp }) |     slaveFn = { sp => sp }) | ||||||
|  |  | ||||||
|   lazy val module = new LazyModuleImp(this) { |   lazy val module = new LazyModuleImp(this) { | ||||||
| @@ -29,18 +32,31 @@ class AXI4UserYanker(capMaxFlight: Option[Int] = None)(implicit p: Parameters) e | |||||||
|       val need_bypass = edgeOut.slave.minLatency < 1 |       val need_bypass = edgeOut.slave.minLatency < 1 | ||||||
|       require (bits > 0) // useless UserYanker! |       require (bits > 0) // useless UserYanker! | ||||||
|  |  | ||||||
|       val rqueues = Seq.fill(edgeIn.master.endId) { Module(new Queue(UInt(width = bits), maxFlightPerId, flow=need_bypass)) } |       edgeOut.master.masters.foreach { m => | ||||||
|       val wqueues = Seq.fill(edgeIn.master.endId) { Module(new Queue(UInt(width = bits), maxFlightPerId, flow=need_bypass)) } |         require (m.maxFlight.isDefined, "UserYanker needs a flight cap on each ID") | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       def queue(id: Int) = { | ||||||
|  |         val depth = edgeOut.master.masters.find(_.id.contains(id)).flatMap(_.maxFlight).getOrElse(0) | ||||||
|  |         if (depth == 0) { | ||||||
|  |           Wire(new QueueIO(UInt(width = bits), 1)) // unused ID => undefined value | ||||||
|  |         } else { | ||||||
|  |           Module(new Queue(UInt(width = bits), depth, flow=need_bypass)).io | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       val rqueues = Seq.tabulate(edgeIn.master.endId) { i => queue(i) } | ||||||
|  |       val wqueues = Seq.tabulate(edgeIn.master.endId) { i => queue(i) } | ||||||
|  |  | ||||||
|       val arid = in.ar.bits.id |       val arid = in.ar.bits.id | ||||||
|       val ar_ready = Vec(rqueues.map(_.io.enq.ready))(arid) |       val ar_ready = Vec(rqueues.map(_.enq.ready))(arid) | ||||||
|       in .ar.ready := out.ar.ready && ar_ready |       in .ar.ready := out.ar.ready && ar_ready | ||||||
|       out.ar.valid := in .ar.valid && ar_ready |       out.ar.valid := in .ar.valid && ar_ready | ||||||
|       out.ar.bits  := in .ar.bits |       out.ar.bits  := in .ar.bits | ||||||
|  |  | ||||||
|       val rid = out.r.bits.id |       val rid = out.r.bits.id | ||||||
|       val r_valid = Vec(rqueues.map(_.io.deq.valid))(rid) |       val r_valid = Vec(rqueues.map(_.deq.valid))(rid) | ||||||
|       val r_bits = Vec(rqueues.map(_.io.deq.bits))(rid) |       val r_bits = Vec(rqueues.map(_.deq.bits))(rid) | ||||||
|       assert (!out.r.valid || r_valid) // Q must be ready faster than the response |       assert (!out.r.valid || r_valid) // Q must be ready faster than the response | ||||||
|       in.r <> out.r |       in.r <> out.r | ||||||
|       in.r.bits.user.get := r_bits |       in.r.bits.user.get := r_bits | ||||||
| @@ -48,20 +64,20 @@ class AXI4UserYanker(capMaxFlight: Option[Int] = None)(implicit p: Parameters) e | |||||||
|       val arsel = UIntToOH(arid, edgeIn.master.endId).toBools |       val arsel = UIntToOH(arid, edgeIn.master.endId).toBools | ||||||
|       val rsel  = UIntToOH(rid,  edgeIn.master.endId).toBools |       val rsel  = UIntToOH(rid,  edgeIn.master.endId).toBools | ||||||
|       (rqueues zip (arsel zip rsel)) foreach { case (q, (ar, r)) => |       (rqueues zip (arsel zip rsel)) foreach { case (q, (ar, r)) => | ||||||
|         q.io.deq.ready := out.r .valid && in .r .ready && r && out.r.bits.last |         q.deq.ready := out.r .valid && in .r .ready && r && out.r.bits.last | ||||||
|         q.io.enq.valid := in .ar.valid && out.ar.ready && ar |         q.enq.valid := in .ar.valid && out.ar.ready && ar | ||||||
|         q.io.enq.bits  := in.ar.bits.user.get |         q.enq.bits  := in.ar.bits.user.get | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       val awid = in.aw.bits.id |       val awid = in.aw.bits.id | ||||||
|       val aw_ready = Vec(wqueues.map(_.io.enq.ready))(awid) |       val aw_ready = Vec(wqueues.map(_.enq.ready))(awid) | ||||||
|       in .aw.ready := out.aw.ready && aw_ready |       in .aw.ready := out.aw.ready && aw_ready | ||||||
|       out.aw.valid := in .aw.valid && aw_ready |       out.aw.valid := in .aw.valid && aw_ready | ||||||
|       out.aw.bits  := in .aw.bits |       out.aw.bits  := in .aw.bits | ||||||
|  |  | ||||||
|       val bid = out.b.bits.id |       val bid = out.b.bits.id | ||||||
|       val b_valid = Vec(wqueues.map(_.io.deq.valid))(bid) |       val b_valid = Vec(wqueues.map(_.deq.valid))(bid) | ||||||
|       val b_bits = Vec(wqueues.map(_.io.deq.bits))(bid) |       val b_bits = Vec(wqueues.map(_.deq.bits))(bid) | ||||||
|       assert (!out.b.valid || b_valid) // Q must be ready faster than the response |       assert (!out.b.valid || b_valid) // Q must be ready faster than the response | ||||||
|       in.b <> out.b |       in.b <> out.b | ||||||
|       in.b.bits.user.get := b_bits |       in.b.bits.user.get := b_bits | ||||||
| @@ -69,9 +85,9 @@ class AXI4UserYanker(capMaxFlight: Option[Int] = None)(implicit p: Parameters) e | |||||||
|       val awsel = UIntToOH(awid, edgeIn.master.endId).toBools |       val awsel = UIntToOH(awid, edgeIn.master.endId).toBools | ||||||
|       val bsel  = UIntToOH(bid,  edgeIn.master.endId).toBools |       val bsel  = UIntToOH(bid,  edgeIn.master.endId).toBools | ||||||
|       (wqueues zip (awsel zip bsel)) foreach { case (q, (aw, b)) => |       (wqueues zip (awsel zip bsel)) foreach { case (q, (aw, b)) => | ||||||
|         q.io.deq.ready := out.b .valid && in .b .ready && b |         q.deq.ready := out.b .valid && in .b .ready && b | ||||||
|         q.io.enq.valid := in .aw.valid && out.aw.ready && aw |         q.enq.valid := in .aw.valid && out.aw.ready && aw | ||||||
|         q.io.enq.bits  := in.aw.bits.user.get |         q.enq.bits  := in.aw.bits.user.get | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       out.w <> in.w |       out.w <> in.w | ||||||
|   | |||||||
| @@ -18,6 +18,7 @@ case class TLToAXI4Node(beatBytes: Int) extends MixedAdapterNode(TLImp, AXI4Imp) | |||||||
|       AXI4MasterParameters( |       AXI4MasterParameters( | ||||||
|         id        = IdRange(start, start+size), |         id        = IdRange(start, start+size), | ||||||
|         aligned   = true, |         aligned   = true, | ||||||
|  |         maxFlight = Some(if (c.requestFifo) c.sourceId.size else 1), | ||||||
|         nodePath  = c.nodePath) |         nodePath  = c.nodePath) | ||||||
|     } |     } | ||||||
|     AXI4MasterPortParameters( |     AXI4MasterPortParameters( | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user