1
0

util: add fast2slow direction option to rational crossings

If you manually specify which side of the crossing is slow, you
can move the registers fully to that clock domain.
This commit is contained in:
Wesley W. Terpstra 2017-02-17 03:55:46 +01:00
parent e51609aec0
commit bb334a2cf5

View File

@ -7,6 +7,32 @@
package util package util
import Chisel._ import Chisel._
// A rational crossing must put registers on the slow side.
// This trait covers the options of how/where to put the registers.
// BEWARE: the source+sink must agree on the direction!
sealed trait RationalDirection {
def flip: RationalDirection
}
// If it's unclear which side will be slow (or it is variable),
// place registers on both sides of the crossing, by splitting
// a Queue into flow and pipe parts on either side. This is safe
// for all possible clock ratios, but has the downside that the
// path from the slow domain must close timing in the fast domain.
case object Symmetric extends RationalDirection {
def flip = Symmetric
}
// If the source is fast, place the registers at the sink.
case object FastToSlow extends RationalDirection {
def flip = SlowToFast
}
// If the source is slow, place the registers at the source.
case object SlowToFast extends RationalDirection {
def flip = FastToSlow
}
final class RationalIO[T <: Data](gen: T) extends Bundle final class RationalIO[T <: Data](gen: T) extends Bundle
{ {
val bits = gen.chiselCloneType val bits = gen.chiselCloneType
@ -23,15 +49,19 @@ object RationalIO
def apply[T <: Data](gen: T) = new RationalIO(gen) def apply[T <: Data](gen: T) = new RationalIO(gen)
} }
class RationalCrossingSource[T <: Data](gen: T) extends Module class RationalCrossingSource[T <: Data](gen: T, direction: RationalDirection = Symmetric) extends Module
{ {
val io = new Bundle { val io = new Bundle {
val enq = DecoupledIO(gen).flip val enq = DecoupledIO(gen).flip
val deq = RationalIO(gen) val deq = RationalIO(gen)
} }
val enq = Queue(io.enq, 1, flow=true)
val deq = io.deq val deq = io.deq
val enq = direction match {
case Symmetric => Queue(io.enq, 1, flow=true)
case FastToSlow => io.enq
case SlowToFast => Queue(io.enq, 2)
}
val count = RegInit(UInt(0, width = 2)) val count = RegInit(UInt(0, width = 2))
val equal = count === deq.sink val equal = count === deq.sink
@ -42,9 +72,16 @@ class RationalCrossingSource[T <: Data](gen: T) extends Module
enq.ready := Mux(equal, deq.ready, count(1) =/= deq.sink(0)) enq.ready := Mux(equal, deq.ready, count(1) =/= deq.sink(0))
when (enq.fire()) { count := Cat(count(0), !count(1)) } when (enq.fire()) { count := Cat(count(0), !count(1)) }
// Ensure the clocking is setup correctly
direction match {
case Symmetric => () // always safe
case FastToSlow => assert (equal || count(1) === deq.sink(0))
case SlowToFast => assert (equal || count(1) =/= deq.sink(0))
}
} }
class RationalCrossingSink[T <: Data](gen: T) extends Module class RationalCrossingSink[T <: Data](gen: T, direction: RationalDirection = Symmetric) extends Module
{ {
val io = new Bundle { val io = new Bundle {
val enq = RationalIO(gen).flip val enq = RationalIO(gen).flip
@ -53,7 +90,11 @@ class RationalCrossingSink[T <: Data](gen: T) extends Module
val enq = io.enq val enq = io.enq
val deq = Wire(io.deq) val deq = Wire(io.deq)
io.deq <> Queue(deq, 1, pipe=true) direction match {
case Symmetric => io.deq <> Queue(deq, 1, pipe=true)
case FastToSlow => io.deq <> Queue(deq, 2)
case SlowToFast => io.deq <> deq
}
val count = RegInit(UInt(0, width = 2)) val count = RegInit(UInt(0, width = 2))
val equal = count === enq.source val equal = count === enq.source
@ -64,14 +105,21 @@ class RationalCrossingSink[T <: Data](gen: T) extends Module
deq.valid := Mux(equal, enq.valid, count(1) =/= enq.source(0)) deq.valid := Mux(equal, enq.valid, count(1) =/= enq.source(0))
when (deq.fire()) { count := Cat(count(0), !count(1)) } when (deq.fire()) { count := Cat(count(0), !count(1)) }
// Ensure the clocking is setup correctly
direction match {
case Symmetric => () // always safe
case FastToSlow => assert (equal || count(1) =/= enq.source(0))
case SlowToFast => assert (equal || count(1) === enq.source(0))
}
} }
class RationalCrossing[T <: Data](gen: T) extends Module class RationalCrossing[T <: Data](gen: T, direction: RationalDirection = Symmetric) extends Module
{ {
val io = new CrossingIO(gen) val io = new CrossingIO(gen)
val source = Module(new RationalCrossingSource(gen)) val source = Module(new RationalCrossingSource(gen, direction))
val sink = Module(new RationalCrossingSink(gen)) val sink = Module(new RationalCrossingSink(gen, direction))
source.clock := io.enq_clock source.clock := io.enq_clock
source.reset := io.enq_reset source.reset := io.enq_reset
@ -84,8 +132,8 @@ class RationalCrossing[T <: Data](gen: T) extends Module
object ToRational object ToRational
{ {
def apply[T <: Data](x: DecoupledIO[T]): RationalIO[T] = { def apply[T <: Data](x: DecoupledIO[T], direction: RationalDirection = Symmetric): RationalIO[T] = {
val source = Module(new RationalCrossingSource(x.bits)) val source = Module(new RationalCrossingSource(x.bits, direction))
source.io.enq <> x source.io.enq <> x
source.io.deq source.io.deq
} }
@ -93,8 +141,8 @@ object ToRational
object FromRational object FromRational
{ {
def apply[T <: Data](x: RationalIO[T]): DecoupledIO[T] = { def apply[T <: Data](x: RationalIO[T], direction: RationalDirection = Symmetric): DecoupledIO[T] = {
val sink = Module(new RationalCrossingSink(x.bits)) val sink = Module(new RationalCrossingSink(x.bits, direction))
sink.io.enq <> x sink.io.enq <> x
sink.io.deq sink.io.deq
} }