1
0

Merge pull request #556 from ucb-bar/rational-config

Rational config
This commit is contained in:
Wesley W. Terpstra 2017-02-17 20:31:56 +01:00 committed by GitHub
commit 6ea35125d4
12 changed files with 215 additions and 75 deletions

View File

@ -47,6 +47,11 @@ $(output_dir)/%.vpd: $(output_dir)/% $(emu_debug)
vcd2vpd $@.vcd $@ > /dev/null &
./$(emu_debug) +max-cycles=$(timeout_cycles) +verbose -v$@.vcd $< $(disasm) $(patsubst %.vpd,%.out,$@) && [ $$PIPESTATUS -eq 0 ]
$(output_dir)/%.fst: $(output_dir)/% $(emu_debug)
rm -rf $@.vcd && mkfifo $@.vcd
vcd2fst -Z $@.vcd $@ &
./$(emu_debug) +max-cycles=$(timeout_cycles) +verbose -v$@.vcd $< $(disasm) $(patsubst %.fst,%.out,$@) && [ $$PIPESTATUS -eq 0 ]
run: run-asm-tests run-bmark-tests
run-debug: run-asm-tests-debug run-bmark-tests-debug
run-fast: run-asm-tests-fast run-bmark-tests-fast

View File

@ -73,7 +73,7 @@ trait HasRocketTiles extends CoreplexRISCVPlatform {
}
case Rational => {
val wrapper = LazyModule(new RationalRocketTile(c)(pWithExtra))
val sink = LazyModule(new TLRationalCrossingSink)
val sink = LazyModule(new TLRationalCrossingSink(util.FastToSlow))
val source = LazyModule(new TLRationalCrossingSource)
sink.node :=* wrapper.masterNode
l1tol2.node :=* sink.node

View File

@ -103,7 +103,7 @@ class RationalRocketTile(rtp: RocketTileParams)(implicit p: Parameters) extends
masterNode :=* source.node
val slaveNode = TLRationalInputNode()
val sink = LazyModule(new TLRationalCrossingSink)
val sink = LazyModule(new TLRationalCrossingSink(util.SlowToFast))
rocket.slaveNode :*= sink.node
sink.node :*= slaveNode

View File

@ -26,6 +26,9 @@ run-$makeTargetName: $$(addprefix $$(output_dir)/, $$(addsuffix .out, $$($makeTa
run-$makeTargetName-debug: $$(addprefix $$(output_dir)/, $$(addsuffix .vpd, $$($makeTargetName)))
\t@echo; perl -ne 'print " [$$$$1] $$$$ARGV \\t$$$$2\\n" if( /\\*{3}(.{8})\\*{3}(.*)/ || /ASSERTION (FAILED)/i )' $$(patsubst %.vpd,%.out,$$^) /dev/null | perl -ne 'if(/(.*)/){print "$$$$1\\n\\n"; exit(1) if eof()}'
run-$makeTargetName-fst: $$(addprefix $$(output_dir)/, $$(addsuffix .fst, $$($makeTargetName)))
\t@echo; perl -ne 'print " [$$$$1] $$$$ARGV \\t$$$$2\\n" if( /\\*{3}(.{8})\\*{3}(.*)/ || /ASSERTION (FAILED)/i )' $$(patsubst %.fst,%.out,$$^) /dev/null | perl -ne 'if(/(.*)/){print "$$$$1\\n\\n"; exit(1) if eof()}'
"""
}
@ -78,6 +81,8 @@ run-$kind-$env-tests: $$(addprefix $$(output_dir)/, $$(addsuffix .out, $suites))
\t@echo; perl -ne 'print " [$$$$1] $$$$ARGV \\t$$$$2\\n" if( /\\*{3}(.{8})\\*{3}(.*)/ || /ASSERTION (FAILED)/i )' $$^ /dev/null | perl -ne 'if(/(.*)/){print "$$$$1\\n\\n"; exit(1) if eof()}'
run-$kind-$env-tests-debug: $$(addprefix $$(output_dir)/, $$(addsuffix .vpd, $suites))
\t@echo; perl -ne 'print " [$$$$1] $$$$ARGV \\t$$$$2\\n" if( /\\*{3}(.{8})\\*{3}(.*)/ || /ASSERTION (FAILED)/i )' $$(patsubst %.vpd,%.out,$$^) /dev/null | perl -ne 'if(/(.*)/){print "$$$$1\\n\\n"; exit(1) if eof()}'
run-$kind-$env-tests-fst: $$(addprefix $$(output_dir)/, $$(addsuffix .fst, $suites))
\t@echo; perl -ne 'print " [$$$$1] $$$$ARGV \\t$$$$2\\n" if( /\\*{3}(.{8})\\*{3}(.*)/ || /ASSERTION (FAILED)/i )' $$(patsubst %.fst,%.out,$$^) /dev/null | perl -ne 'if(/(.*)/){print "$$$$1\\n\\n"; exit(1) if eof()}'
run-$kind-$env-tests-fast: $$(addprefix $$(output_dir)/, $$(addsuffix .run, $suites))
\t@echo; perl -ne 'print " [$$$$1] $$$$ARGV \\t$$$$2\\n" if( /\\*{3}(.{8})\\*{3}(.*)/ || /ASSERTION (FAILED)/i )' $$^ /dev/null | perl -ne 'if(/(.*)/){print "$$$$1\\n\\n"; exit(1) if eof()}'
"""} } ).mkString("\n") + s"""
@ -85,6 +90,8 @@ run-$kind-tests: $$(addprefix $$(output_dir)/, $$(addsuffix .out, $targets))
\t@echo; perl -ne 'print " [$$$$1] $$$$ARGV \\t$$$$2\\n" if( /\\*{3}(.{8})\\*{3}(.*)/ || /ASSERTION (FAILED)/i )' $$^ /dev/null | perl -ne 'if(/(.*)/){print "$$$$1\\n\\n"; exit(1) if eof()}'
run-$kind-tests-debug: $$(addprefix $$(output_dir)/, $$(addsuffix .vpd, $targets))
\t@echo; perl -ne 'print " [$$$$1] $$$$ARGV \\t$$$$2\\n" if( /\\*{3}(.{8})\\*{3}(.*)/ || /ASSERTION (FAILED)/i )' $$(patsubst %.vpd,%.out,$$^) /dev/null | perl -ne 'if(/(.*)/){print "$$$$1\\n\\n"; exit(1) if eof()}'
run-$kind-tests-fst: $$(addprefix $$(output_dir)/, $$(addsuffix .fst, $targets))
\t@echo; perl -ne 'print " [$$$$1] $$$$ARGV \\t$$$$2\\n" if( /\\*{3}(.{8})\\*{3}(.*)/ || /ASSERTION (FAILED)/i )' $$(patsubst %.fst,%.out,$$^) /dev/null | perl -ne 'if(/(.*)/){print "$$$$1\\n\\n"; exit(1) if eof()}'
run-$kind-tests-fast: $$(addprefix $$(output_dir)/, $$(addsuffix .run, $targets))
\t@echo; perl -ne 'print " [$$$$1] $$$$ARGV \\t$$$$2\\n" if( /\\*{3}(.{8})\\*{3}(.*)/ || /ASSERTION (FAILED)/i )' $$^ /dev/null | perl -ne 'if(/(.*)/){print "$$$$1\\n\\n"; exit(1) if eof()}'
"""

View File

@ -7,6 +7,7 @@ import chisel3.internal.sourceinfo.SourceInfo
import config._
import diplomacy._
import scala.collection.mutable.ListBuffer
import util.RationalDirection
case object TLMonitorBuilder extends Field[TLMonitorArgs => Option[TLMonitorBase]]
case object TLFuzzReadyValid extends Field[Boolean]
@ -182,20 +183,20 @@ case class TLAsyncSinkNode(depth: Int, sync: Int)
dFn = { p => p.base.copy(minLatency = sync+1) },
uFn = { p => TLAsyncManagerPortParameters(depth, p) })
object TLRationalImp extends NodeImp[TLClientPortParameters, TLManagerPortParameters, TLEdgeParameters, TLEdgeParameters, TLRationalBundle]
object TLRationalImp extends NodeImp[TLRationalClientPortParameters, TLRationalManagerPortParameters, TLRationalEdgeParameters, TLRationalEdgeParameters, TLRationalBundle]
{
def edgeO(pd: TLClientPortParameters, pu: TLManagerPortParameters): TLEdgeParameters = TLEdgeParameters(pd, pu)
def edgeI(pd: TLClientPortParameters, pu: TLManagerPortParameters): TLEdgeParameters = TLEdgeParameters(pd, pu)
def edgeO(pd: TLRationalClientPortParameters, pu: TLRationalManagerPortParameters): TLRationalEdgeParameters = TLRationalEdgeParameters(pd, pu)
def edgeI(pd: TLRationalClientPortParameters, pu: TLRationalManagerPortParameters): TLRationalEdgeParameters = TLRationalEdgeParameters(pd, pu)
def bundleO(eo: Seq[TLEdgeParameters]): Vec[TLRationalBundle] = Vec(eo.size, new TLRationalBundle(TLBundleParameters.union(eo.map(_.bundle))))
def bundleI(ei: Seq[TLEdgeParameters]): Vec[TLRationalBundle] = Vec(ei.size, new TLRationalBundle(TLBundleParameters.union(ei.map(_.bundle))))
def bundleO(eo: Seq[TLRationalEdgeParameters]): Vec[TLRationalBundle] = Vec(eo.size, new TLRationalBundle(TLBundleParameters.union(eo.map(_.bundle))))
def bundleI(ei: Seq[TLRationalEdgeParameters]): Vec[TLRationalBundle] = Vec(ei.size, new TLRationalBundle(TLBundleParameters.union(ei.map(_.bundle))))
def colour = "#00ff00" // green
override def mixO(pd: TLClientPortParameters, node: OutwardNode[TLClientPortParameters, TLManagerPortParameters, TLRationalBundle]): TLClientPortParameters =
pd.copy(clients = pd.clients.map { c => c.copy (nodePath = node +: c.nodePath) })
override def mixI(pu: TLManagerPortParameters, node: InwardNode[TLClientPortParameters, TLManagerPortParameters, TLRationalBundle]): TLManagerPortParameters =
pu.copy(managers = pu.managers.map { m => m.copy (nodePath = node +: m.nodePath) })
override def mixO(pd: TLRationalClientPortParameters, node: OutwardNode[TLRationalClientPortParameters, TLRationalManagerPortParameters, TLRationalBundle]): TLRationalClientPortParameters =
pd.copy(base = pd.base.copy(clients = pd.base.clients.map { c => c.copy (nodePath = node +: c.nodePath) }))
override def mixI(pu: TLRationalManagerPortParameters, node: InwardNode[TLRationalClientPortParameters, TLRationalManagerPortParameters, TLRationalBundle]): TLRationalManagerPortParameters =
pu.copy(base = pu.base.copy(managers = pu.base.managers.map { m => m.copy (nodePath = node +: m.nodePath) }))
}
case class TLRationalIdentityNode() extends IdentityNode(TLRationalImp)
@ -204,10 +205,10 @@ case class TLRationalInputNode() extends InputNode(TLRationalImp)
case class TLRationalSourceNode()
extends MixedAdapterNode(TLImp, TLRationalImp)(
dFn = { p => p },
uFn = { p => p.copy(minLatency = 1) }) // discard cycles from other clock domain
dFn = { p => TLRationalClientPortParameters(p) },
uFn = { p => p.base.copy(minLatency = 1) }) // discard cycles from other clock domain
case class TLRationalSinkNode()
case class TLRationalSinkNode(direction: RationalDirection)
extends MixedAdapterNode(TLRationalImp, TLImp)(
dFn = { p => p.copy(minLatency = 1) },
uFn = { p => p })
dFn = { p => p.base.copy(minLatency = 1) },
uFn = { p => TLRationalManagerPortParameters(direction, p) })

