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:
parent
e51609aec0
commit
bb334a2cf5
@ -7,6 +7,32 @@
|
||||
package util
|
||||
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
|
||||
{
|
||||
val bits = gen.chiselCloneType
|
||||
@ -23,15 +49,19 @@ object RationalIO
|
||||
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 enq = DecoupledIO(gen).flip
|
||||
val deq = RationalIO(gen)
|
||||
}
|
||||
|
||||
val enq = Queue(io.enq, 1, flow=true)
|
||||
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 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))
|
||||
|
||||
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 enq = RationalIO(gen).flip
|
||||
@ -53,7 +90,11 @@ class RationalCrossingSink[T <: Data](gen: T) extends Module
|
||||
|
||||
val enq = io.enq
|
||||
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 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))
|
||||
|
||||
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 source = Module(new RationalCrossingSource(gen))
|
||||
val sink = Module(new RationalCrossingSink(gen))
|
||||
val source = Module(new RationalCrossingSource(gen, direction))
|
||||
val sink = Module(new RationalCrossingSink(gen, direction))
|
||||
|
||||
source.clock := io.enq_clock
|
||||
source.reset := io.enq_reset
|
||||
@ -84,8 +132,8 @@ class RationalCrossing[T <: Data](gen: T) extends Module
|
||||
|
||||
object ToRational
|
||||
{
|
||||
def apply[T <: Data](x: DecoupledIO[T]): RationalIO[T] = {
|
||||
val source = Module(new RationalCrossingSource(x.bits))
|
||||
def apply[T <: Data](x: DecoupledIO[T], direction: RationalDirection = Symmetric): RationalIO[T] = {
|
||||
val source = Module(new RationalCrossingSource(x.bits, direction))
|
||||
source.io.enq <> x
|
||||
source.io.deq
|
||||
}
|
||||
@ -93,8 +141,8 @@ object ToRational
|
||||
|
||||
object FromRational
|
||||
{
|
||||
def apply[T <: Data](x: RationalIO[T]): DecoupledIO[T] = {
|
||||
val sink = Module(new RationalCrossingSink(x.bits))
|
||||
def apply[T <: Data](x: RationalIO[T], direction: RationalDirection = Symmetric): DecoupledIO[T] = {
|
||||
val sink = Module(new RationalCrossingSink(x.bits, direction))
|
||||
sink.io.enq <> x
|
||||
sink.io.deq
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user