generalize NastiReadDataArbiter
This commit is contained in:
parent
cedef98045
commit
11eacda84a
@ -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) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user