View File

@ -5,6 +5,7 @@ package uncore.tilelink2
import Chisel._
import diplomacy._
import scala.math.max
import util.RationalDirection
case class TLManagerParameters(
address: Seq[AddressSet],
@ -326,6 +327,14 @@ case class TLAsyncEdgeParameters(client: TLAsyncClientPortParameters, manager: T
val bundle = TLAsyncBundleParameters(manager.depth, TLBundleParameters(client.base, manager.base))
}
case class TLRationalManagerPortParameters(direction: RationalDirection, base: TLManagerPortParameters)
case class TLRationalClientPortParameters(base: TLClientPortParameters)
case class TLRationalEdgeParameters(client: TLRationalClientPortParameters, manager: TLRationalManagerPortParameters)
{
val bundle = TLBundleParameters(client.base, manager.base)
}
object ManagerUnification
{
def apply(managers: Seq[TLManagerParameters]) = {

View File

@ -27,14 +27,15 @@ class TLRationalCrossingSource(implicit p: Parameters) extends LazyModule
((io.in zip io.out) zip (node.edgesIn zip node.edgesOut)) foreach { case ((in, out), (edgeIn, edgeOut)) =>
val bce = edgeIn.manager.anySupportAcquireB && edgeIn.client.anySupportProbe
val direction = edgeOut.manager.direction
out.a <> ToRational(in.a)
in.d <> FromRational(out.d)
out.a <> ToRational(in.a, direction)
in.d <> FromRational(out.d, direction.flip)
if (bce) {
in.b <> FromRational(out.b)
out.c <> ToRational(in.c)
out.e <> ToRational(in.e)
in.b <> FromRational(out.b, direction.flip)
out.c <> ToRational(in.c, direction)
out.e <> ToRational(in.e, direction)
} else {
in.b.valid := Bool(false)
in.c.ready := Bool(true)
@ -50,9 +51,9 @@ class TLRationalCrossingSource(implicit p: Parameters) extends LazyModule
}
}
class TLRationalCrossingSink(implicit p: Parameters) extends LazyModule
class TLRationalCrossingSink(direction: RationalDirection = Symmetric)(implicit p: Parameters) extends LazyModule
{
val node = TLRationalSinkNode()
val node = TLRationalSinkNode(direction)
lazy val module = new LazyModuleImp(this) {
val io = new Bundle {
@ -62,14 +63,15 @@ class TLRationalCrossingSink(implicit p: Parameters) extends LazyModule
((io.in zip io.out) zip (node.edgesIn zip node.edgesOut)) foreach { case ((in, out), (edgeIn, edgeOut)) =>
val bce = edgeOut.manager.anySupportAcquireB && edgeOut.client.anySupportProbe
val direction = edgeIn.manager.direction
out.a <> FromRational(in.a)
in.d <> ToRational(out.d)
out.a <> FromRational(in.a, direction)
in.d <> ToRational(out.d, direction.flip)
if (bce) {
in.b <> ToRational(out.b)
out.c <> FromRational(in.c)
out.e <> FromRational(in.e)
in.b <> ToRational(out.b, direction.flip)
out.c <> FromRational(in.c, direction)
out.e <> FromRational(in.e, direction)
} else {
out.b.ready := Bool(true)
out.c.valid := Bool(false)
@ -98,21 +100,21 @@ object TLRationalCrossingSource
object TLRationalCrossingSink
{
// applied to the TL source node; y.node := TLRationalCrossingSink()(x.node)
def apply()(x: TLRationalOutwardNode)(implicit p: Parameters, sourceInfo: SourceInfo): TLOutwardNode = {
val sink = LazyModule(new TLRationalCrossingSink)
def apply(direction: RationalDirection = Symmetric)(x: TLRationalOutwardNode)(implicit p: Parameters, sourceInfo: SourceInfo): TLOutwardNode = {
val sink = LazyModule(new TLRationalCrossingSink(direction))
sink.node := x
sink.node
}
}
class TLRationalCrossing(implicit p: Parameters) extends LazyModule
class TLRationalCrossing(direction: RationalDirection = Symmetric)(implicit p: Parameters) extends LazyModule
{
val nodeIn = TLInputNode()
val nodeOut = TLOutputNode()
val node = NodeHandle(nodeIn, nodeOut)
val source = LazyModule(new TLRationalCrossingSource)
val sink = LazyModule(new TLRationalCrossingSink)
val sink = LazyModule(new TLRationalCrossingSink(direction))
val _ = (sink.node := source.node) // no monitor
val in = (source.node := nodeIn)
@ -147,35 +149,73 @@ class TLRationalCrossing(implicit p: Parameters) extends LazyModule
/** Synthesizeable unit tests */
import unittest._
class TLRAMRationalCrossing(implicit p: Parameters) extends LazyModule {
class TLRAMRationalCrossingSource(implicit p: Parameters) extends LazyModule {
val node = TLRationalOutputNode()
val fuzz = LazyModule(new TLFuzzer(5000))
val model = LazyModule(new TLRAMModel)
val cross = LazyModule(new TLRationalCrossing)
val delay = LazyModule(new TLDelayer(0.25))
val ram = LazyModule(new TLRAM(AddressSet(0x0, 0x3ff)))
model.node := fuzz.node
cross.node := TLDelayer(0.25)(TLFragmenter(4, 256)(model.node))
val monitor1 = (delay.node := cross.node)
val monitor2 = (ram.node := delay.node)
val monitors = monitor1.toList ++ monitor2.toList
node := TLRationalCrossingSource()(TLDelayer(0.25)(model.node))
lazy val module = new LazyModuleImp(this) {
val io = new Bundle {
val finished = Bool(OUTPUT)
val out = node.bundleOut
}
io.finished := fuzz.module.io.finished
}
}
class TLRAMRationalCrossingSink(direction: RationalDirection)(implicit p: Parameters) extends LazyModule {
val node = TLRationalInputNode()
val ram = LazyModule(new TLRAM(AddressSet(0x0, 0x3ff)))
ram.node := TLFragmenter(4, 256)(TLDelayer(0.25)(TLRationalCrossingSink(direction)(node)))
lazy val module = new LazyModuleImp(this) {
val io = new Bundle {
val in = node.bundleIn
}
}
}
class TLRAMRationalCrossing(implicit p: Parameters) extends LazyModule {
val sym_fast_source = LazyModule(new TLRAMRationalCrossingSource)
val sym_slow_sink = LazyModule(new TLRAMRationalCrossingSink(Symmetric))
sym_slow_sink.node := sym_fast_source.node
val sym_slow_source = LazyModule(new TLRAMRationalCrossingSource)
val sym_fast_sink = LazyModule(new TLRAMRationalCrossingSink(Symmetric))
sym_fast_sink.node := sym_slow_source.node
val fix_fast_source = LazyModule(new TLRAMRationalCrossingSource)
val fix_slow_sink = LazyModule(new TLRAMRationalCrossingSink(FastToSlow))
fix_slow_sink.node := fix_fast_source.node
val fix_slow_source = LazyModule(new TLRAMRationalCrossingSource)
val fix_fast_sink = LazyModule(new TLRAMRationalCrossingSink(SlowToFast))
fix_fast_sink.node := fix_slow_source.node
lazy val module = new LazyModuleImp(this) with HasUnitTestIO {
io.finished := fuzz.module.io.finished
io.finished :=
sym_fast_source.module.io.finished &&
sym_slow_source.module.io.finished &&
fix_fast_source.module.io.finished &&
fix_slow_source.module.io.finished
// Shove the RAM into another clock domain
val clocks = Module(new util.Pow2ClockDivider(2))
ram.module.clock := clocks.io.clock_out
delay.module.clock := clocks.io.clock_out
// Generate faster clock (still divided so verilator approves)
val fast = Module(new util.Pow2ClockDivider(1))
sym_fast_source.module.clock := fast.io.clock_out
sym_fast_sink .module.clock := fast.io.clock_out
fix_fast_source.module.clock := fast.io.clock_out
fix_fast_sink .module.clock := fast.io.clock_out
// ... and safely cross TL2 into it
cross.module.io.in_clock := clock
cross.module.io.in_reset := reset
cross.module.io.out_clock := clocks.io.clock_out
cross.module.io.out_reset := reset
// Push the Monitors into the right clock domain
monitors.foreach { m => m.module.clock := clocks.io.clock_out }
// Generate slower clock
val slow = Module(new util.Pow2ClockDivider(2))
sym_slow_source.module.clock := slow.io.clock_out
sym_slow_sink .module.clock := slow.io.clock_out
fix_slow_source.module.clock := slow.io.clock_out
fix_slow_sink .module.clock := slow.io.clock_out
}
}

View File

@ -10,7 +10,7 @@ package object tilelink2
type TLInwardNode = InwardNodeHandle[TLClientPortParameters, TLManagerPortParameters, TLBundle]
type TLOutwardNode = OutwardNodeHandle[TLClientPortParameters, TLManagerPortParameters, TLBundle]
type TLAsyncOutwardNode = OutwardNodeHandle[TLAsyncClientPortParameters, TLAsyncManagerPortParameters, TLAsyncBundle]
type TLRationalOutwardNode = OutwardNodeHandle[TLClientPortParameters, TLManagerPortParameters, TLRationalBundle]
type TLRationalOutwardNode = OutwardNodeHandle[TLRationalClientPortParameters, TLRationalManagerPortParameters, TLRationalBundle]
type IntOutwardNode = OutwardNodeHandle[IntSourcePortParameters, IntSinkPortParameters, Vec[Bool]]
def OH1ToOH(x: UInt) = (x << 1 | UInt(1)) & ~Cat(UInt(0, width=1), x)

View File

@ -4,16 +4,20 @@ package util
import Chisel._
/** Divide the clock by 2 */
class ClockDivider2 extends Module {
/** This black-boxes a Clock Divider by 2.
* The output clock is phase-aligned to the input clock.
* If you use this in synthesis, make sure your sdc
* declares that you want it to do the same.
*
* Because Chisel does not support
* blocking assignments, it is impossible
* to create a deterministic divided clock.
*/
class ClockDivider2 extends BlackBox {
val io = new Bundle {
val clock_out = Clock(OUTPUT)
val clk_out = Clock(OUTPUT)
val clk_in = Clock(INPUT)
}
val clock_reg = Reg(Bool())
clock_reg := !clock_reg
io.clock_out := clock_reg.asClock
}
/** Divide the clock by power of 2 times.
@ -30,9 +34,10 @@ class Pow2ClockDivider(pow2: Int) extends Module {
val dividers = Seq.fill(pow2) { Module(new ClockDivider2) }
dividers.init.zip(dividers.tail).map { case (last, next) =>
next.clock := last.io.clock_out
next.io.clk_in := last.io.clk_out
}
io.clock_out := dividers.last.io.clock_out
dividers.head.io.clk_in := clock
io.clock_out := dividers.last.io.clk_out
}
}

View File

@ -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
}

View File

@ -6,6 +6,7 @@
bb_vsrcs = $(base_dir)/vsrc/DebugTransportModuleJtag.v \
$(base_dir)/vsrc/jtag_vpi.v \
$(base_dir)/vsrc/ClockDivider2.v \
$(base_dir)/vsrc/AsyncResetReg.v \
sim_vsrcs = \

24
vsrc/ClockDivider2.v Normal file
View File

@ -0,0 +1,24 @@
// See LICENSE.SiFive for license details.
/** This black-boxes a Clock Divider by 2.
* The output clock is phase-aligned to the input clock.
* If you use this in synthesis, make sure your sdc
* declares that you want it to do the same.
*
* Because Chisel does not support
* blocking assignments, it is impossible
* to create a deterministic divided clock.
*
* @param clk_out Divided Clock
* @param clk_in Clock Input
*
*/
module ClockDivider2 (output reg clk_out, input clk_in);
initial clk_out = 1'b0;
always @(posedge clk_in) begin
clk_out = ~clk_out; // Must use =, NOT <=
end
endmodule // ClockDivider2