diff --git a/src/main/scala/uncore/tilelink2/Arbiter.scala b/src/main/scala/uncore/tilelink2/Arbiter.scala new file mode 100644 index 00000000..1981a818 --- /dev/null +++ b/src/main/scala/uncore/tilelink2/Arbiter.scala @@ -0,0 +1,69 @@ +// See LICENSE for license details. + +package uncore.tilelink2 + +import Chisel._ +import chisel3.util.IrrevocableIO + +object TLArbiter +{ + // (valids, idle) => readys + type Policy = (Seq[Bool], Bool) => Seq[Bool] + + val lowestIndexFirst: Policy = (valids, idle) => + valids.scanLeft(Bool(true))(_ && !_).init + + def apply[T <: Data](policy: Policy)(sink: IrrevocableIO[T], sources: (UInt, IrrevocableIO[T])*) { + require (sources.size >= 1) + + val pairs = sources.toList + val beatsIn = pairs.map(_._1) + val sourcesIn = pairs.map(_._2) + + // The number of beats which remain to be sent + val beatsLeft = RegInit(UInt(0)) + val idle = beatsLeft === UInt(0) + + // Who wants access to the sink? + val valids = sourcesIn.map(_.valid) + // Arbitrate amongst the requests + val readys = Vec(policy(valids, idle)) + // Which request wins arbitration? + val winners = Vec((readys zip valids) map { case (r,v) => r&&v }) + + // Confirm the policy works properly + require (readys.size == valids.size) + // Never two winners + val prefixOR = winners.scanLeft(Bool(false))(_||_).init + assert((prefixOR zip winners) map { case (p,w) => !p || !w } reduce {_ && _}) + // If there was any request, there is a winner + assert (!valids.reduce(_||_) || winners.reduce(_||_)) + + // Track remaining beats + val maskedBeats = (winners zip beatsIn) map { case (w,b) => Mux(w, b, UInt(0)) } + val initBeats = maskedBeats.reduce(_ | _) // no winner => 0 beats + val todoBeats = Mux(idle, initBeats, beatsLeft) + beatsLeft := todoBeats - sink.fire() + assert (!sink.fire() || todoBeats =/= UInt(0)) // underflow is impoosible + + // The one-hot source granted access in the previous cycle + val state = RegInit(Vec.fill(sources.size)(Bool(false))) + val muxState = Mux(idle, winners, state) + state := muxState + + val ones = Vec.fill(sources.size)(Bool(true)) + val picked = Mux(idle, ones, state) + sink.valid := Mux1H(picked, valids) + + if (sources.size > 1) { + val allowed = Mux(idle, readys, state) + (sourcesIn zip allowed) foreach { case (s, r) => + s.ready := sink.ready && r + } + } else { + sourcesIn(0).ready := sink.ready + } + + sink.bits := Mux1H(muxState, sourcesIn.map(_.bits)) + } +}