2017-01-26 23:31:12 +01:00
|
|
|
// See LICENSE.SiFive for license details.
|
|
|
|
|
2017-07-07 19:48:16 +02:00
|
|
|
package freechips.rocketchip.util
|
|
|
|
|
|
|
|
import Chisel._
|
|
|
|
|
2017-05-15 00:11:29 +02:00
|
|
|
// If you know two clocks are related with an N:M relationship, you
|
|
|
|
// can cross the clock domains with lower latency than an AsyncQueue.
|
|
|
|
// This crossing adds 1 cycle in the target clock domain.
|
2017-01-27 06:27:34 +01:00
|
|
|
|
2017-02-17 03:55:46 +01:00
|
|
|
// 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
|
2017-05-15 00:11:29 +02:00
|
|
|
// timing must be met for the least-common-multiple of the clocks.
|
2017-02-17 03:55:46 +01:00
|
|
|
case object Symmetric extends RationalDirection {
|
|
|
|
def flip = Symmetric
|
|
|
|
}
|
|
|
|
|
2017-05-15 00:11:29 +02:00
|
|
|
// Like Symmetric, this crossing works for all ratios N:M.
|
|
|
|
// However, unlike the other crossing options, this varient adds
|
|
|
|
// a full flow+pipe buffer on both sides of the crossing. This
|
|
|
|
// ends up costing potentially two cycles of delay, but gives
|
|
|
|
// both clock domains a full clock period to close timing.
|
|
|
|
case object Flexible extends RationalDirection {
|
|
|
|
def flip = Flexible
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the source is N:1 of the sink, place the registers at the sink.
|
|
|
|
// This imposes only a single clock cycle of delay and both side of
|
|
|
|
// the crossing have a full clock period to close timing.
|
2017-02-17 03:55:46 +01:00
|
|
|
case object FastToSlow extends RationalDirection {
|
|
|
|
def flip = SlowToFast
|
|
|
|
}
|
|
|
|
|
2017-05-15 00:11:29 +02:00
|
|
|
// If the source is 1:N of the sink, place the registers at the source.
|
|
|
|
// This imposes only a single clock cycle of delay and both side of
|
|
|
|
// the crossing have a full clock period to close timing.
|
2017-02-17 03:55:46 +01:00
|
|
|
case object SlowToFast extends RationalDirection {
|
|
|
|
def flip = FastToSlow
|
|
|
|
}
|
|
|
|
|
2017-01-26 23:31:12 +01:00
|
|
|
final class RationalIO[T <: Data](gen: T) extends Bundle
|
|
|
|
{
|
2017-07-31 07:31:19 +02:00
|
|
|
val bits0 = gen.chiselCloneType
|
|
|
|
val bits1 = gen.chiselCloneType
|
2017-01-26 23:31:12 +01:00
|
|
|
val valid = Bool()
|
|
|
|
val source = UInt(width = 2)
|
|
|
|
val ready = Bool().flip
|
|
|
|
val sink = UInt(width = 2).flip
|
|
|
|
|
|
|
|
override def cloneType: this.type = new RationalIO(gen).asInstanceOf[this.type]
|
|
|
|
}
|
|
|
|
|
|
|
|
object RationalIO
|
|
|
|
{
|
|
|
|
def apply[T <: Data](gen: T) = new RationalIO(gen)
|
|
|
|
}
|
|
|
|
|
2017-02-17 03:55:46 +01:00
|
|
|
class RationalCrossingSource[T <: Data](gen: T, direction: RationalDirection = Symmetric) extends Module
|
2017-01-26 23:31:12 +01:00
|
|
|
{
|
|
|
|
val io = new Bundle {
|
|
|
|
val enq = DecoupledIO(gen).flip
|
|
|
|
val deq = RationalIO(gen)
|
|
|
|
}
|
|
|
|
|
|
|
|
val deq = io.deq
|
2017-02-17 03:55:46 +01:00
|
|
|
val enq = direction match {
|
2017-09-06 23:27:45 +02:00
|
|
|
case Symmetric => ShiftQueue(io.enq, 1, flow=true)
|
|
|
|
case Flexible => ShiftQueue(io.enq, 2)
|
2017-02-17 03:55:46 +01:00
|
|
|
case FastToSlow => io.enq
|
2017-09-06 23:27:45 +02:00
|
|
|
case SlowToFast => ShiftQueue(io.enq, 2)
|
2017-02-17 03:55:46 +01:00
|
|
|
}
|
2017-01-26 23:31:12 +01:00
|
|
|
|
|
|
|
val count = RegInit(UInt(0, width = 2))
|
|
|
|
val equal = count === deq.sink
|
|
|
|
|
|
|
|
deq.valid := enq.valid
|
|
|
|
deq.source := count
|
2017-07-31 07:31:19 +02:00
|
|
|
deq.bits0 := enq.bits
|
|
|
|
deq.bits1 := RegEnable(enq.bits, equal)
|
2017-01-26 23:31:12 +01:00
|
|
|
enq.ready := Mux(equal, deq.ready, count(1) =/= deq.sink(0))
|
|
|
|
|
|
|
|
when (enq.fire()) { count := Cat(count(0), !count(1)) }
|
2017-02-17 03:55:46 +01:00
|
|
|
|
|
|
|
// Ensure the clocking is setup correctly
|
|
|
|
direction match {
|
|
|
|
case Symmetric => () // always safe
|
2017-05-15 00:11:29 +02:00
|
|
|
case Flexible => ()
|
2017-02-17 03:55:46 +01:00
|
|
|
case FastToSlow => assert (equal || count(1) === deq.sink(0))
|
|
|
|
case SlowToFast => assert (equal || count(1) =/= deq.sink(0))
|
|
|
|
}
|
2017-01-26 23:31:12 +01:00
|
|
|
}
|
|
|
|
|
2017-02-17 03:55:46 +01:00
|
|
|
class RationalCrossingSink[T <: Data](gen: T, direction: RationalDirection = Symmetric) extends Module
|
2017-01-26 23:31:12 +01:00
|
|
|
{
|
|
|
|
val io = new Bundle {
|
|
|
|
val enq = RationalIO(gen).flip
|
|
|
|
val deq = DecoupledIO(gen)
|
|
|
|
}
|
|
|
|
|
|
|
|
val enq = io.enq
|
|
|
|
val deq = Wire(io.deq)
|
2017-02-17 03:55:46 +01:00
|
|
|
direction match {
|
2017-09-06 23:27:45 +02:00
|
|
|
case Symmetric => io.deq <> ShiftQueue(deq, 1, pipe=true)
|
|
|
|
case Flexible => io.deq <> ShiftQueue(deq, 2)
|
|
|
|
case FastToSlow => io.deq <> ShiftQueue(deq, 2)
|
2017-02-17 03:55:46 +01:00
|
|
|
case SlowToFast => io.deq <> deq
|
|
|
|
}
|
2017-01-26 23:31:12 +01:00
|
|
|
|
|
|
|
val count = RegInit(UInt(0, width = 2))
|
|
|
|
val equal = count === enq.source
|
|
|
|
|
|
|
|
enq.ready := deq.ready
|
|
|
|
enq.sink := count
|
2017-07-31 07:31:19 +02:00
|
|
|
deq.bits := Mux(equal, enq.bits0, enq.bits1)
|
2017-01-26 23:31:12 +01:00
|
|
|
deq.valid := Mux(equal, enq.valid, count(1) =/= enq.source(0))
|
|
|
|
|
|
|
|
when (deq.fire()) { count := Cat(count(0), !count(1)) }
|
2017-02-17 03:55:46 +01:00
|
|
|
|
|
|
|
// Ensure the clocking is setup correctly
|
|
|
|
direction match {
|
|
|
|
case Symmetric => () // always safe
|
2017-05-15 00:11:29 +02:00
|
|
|
case Flexible => ()
|
2017-02-17 03:55:46 +01:00
|
|
|
case FastToSlow => assert (equal || count(1) =/= enq.source(0))
|
|
|
|
case SlowToFast => assert (equal || count(1) === enq.source(0))
|
|
|
|
}
|
2017-01-26 23:31:12 +01:00
|
|
|
}
|
|
|
|
|
2017-04-20 01:16:05 +02:00
|
|
|
class RationalCrossingFull[T <: Data](gen: T, direction: RationalDirection = Symmetric) extends Module
|
2017-01-26 23:31:12 +01:00
|
|
|
{
|
|
|
|
val io = new CrossingIO(gen)
|
|
|
|
|
2017-02-17 03:55:46 +01:00
|
|
|
val source = Module(new RationalCrossingSource(gen, direction))
|
|
|
|
val sink = Module(new RationalCrossingSink(gen, direction))
|
2017-01-26 23:31:12 +01:00
|
|
|
|
|
|
|
source.clock := io.enq_clock
|
|
|
|
source.reset := io.enq_reset
|
|
|
|
sink .clock := io.deq_clock
|
|
|
|
sink .reset := io.deq_reset
|
|
|
|
|
|
|
|
source.io.enq <> io.enq
|
|
|
|
io.deq <> sink.io.deq
|
|
|
|
}
|
|
|
|
|
|
|
|
object ToRational
|
|
|
|
{
|
2017-02-17 03:55:46 +01:00
|
|
|
def apply[T <: Data](x: DecoupledIO[T], direction: RationalDirection = Symmetric): RationalIO[T] = {
|
|
|
|
val source = Module(new RationalCrossingSource(x.bits, direction))
|
2017-01-26 23:31:12 +01:00
|
|
|
source.io.enq <> x
|
|
|
|
source.io.deq
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
object FromRational
|
|
|
|
{
|
2017-02-17 03:55:46 +01:00
|
|
|
def apply[T <: Data](x: RationalIO[T], direction: RationalDirection = Symmetric): DecoupledIO[T] = {
|
2017-07-31 07:31:19 +02:00
|
|
|
val sink = Module(new RationalCrossingSink(x.bits0, direction))
|
2017-01-26 23:31:12 +01:00
|
|
|
sink.io.enq <> x
|
|
|
|
sink.io.deq
|
|
|
|
}
|
|
|
|
}
|