generalize NastiReadDataArbiter
This commit is contained in:
		| @@ -311,48 +311,6 @@ class NastiArbiter(val arbN: Int)(implicit p: Parameters) extends NastiModule { | ||||
|   } else { io.slave <> io.master.head } | ||||
| } | ||||
|  | ||||
| /** Locking RR arbiter for Nasti read data channel | ||||
|  *  Arbiter locks until last message in channel is sent */ | ||||
| class NastiReadDataArbiter(arbN: Int)(implicit p: Parameters) extends NastiModule { | ||||
|   val io = new Bundle { | ||||
|     val in = Vec(Decoupled(new NastiReadDataChannel), arbN).flip | ||||
|     val out = Decoupled(new NastiReadDataChannel) | ||||
|   } | ||||
|  | ||||
|   def rotateLeft[T <: Data](norm: Vec[T], rot: UInt): Vec[T] = { | ||||
|     val n = norm.size | ||||
|     Vec.tabulate(n) { i => | ||||
|       Mux(rot < UInt(n - i), norm(UInt(i) + rot), norm(rot - UInt(n - i))) | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   val lockIdx = Reg(init = UInt(0, log2Up(arbN))) | ||||
|   val locked = Reg(init = Bool(false)) | ||||
|  | ||||
|   // use rotation to give priority to the input after the last one granted | ||||
|   val choice = PriorityMux( | ||||
|     rotateLeft(Vec(io.in.map(_.valid)), lockIdx + UInt(1)), | ||||
|     rotateLeft(Vec((0 until arbN).map(UInt(_))), lockIdx + UInt(1))) | ||||
|  | ||||
|   val chosen = Mux(locked, lockIdx, choice) | ||||
|  | ||||
|   for (i <- 0 until arbN) { | ||||
|     io.in(i).ready := io.out.ready && chosen === UInt(i) | ||||
|   } | ||||
|  | ||||
|   io.out.valid := io.in(chosen).valid | ||||
|   io.out.bits := io.in(chosen).bits | ||||
|  | ||||
|   when (io.out.fire()) { | ||||
|     when (!locked) { | ||||
|       lockIdx := choice | ||||
|       locked := !io.out.bits.last | ||||
|     } .elsewhen (io.out.bits.last) { | ||||
|       locked := Bool(false) | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| /** A slave that send decode error for every request it receives */ | ||||
| class NastiErrorSlave(implicit p: Parameters) extends NastiModule { | ||||
|   val io = (new NastiIO).flip | ||||
| @@ -466,7 +424,10 @@ class NastiRouter(addrmap: Seq[(BigInt, BigInt)])(implicit p: Parameters) extend | ||||
|   io.master.w.ready := w_ready || err_slave.io.w.ready | ||||
|  | ||||
|   val b_arb = Module(new RRArbiter(new NastiWriteResponseChannel, nSlaves + 1)) | ||||
|   val r_arb = Module(new NastiReadDataArbiter(nSlaves + 1)) | ||||
|   val r_arb = Module(new JunctionsPeekingArbiter( | ||||
|     new NastiReadDataChannel, nSlaves + 1, | ||||
|     // we can unlock if it's the last beat | ||||
|     (r: NastiReadDataChannel) => r.last)) | ||||
|  | ||||
|   for (i <- 0 until nSlaves) { | ||||
|     b_arb.io.in(i) <> io.slave(i).b | ||||
|   | ||||
| @@ -58,3 +58,87 @@ object HellaQueue { | ||||
|     q.io.deq | ||||
|   } | ||||
| } | ||||
|  | ||||
| /** A generalized locking RR arbiter that addresses the limitations of the | ||||
|  *  version in the Chisel standard library */ | ||||
| abstract class JunctionsAbstractLockingArbiter[T <: Data](typ: T, arbN: Int) | ||||
|     extends Module { | ||||
|  | ||||
|   val io = new Bundle { | ||||
|     val in = Vec(Decoupled(typ.cloneType), arbN).flip | ||||
|     val out = Decoupled(typ.cloneType) | ||||
|   } | ||||
|  | ||||
|   def rotateLeft[T <: Data](norm: Vec[T], rot: UInt): Vec[T] = { | ||||
|     val n = norm.size | ||||
|     Vec.tabulate(n) { i => | ||||
|       Mux(rot < UInt(n - i), norm(UInt(i) + rot), norm(rot - UInt(n - i))) | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   val lockIdx = Reg(init = UInt(0, log2Up(arbN))) | ||||
|   val locked = Reg(init = Bool(false)) | ||||
|  | ||||
|   val choice = PriorityMux( | ||||
|     rotateLeft(Vec(io.in.map(_.valid)), lockIdx + UInt(1)), | ||||
|     rotateLeft(Vec((0 until arbN).map(UInt(_))), lockIdx + UInt(1))) | ||||
|  | ||||
|   val chosen = Mux(locked, lockIdx, choice) | ||||
|  | ||||
|   for (i <- 0 until arbN) { | ||||
|     io.in(i).ready := io.out.ready && chosen === UInt(i) | ||||
|   } | ||||
|  | ||||
|   io.out.valid := io.in(chosen).valid | ||||
|   io.out.bits := io.in(chosen).bits | ||||
| } | ||||
|  | ||||
| /** This locking arbiter determines when it is safe to unlock | ||||
|  *  by peeking at the data */ | ||||
| class JunctionsPeekingArbiter[T <: Data]( | ||||
|     typ: T, arbN: Int, | ||||
|     canUnlock: T => Bool, | ||||
|     needsLock: Option[T => Bool] = None) | ||||
|     extends JunctionsAbstractLockingArbiter(typ, arbN) { | ||||
|  | ||||
|   def realNeedsLock(data: T): Bool = | ||||
|     needsLock.map(_(data)).getOrElse(Bool(true)) | ||||
|  | ||||
|   when (io.out.fire()) { | ||||
|     when (!locked && realNeedsLock(io.out.bits)) { | ||||
|       lockIdx := choice | ||||
|       locked := Bool(true) | ||||
|     } | ||||
|     // the unlock statement takes precedent | ||||
|     when (canUnlock(io.out.bits)) { | ||||
|       locked := Bool(false) | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| /** This arbiter determines when it is safe to unlock by counting transactions */ | ||||
| class JunctionsCountingArbiter[T <: Data]( | ||||
|     typ: T, arbN: Int, count: Int, | ||||
|     val needsLock: Option[T => Bool] = None) | ||||
|     extends JunctionsAbstractLockingArbiter(typ, arbN) { | ||||
|  | ||||
|   def realNeedsLock(data: T): Bool = | ||||
|     needsLock.map(_(data)).getOrElse(Bool(true)) | ||||
|  | ||||
|   // if count is 1, you should use a non-locking arbiter | ||||
|   require(count > 1, "CountingArbiter cannot have count <= 1") | ||||
|  | ||||
|   val lock_ctr = Counter(count) | ||||
|  | ||||
|   when (io.out.fire()) { | ||||
|     when (!locked && realNeedsLock(io.out.bits)) { | ||||
|       lockIdx := choice | ||||
|       locked := Bool(true) | ||||
|       lock_ctr.inc() | ||||
|     } | ||||
|  | ||||
|     when (locked) { | ||||
|       when (lock_ctr.inc()) { locked := Bool(false) } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user