2015-10-02 23:19:51 +02:00
|
|
|
/// See LICENSE for license details.
|
|
|
|
package junctions
|
|
|
|
import Chisel._
|
2015-10-22 03:15:46 +02:00
|
|
|
import cde.Parameters
|
2015-10-02 23:19:51 +02:00
|
|
|
|
|
|
|
object bigIntPow2 {
|
|
|
|
def apply(in: BigInt): Boolean = in > 0 && ((in & (in-1)) == 0)
|
|
|
|
}
|
2015-10-03 00:37:41 +02:00
|
|
|
|
2015-10-06 05:33:55 +02:00
|
|
|
class ParameterizedBundle(implicit p: Parameters) extends Bundle {
|
2016-02-05 00:26:42 +01:00
|
|
|
override def cloneType = {
|
|
|
|
try {
|
|
|
|
this.getClass.getConstructors.head.newInstance(p).asInstanceOf[this.type]
|
|
|
|
} catch {
|
|
|
|
case e: java.lang.IllegalArgumentException =>
|
|
|
|
throwException("Unable to use ParamaterizedBundle.cloneType on " +
|
|
|
|
this.getClass + ", probably because " + this.getClass +
|
|
|
|
"() takes more than one argument. Consider overriding " +
|
|
|
|
"cloneType() on " + this.getClass, e)
|
|
|
|
}
|
|
|
|
}
|
2015-10-03 00:37:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
class HellaFlowQueue[T <: Data](val entries: Int)(data: => T) extends Module {
|
|
|
|
val io = new QueueIO(data, entries)
|
|
|
|
require(entries > 1)
|
|
|
|
|
|
|
|
val do_flow = Wire(Bool())
|
|
|
|
val do_enq = io.enq.fire() && !do_flow
|
|
|
|
val do_deq = io.deq.fire() && !do_flow
|
|
|
|
|
|
|
|
val maybe_full = Reg(init=Bool(false))
|
|
|
|
val enq_ptr = Counter(do_enq, entries)._1
|
|
|
|
val (deq_ptr, deq_done) = Counter(do_deq, entries)
|
2016-01-14 22:38:00 +01:00
|
|
|
when (do_enq =/= do_deq) { maybe_full := do_enq }
|
2015-10-03 00:37:41 +02:00
|
|
|
|
|
|
|
val ptr_match = enq_ptr === deq_ptr
|
|
|
|
val empty = ptr_match && !maybe_full
|
|
|
|
val full = ptr_match && maybe_full
|
|
|
|
val atLeastTwo = full || enq_ptr - deq_ptr >= UInt(2)
|
|
|
|
do_flow := empty && io.deq.ready
|
|
|
|
|
2016-01-14 22:38:00 +01:00
|
|
|
val ram = SeqMem(entries, data)
|
2015-10-03 00:37:41 +02:00
|
|
|
when (do_enq) { ram.write(enq_ptr, io.enq.bits) }
|
|
|
|
|
|
|
|
val ren = io.deq.ready && (atLeastTwo || !io.deq.valid && !empty)
|
|
|
|
val raddr = Mux(io.deq.valid, Mux(deq_done, UInt(0), deq_ptr + UInt(1)), deq_ptr)
|
|
|
|
val ram_out_valid = Reg(next = ren)
|
|
|
|
|
|
|
|
io.deq.valid := Mux(empty, io.enq.valid, ram_out_valid)
|
|
|
|
io.enq.ready := !full
|
|
|
|
io.deq.bits := Mux(empty, io.enq.bits, ram.read(raddr, ren))
|
|
|
|
}
|
|
|
|
|
|
|
|
class HellaQueue[T <: Data](val entries: Int)(data: => T) extends Module {
|
|
|
|
val io = new QueueIO(data, entries)
|
|
|
|
|
|
|
|
val fq = Module(new HellaFlowQueue(entries)(data))
|
|
|
|
fq.io.enq <> io.enq
|
|
|
|
io.deq <> Queue(fq.io.deq, 1, pipe = true)
|
|
|
|
}
|
|
|
|
|
|
|
|
object HellaQueue {
|
|
|
|
def apply[T <: Data](enq: DecoupledIO[T], entries: Int) = {
|
|
|
|
val q = Module((new HellaQueue(entries)) { enq.bits })
|
|
|
|
q.io.enq.valid := enq.valid // not using <> so that override is allowed
|
|
|
|
q.io.enq.bits := enq.bits
|
|
|
|
enq.ready := q.io.enq.ready
|
|
|
|
q.io.deq
|
|
|
|
}
|
|
|
|
}
|
2015-10-21 03:36:19 +02:00
|
|
|
|
|
|
|
/** 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 {
|
2016-01-14 22:38:00 +01:00
|
|
|
val in = Vec(arbN, Decoupled(typ.cloneType)).flip
|
2015-10-21 03:36:19 +02:00
|
|
|
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) }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-01-22 00:37:07 +01:00
|
|
|
|
|
|
|
class ReorderQueueWrite[T <: Data](dType: T, tagWidth: Int) extends Bundle {
|
|
|
|
val data = dType.cloneType
|
|
|
|
val tag = UInt(width = tagWidth)
|
|
|
|
|
|
|
|
override def cloneType =
|
|
|
|
new ReorderQueueWrite(dType, tagWidth).asInstanceOf[this.type]
|
|
|
|
}
|
|
|
|
|
|
|
|
class ReorderEnqueueIO[T <: Data](dType: T, tagWidth: Int)
|
2016-01-24 06:36:16 +01:00
|
|
|
extends DecoupledIO(new ReorderQueueWrite(dType, tagWidth)) {
|
|
|
|
|
|
|
|
override def cloneType =
|
|
|
|
new ReorderEnqueueIO(dType, tagWidth).asInstanceOf[this.type]
|
|
|
|
}
|
2016-01-22 00:37:07 +01:00
|
|
|
|
|
|
|
class ReorderDequeueIO[T <: Data](dType: T, tagWidth: Int) extends Bundle {
|
|
|
|
val valid = Bool(INPUT)
|
|
|
|
val tag = UInt(INPUT, tagWidth)
|
|
|
|
val data = dType.cloneType.asOutput
|
|
|
|
val matches = Bool(OUTPUT)
|
|
|
|
|
|
|
|
override def cloneType =
|
|
|
|
new ReorderDequeueIO(dType, tagWidth).asInstanceOf[this.type]
|
|
|
|
}
|
|
|
|
|
|
|
|
class ReorderQueue[T <: Data](dType: T, tagWidth: Int, size: Int)
|
|
|
|
extends Module {
|
|
|
|
val io = new Bundle {
|
|
|
|
val enq = new ReorderEnqueueIO(dType, tagWidth).flip
|
|
|
|
val deq = new ReorderDequeueIO(dType, tagWidth)
|
|
|
|
}
|
|
|
|
|
|
|
|
val roq_data = Reg(Vec(size, dType.cloneType))
|
|
|
|
val roq_tags = Reg(Vec(size, UInt(width = tagWidth)))
|
|
|
|
val roq_free = Reg(init = Vec.fill(size)(Bool(true)))
|
|
|
|
|
|
|
|
val roq_enq_addr = PriorityEncoder(roq_free)
|
|
|
|
val roq_matches = roq_tags.zip(roq_free)
|
|
|
|
.map { case (tag, free) => tag === io.deq.tag && !free }
|
|
|
|
val roq_deq_addr = PriorityEncoder(roq_matches)
|
|
|
|
|
|
|
|
io.enq.ready := roq_free.reduce(_ || _)
|
|
|
|
io.deq.data := roq_data(roq_deq_addr)
|
|
|
|
io.deq.matches := roq_matches.reduce(_ || _)
|
|
|
|
|
|
|
|
when (io.enq.valid && io.enq.ready) {
|
|
|
|
roq_data(roq_enq_addr) := io.enq.bits.data
|
|
|
|
roq_tags(roq_enq_addr) := io.enq.bits.tag
|
|
|
|
roq_free(roq_enq_addr) := Bool(false)
|
|
|
|
}
|
|
|
|
|
|
|
|
when (io.deq.valid) {
|
|
|
|
roq_free(roq_deq_addr) := Bool(true)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
object DecoupledHelper {
|
|
|
|
def apply(rvs: Bool*) = new DecoupledHelper(rvs)
|
|
|
|
}
|
|
|
|
|
|
|
|
class DecoupledHelper(val rvs: Seq[Bool]) {
|
|
|
|
def fire(exclude: Bool, includes: Bool*) = {
|
|
|
|
(rvs.filter(_ ne exclude) ++ includes).reduce(_ && _)
|
|
|
|
}
|
|
|
|
}
|
2016-02-17 06:45:39 +01:00
|
|
|
|
|
|
|
class MultiWidthFifo(inW: Int, outW: Int, n: Int) extends Module {
|
|
|
|
val io = new Bundle {
|
|
|
|
val in = Decoupled(Bits(width = inW)).flip
|
|
|
|
val out = Decoupled(Bits(width = outW))
|
|
|
|
}
|
|
|
|
|
|
|
|
if (inW == outW) {
|
|
|
|
io.out <> Queue(io.in, n)
|
|
|
|
} else if (inW > outW) {
|
|
|
|
val nBeats = inW / outW
|
|
|
|
|
|
|
|
require(inW % outW == 0, s"MultiWidthFifo: in: $inW not divisible by out: $outW")
|
|
|
|
require(n % nBeats == 0, s"Cannot store $n output words when output beats is $nBeats")
|
|
|
|
|
|
|
|
val wdata = Reg(Vec(n / nBeats, Bits(width = inW)))
|
|
|
|
val rdata = Vec(wdata.flatMap { indat =>
|
|
|
|
(0 until nBeats).map(i => indat(outW * (i + 1) - 1, outW * i)) })
|
|
|
|
|
|
|
|
val head = Reg(init = UInt(0, log2Up(n / nBeats)))
|
|
|
|
val tail = Reg(init = UInt(0, log2Up(n)))
|
|
|
|
val size = Reg(init = UInt(0, log2Up(n + 1)))
|
|
|
|
|
|
|
|
when (io.in.fire()) {
|
|
|
|
wdata(head) := io.in.bits
|
|
|
|
head := head + UInt(1)
|
|
|
|
}
|
|
|
|
|
|
|
|
when (io.out.fire()) { tail := tail + UInt(1) }
|
|
|
|
|
|
|
|
size := MuxCase(size, Seq(
|
|
|
|
(io.in.fire() && io.out.fire()) -> (size + UInt(nBeats - 1)),
|
|
|
|
io.in.fire() -> (size + UInt(nBeats)),
|
|
|
|
io.out.fire() -> (size - UInt(1))))
|
|
|
|
|
|
|
|
io.out.valid := size > UInt(0)
|
|
|
|
io.out.bits := rdata(tail)
|
|
|
|
io.in.ready := size < UInt(n)
|
|
|
|
} else {
|
|
|
|
val nBeats = outW / inW
|
|
|
|
|
|
|
|
require(outW % inW == 0, s"MultiWidthFifo: out: $outW not divisible by in: $inW")
|
|
|
|
|
|
|
|
val wdata = Reg(Vec(n * nBeats, Bits(width = inW)))
|
|
|
|
val rdata = Vec.tabulate(n) { i =>
|
|
|
|
Cat(wdata.slice(i * nBeats, (i + 1) * nBeats).reverse)}
|
|
|
|
|
|
|
|
val head = Reg(init = UInt(0, log2Up(n * nBeats)))
|
|
|
|
val tail = Reg(init = UInt(0, log2Up(n)))
|
|
|
|
val size = Reg(init = UInt(0, log2Up(n * nBeats + 1)))
|
|
|
|
|
|
|
|
when (io.in.fire()) {
|
|
|
|
wdata(head) := io.in.bits
|
|
|
|
head := head + UInt(1)
|
|
|
|
}
|
|
|
|
|
|
|
|
when (io.out.fire()) { tail := tail + UInt(1) }
|
|
|
|
|
|
|
|
size := MuxCase(size, Seq(
|
|
|
|
(io.in.fire() && io.out.fire()) -> (size - UInt(nBeats - 1)),
|
|
|
|
io.in.fire() -> (size + UInt(1)),
|
|
|
|
io.out.fire() -> (size - UInt(nBeats))))
|
|
|
|
|
|
|
|
io.out.valid := size >= UInt(nBeats)
|
|
|
|
io.out.bits := rdata(tail)
|
|
|
|
io.in.ready := size < UInt(n * nBeats)
|
|
|
|
}
|
|
|
|
}
|