diff --git a/src/main/scala/amba/ahb/RegisterRouter.scala b/src/main/scala/amba/ahb/RegisterRouter.scala index da1960d7..59a81611 100644 --- a/src/main/scala/amba/ahb/RegisterRouter.scala +++ b/src/main/scala/amba/ahb/RegisterRouter.scala @@ -6,7 +6,7 @@ import Chisel._ import freechips.rocketchip.config.Parameters import freechips.rocketchip.diplomacy._ import freechips.rocketchip.regmapper._ -import freechips.rocketchip.tilelink.{IntSourceNode, IntSourcePortSimple} +import freechips.rocketchip.interrupts.{IntSourceNode, IntSourcePortSimple} import freechips.rocketchip.util.{HeterogeneousBag, MaskGen} import scala.math.{min,max} diff --git a/src/main/scala/amba/apb/RegisterRouter.scala b/src/main/scala/amba/apb/RegisterRouter.scala index f83c38c9..2ebb57d8 100644 --- a/src/main/scala/amba/apb/RegisterRouter.scala +++ b/src/main/scala/amba/apb/RegisterRouter.scala @@ -6,7 +6,7 @@ import Chisel._ import freechips.rocketchip.config.Parameters import freechips.rocketchip.diplomacy._ import freechips.rocketchip.regmapper._ -import freechips.rocketchip.tilelink.{IntSourceNode, IntSourcePortSimple} +import freechips.rocketchip.interrupts.{IntSourceNode, IntSourcePortSimple} import freechips.rocketchip.util.HeterogeneousBag import scala.math.{min,max} diff --git a/src/main/scala/amba/axi4/AsyncCrossing.scala b/src/main/scala/amba/axi4/AsyncCrossing.scala index a394fa6b..f582f665 100644 --- a/src/main/scala/amba/axi4/AsyncCrossing.scala +++ b/src/main/scala/amba/axi4/AsyncCrossing.scala @@ -8,6 +8,7 @@ import freechips.rocketchip.config.Parameters import freechips.rocketchip.diplomacy._ import freechips.rocketchip.tilelink._ import freechips.rocketchip.util._ +import freechips.rocketchip.coreplex.{CrossingWrapper, AsynchronousCrossing} class AXI4AsyncCrossingSource(sync: Int = 3)(implicit p: Parameters) extends LazyModule { @@ -61,6 +62,7 @@ object AXI4AsyncCrossingSink } } +@deprecated("AXI4AsyncCrossing is fragile. Use AXI4AsyncCrossingSource and AXI4AsyncCrossingSink", "rocket-chip 1.2") class AXI4AsyncCrossing(depth: Int = 8, sync: Int = 3)(implicit p: Parameters) extends LazyModule { val source = LazyModule(new AXI4AsyncCrossingSource(sync)) @@ -89,28 +91,21 @@ import freechips.rocketchip.unittest._ class AXI4RAMAsyncCrossing(txns: Int)(implicit p: Parameters) extends LazyModule { val model = LazyModule(new TLRAMModel("AsyncCrossing")) - val ram = LazyModule(new AXI4RAM(AddressSet(0x0, 0x3ff))) val fuzz = LazyModule(new TLFuzzer(txns)) val toaxi = LazyModule(new TLToAXI4) - val cross = LazyModule(new AXI4AsyncCrossing) + val island = LazyModule(new CrossingWrapper(AsynchronousCrossing(8))) + val ram = island { LazyModule(new AXI4RAM(AddressSet(0x0, 0x3ff))) } model.node := fuzz.node toaxi.node := model.node - cross.node := toaxi.node - ram.node := cross.node + ram.node := island.crossAXI4In := toaxi.node lazy val module = new LazyModuleImp(this) with UnitTestModule { io.finished := fuzz.module.io.finished // Shove the RAM into another clock domain val clocks = Module(new Pow2ClockDivider(2)) - ram.module.clock := clocks.io.clock_out - - // ... and safely cross AXI42 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 + island.module.clock := clocks.io.clock_out } } diff --git a/src/main/scala/amba/axi4/RegisterRouter.scala b/src/main/scala/amba/axi4/RegisterRouter.scala index 32d0b1de..0da5da31 100644 --- a/src/main/scala/amba/axi4/RegisterRouter.scala +++ b/src/main/scala/amba/axi4/RegisterRouter.scala @@ -6,7 +6,7 @@ import Chisel._ import freechips.rocketchip.config.Parameters import freechips.rocketchip.diplomacy._ import freechips.rocketchip.regmapper._ -import freechips.rocketchip.tilelink.{IntSourceNode, IntSourcePortSimple} +import freechips.rocketchip.interrupts.{IntSourceNode, IntSourcePortSimple} import freechips.rocketchip.util.{HeterogeneousBag, MaskGen} import scala.math.{min,max} diff --git a/src/main/scala/amba/axi4/package.scala b/src/main/scala/amba/axi4/package.scala index af72e46f..cc5567bd 100644 --- a/src/main/scala/amba/axi4/package.scala +++ b/src/main/scala/amba/axi4/package.scala @@ -3,10 +3,11 @@ package freechips.rocketchip.amba import Chisel._ -import freechips.rocketchip.diplomacy.OutwardNodeHandle +import freechips.rocketchip.diplomacy._ package object axi4 { type AXI4OutwardNode = OutwardNodeHandle[AXI4MasterPortParameters, AXI4SlavePortParameters, AXI4Bundle] type AXI4AsyncOutwardNode = OutwardNodeHandle[AXI4AsyncMasterPortParameters, AXI4AsyncSlavePortParameters, AXI4AsyncBundle] + type AXI4Node = NodeHandle[AXI4MasterPortParameters, AXI4SlavePortParameters, AXI4Bundle, AXI4MasterPortParameters, AXI4SlavePortParameters, AXI4Bundle] } diff --git a/src/main/scala/coreplex/BaseCoreplex.scala b/src/main/scala/coreplex/BaseCoreplex.scala index bb004313..b00b0681 100644 --- a/src/main/scala/coreplex/BaseCoreplex.scala +++ b/src/main/scala/coreplex/BaseCoreplex.scala @@ -5,18 +5,13 @@ package freechips.rocketchip.coreplex import Chisel._ import freechips.rocketchip.config.Parameters import freechips.rocketchip.diplomacy._ +import freechips.rocketchip.interrupts._ import freechips.rocketchip.tilelink._ import freechips.rocketchip.devices.tilelink._ import freechips.rocketchip.tile.{BaseTile, TileParams, SharedMemoryTLEdge, HasExternallyDrivenTileConstants} import freechips.rocketchip.devices.debug.{HasPeripheryDebug, HasPeripheryDebugModuleImp} import freechips.rocketchip.util._ -/** Enumerates the three types of clock crossing between tiles and system bus */ -sealed trait CoreplexClockCrossing -case class SynchronousCrossing(params: BufferParams = BufferParams.default) extends CoreplexClockCrossing -case class RationalCrossing(direction: RationalDirection = FastToSlow) extends CoreplexClockCrossing -case class AsynchronousCrossing(depth: Int, sync: Int = 3) extends CoreplexClockCrossing - /** BareCoreplex is the root class for creating a coreplex sub-system */ abstract class BareCoreplex(implicit p: Parameters) extends LazyModule with BindingScope { lazy val dts = DTS(bindingTree) diff --git a/src/main/scala/coreplex/CrossingWrapper.scala b/src/main/scala/coreplex/CrossingWrapper.scala new file mode 100644 index 00000000..1b6f1a4d --- /dev/null +++ b/src/main/scala/coreplex/CrossingWrapper.scala @@ -0,0 +1,192 @@ +// See LICENSE.SiFive for license details. + +package freechips.rocketchip.coreplex + +import Chisel._ +import freechips.rocketchip.config._ +import freechips.rocketchip.diplomacy._ +import freechips.rocketchip.tilelink._ +import freechips.rocketchip.amba.axi4._ +import freechips.rocketchip.interrupts._ +import freechips.rocketchip.util._ + +/** Enumerates the three types of clock crossing between tiles and system bus */ +sealed trait CoreplexClockCrossing +case class SynchronousCrossing(params: BufferParams = BufferParams.default) extends CoreplexClockCrossing +case class RationalCrossing(direction: RationalDirection = FastToSlow) extends CoreplexClockCrossing +case class AsynchronousCrossing(depth: Int, sync: Int = 3) extends CoreplexClockCrossing + +private case class CrossingCheck(out: Boolean, source: BaseNode, sink: BaseNode) + +trait HasCrossingMethods extends LazyModule with LazyScope +{ + // Detect incorrect crossing connectivity + + private var checks: List[CrossingCheck] = Nil + private def inside(node: BaseNode) = node.parents.exists(_ eq this) + override def instantiate() { + super.instantiate() + checks.foreach { case CrossingCheck(out, source, sink) => + source.inputs.foreach { case (syncSource, _) => + require (inside(syncSource) == out, s"${syncSource.name} must ${if(out)""else"not "}be inside ${name} (wrong .cross direction?)") + } + sink.outputs.foreach { case (syncSink, _) => + require (inside(syncSink) != out, s"${syncSink.name} must ${if(out)"not "else""}be inside ${name} (wrong .cross direction?)") + } + } + } + + // TileLink + + def crossTLSyncInOut(out: Boolean)(params: BufferParams = BufferParams.default)(implicit p: Parameters): TLNode = { + val node = this { LazyModule(new TLBuffer(params)).node } + checks = CrossingCheck(out, node, node) :: checks + node + } + + def crossTLAsyncInOut(out: Boolean)(depth: Int = 8, sync: Int = 3)(implicit p: Parameters): TLNode = { + def sourceGen = LazyModule(new TLAsyncCrossingSource(sync)) + def sinkGen = LazyModule(new TLAsyncCrossingSink(depth, sync)) + val source = if (out) this { sourceGen } else sourceGen + val sink = if (out) sinkGen else this { sinkGen } + sink.node :=? source.node + checks = CrossingCheck(out, source.node, sink.node) :: checks + NodeHandle(source.node, sink.node) + } + + def crossTLRationalInOut(out: Boolean)(direction: RationalDirection)(implicit p: Parameters): TLNode = { + def sourceGen = LazyModule(new TLRationalCrossingSource) + def sinkGen = LazyModule(new TLRationalCrossingSink(if (out) direction else direction.flip)) + val source = if (out) this { sourceGen } else sourceGen + val sink = if (out) sinkGen else this { sinkGen } + sink.node :=? source.node + checks = CrossingCheck(out, source.node, sink.node) :: checks + NodeHandle(source.node, sink.node) + } + + def crossTLSyncIn (params: BufferParams = BufferParams.default)(implicit p: Parameters): TLNode = crossTLSyncInOut(false)(params) + def crossTLSyncOut(params: BufferParams = BufferParams.default)(implicit p: Parameters): TLNode = crossTLSyncInOut(true )(params) + def crossTLAsyncIn (depth: Int = 8, sync: Int = 3)(implicit p: Parameters): TLNode = crossTLAsyncInOut(false)(depth, sync) + def crossTLAsyncOut(depth: Int = 8, sync: Int = 3)(implicit p: Parameters): TLNode = crossTLAsyncInOut(true )(depth, sync) + def crossTLRationalIn (direction: RationalDirection)(implicit p: Parameters): TLNode = crossTLRationalInOut(false)(direction) + def crossTLRationalOut(direction: RationalDirection)(implicit p: Parameters): TLNode = crossTLRationalInOut(true )(direction) + + def crossTLIn(arg: CoreplexClockCrossing)(implicit p: Parameters): TLNode = arg match { + case x: SynchronousCrossing => crossTLSyncIn(x.params) + case x: AsynchronousCrossing => crossTLAsyncIn(x.depth, x.sync) + case x: RationalCrossing => crossTLRationalIn(x.direction) + } + + def crossTLOut(arg: CoreplexClockCrossing)(implicit p: Parameters): TLNode = arg match { + case x: SynchronousCrossing => crossTLSyncOut(x.params) + case x: AsynchronousCrossing => crossTLAsyncOut(x.depth, x.sync) + case x: RationalCrossing => crossTLRationalOut(x.direction) + } + + // AXI4 + + def crossAXI4SyncInOut(out: Boolean)(params: BufferParams = BufferParams.default)(implicit p: Parameters): AXI4Node = { + val node = this { LazyModule(new AXI4Buffer(params)).node } + checks = CrossingCheck(out, node, node) :: checks + node + } + + def crossAXI4AsyncInOut(out: Boolean)(depth: Int = 8, sync: Int = 3)(implicit p: Parameters): AXI4Node = { + def sourceGen = LazyModule(new AXI4AsyncCrossingSource(sync)) + def sinkGen = LazyModule(new AXI4AsyncCrossingSink(depth, sync)) + val source = if (out) this { sourceGen } else sourceGen + val sink = if (out) sinkGen else this { sinkGen } + sink.node :=? source.node + checks = CrossingCheck(out, source.node, sink.node) :: checks + NodeHandle(source.node, sink.node) + } + + def crossAXI4SyncIn (params: BufferParams = BufferParams.default)(implicit p: Parameters): AXI4Node = crossAXI4SyncInOut(false)(params) + def crossAXI4SyncOut(params: BufferParams = BufferParams.default)(implicit p: Parameters): AXI4Node = crossAXI4SyncInOut(true )(params) + def crossAXI4AsyncIn (depth: Int = 8, sync: Int = 3)(implicit p: Parameters): AXI4Node = crossAXI4AsyncInOut(false)(depth, sync) + def crossAXI4AsyncOut(depth: Int = 8, sync: Int = 3)(implicit p: Parameters): AXI4Node = crossAXI4AsyncInOut(true )(depth, sync) + + def crossAXI4In(arg: CoreplexClockCrossing)(implicit p: Parameters): AXI4Node = arg match { + case x: SynchronousCrossing => crossAXI4SyncIn(x.params) + case x: AsynchronousCrossing => crossAXI4AsyncIn(x.depth, x.sync) + case x: RationalCrossing => throw new IllegalArgumentException("AXI4 Rational crossing unimplemented") + } + + def crossAXI4Out(arg: CoreplexClockCrossing)(implicit p: Parameters): AXI4Node = arg match { + case x: SynchronousCrossing => crossAXI4SyncOut(x.params) + case x: AsynchronousCrossing => crossAXI4AsyncOut(x.depth, x.sync) + case x: RationalCrossing => throw new IllegalArgumentException("AXI4 Rational crossing unimplemented") + } + + // Interrupts + + def crossIntSyncInOut(out: Boolean)(alreadyRegistered: Boolean = false)(implicit p: Parameters): IntNode = { + def sourceGen = LazyModule(new IntSyncCrossingSource(alreadyRegistered)) + def sinkGen = LazyModule(new IntSyncCrossingSink(0)) + val source = if (out) this { sourceGen } else sourceGen + val sink = if (out) sinkGen else this { sinkGen } + sink.node :=? source.node + checks = CrossingCheck(out, source.node, sink.node) :: checks + NodeHandle(source.node, sink.node) + } + + def crossIntAsyncInOut(out: Boolean)(sync: Int = 3, alreadyRegistered: Boolean = false)(implicit p: Parameters): IntNode = { + def sourceGen = LazyModule(new IntSyncCrossingSource(alreadyRegistered)) + def sinkGen = LazyModule(new IntSyncCrossingSink(sync)) + val source = if (out) this { sourceGen } else sourceGen + val sink = if (out) sinkGen else this { sinkGen } + sink.node :=? source.node + checks = CrossingCheck(out, source.node, sink.node) :: checks + NodeHandle(source.node, sink.node) + } + + def crossIntRationalInOut(out: Boolean)(alreadyRegistered: Boolean = false)(implicit p: Parameters): IntNode = { + def sourceGen = LazyModule(new IntSyncCrossingSource(alreadyRegistered)) + def sinkGen = LazyModule(new IntSyncCrossingSink(1)) + val source = if (out) this { sourceGen } else sourceGen + val sink = if (out) sinkGen else this { sinkGen } + sink.node :=? source.node + checks = CrossingCheck(out, source.node, sink.node) :: checks + NodeHandle(source.node, sink.node) + } + + def crossIntSyncIn (alreadyRegistered: Boolean = false)(implicit p: Parameters): IntNode = crossIntSyncInOut(false)(alreadyRegistered) + def crossIntSyncOut(alreadyRegistered: Boolean = false)(implicit p: Parameters): IntNode = crossIntSyncInOut(true )(alreadyRegistered) + def crossIntAsyncIn (sync: Int = 3, alreadyRegistered: Boolean = false)(implicit p: Parameters): IntNode = crossIntAsyncInOut(false)(sync, alreadyRegistered) + def crossIntAsyncOut(sync: Int = 3, alreadyRegistered: Boolean = false)(implicit p: Parameters): IntNode = crossIntAsyncInOut(true )(sync, alreadyRegistered) + def crossIntRationalIn (alreadyRegistered: Boolean = false)(implicit p: Parameters): IntNode = crossIntRationalInOut(false)(alreadyRegistered) + def crossIntRationalOut(alreadyRegistered: Boolean = false)(implicit p: Parameters): IntNode = crossIntRationalInOut(true )(alreadyRegistered) + + def crossIntIn(arg: CoreplexClockCrossing, alreadyRegistered: Boolean)(implicit p: Parameters): IntNode = arg match { + case x: SynchronousCrossing => crossIntSyncIn(alreadyRegistered) + case x: AsynchronousCrossing => crossIntAsyncIn(x.sync, alreadyRegistered) + case x: RationalCrossing => crossIntRationalIn(alreadyRegistered) + } + + def crossIntOut(arg: CoreplexClockCrossing, alreadyRegistered: Boolean)(implicit p: Parameters): IntNode = arg match { + case x: SynchronousCrossing => crossIntSyncOut(alreadyRegistered) + case x: AsynchronousCrossing => crossIntAsyncOut(x.sync, alreadyRegistered) + case x: RationalCrossing => crossIntRationalOut(alreadyRegistered) + } + + def crossIntIn (arg: CoreplexClockCrossing)(implicit p: Parameters): IntNode = crossIntIn (arg, false) + def crossIntOut(arg: CoreplexClockCrossing)(implicit p: Parameters): IntNode = crossIntOut(arg, false) +} + +trait HasCrossing extends HasCrossingMethods +{ + this: LazyModule => + val crossing: CoreplexClockCrossing + + def crossTLIn (implicit p: Parameters): TLNode = crossTLIn (crossing) + def crossTLOut (implicit p: Parameters): TLNode = crossTLOut (crossing) + def crossAXI4In (implicit p: Parameters): AXI4Node= crossAXI4In (crossing) + def crossAXI4Out(implicit p: Parameters): AXI4Node= crossAXI4Out(crossing) + def crossIntIn (implicit p: Parameters): IntNode = crossIntIn (crossing) + def crossIntOut (implicit p: Parameters): IntNode = crossIntOut (crossing) + + def crossIntIn (alreadyRegistered: Boolean)(implicit p: Parameters): IntNode = crossIntIn (crossing, alreadyRegistered) + def crossIntOut(alreadyRegistered: Boolean)(implicit p: Parameters): IntNode = crossIntOut(crossing, alreadyRegistered) +} + +class CrossingWrapper(val crossing: CoreplexClockCrossing)(implicit p: Parameters) extends SimpleLazyModule with HasCrossing diff --git a/src/main/scala/coreplex/FrontBus.scala b/src/main/scala/coreplex/FrontBus.scala index 2ece4503..ce3e95f9 100644 --- a/src/main/scala/coreplex/FrontBus.scala +++ b/src/main/scala/coreplex/FrontBus.scala @@ -28,16 +28,12 @@ class FrontBus(params: FrontBusParams)(implicit p: Parameters) extends TLBusWrap master_fixer.node :=* master_buffer.node inwardNode :=* master_fixer.node - def fromSyncPorts(addBuffers: Int = 0, name: Option[String] = None): TLInwardNode = { - val (in, out) = bufferChain(addBuffers, name) - master_buffer.node :=* out - in + def fromSyncPorts(addBuffers: Int = 0, name: Option[String] = None): TLInwardNode = SourceCardinality { implicit p => + TLBuffer.chain(addBuffers).foldLeft(master_buffer.node:TLInwardNode)(_ :=? _) } - def fromSyncMasters(addBuffers: Int = 0, name: Option[String] = None): TLInwardNode = { - val (in, out) = bufferChain(addBuffers, name) - master_buffer.node :=* out - in + def fromSyncMasters(addBuffers: Int = 0, name: Option[String] = None): TLInwardNode = SourceCardinality { implicit p => + TLBuffer.chain(addBuffers).foldLeft(master_buffer.node:TLInwardNode)(_ :=? _) } def fromCoherentChip: TLInwardNode = inwardNode diff --git a/src/main/scala/coreplex/InterruptBus.scala b/src/main/scala/coreplex/InterruptBus.scala index 4c04d992..3e639f79 100644 --- a/src/main/scala/coreplex/InterruptBus.scala +++ b/src/main/scala/coreplex/InterruptBus.scala @@ -5,7 +5,7 @@ package freechips.rocketchip.coreplex import Chisel._ import freechips.rocketchip.config.{Field, Parameters} import freechips.rocketchip.diplomacy._ -import freechips.rocketchip.tilelink._ +import freechips.rocketchip.interrupts._ /** Collects interrupts from internal and external devices and feeds them into the PLIC */ class InterruptBusWrapper(implicit p: Parameters) { diff --git a/src/main/scala/coreplex/PeripheryBus.scala b/src/main/scala/coreplex/PeripheryBus.scala index d7ac4a0e..49f0b25b 100644 --- a/src/main/scala/coreplex/PeripheryBus.scala +++ b/src/main/scala/coreplex/PeripheryBus.scala @@ -31,35 +31,23 @@ class PeripheryBus(params: PeripheryBusParams)(implicit p: Parameters) extends T TLFragmenter(params.beatBytes, maxXferBytes)(outwardBufNode) } - def toSyncSlaves(adapt: () => TLNodeChain, name: Option[String]): TLOutwardNode = SinkCardinality { implicit p => - val adapters = adapt() - adapters.in :=? outwardBufNode - adapters.out - } - - def toAsyncSlaves(sync: Int, adapt: () => TLNodeChain, name: Option[String]): TLAsyncOutwardNode = SinkCardinality { implicit p => - val adapters = adapt() - val source = LazyModule(new TLAsyncCrossingSource(sync)) - name.foreach{ n => source.suggestName(s"${busName}_${n}_TLAsyncCrossingSource")} - adapters.in :=? outwardNode - source.node :=? adapters.out - source.node - } - - def toRationalSlaves(adapt: () => TLNodeChain, name: Option[String]): TLRationalOutwardNode = SinkCardinality { implicit p => - val adapters = adapt() - val source = LazyModule(new TLRationalCrossingSource()) - name.foreach{ n => source.suggestName(s"${busName}_${n}_TLRationalCrossingSource")} - adapters.in :=? outwardNode - source.node :=? adapters.out - source.node - } - val fromSystemBus: TLInwardNode = { val atomics = LazyModule(new TLAtomicAutomata(arithmetic = params.arithmetic)) inwardBufNode := atomics.node atomics.node } + + def toTile(name: Option[String] = None)(gen: Parameters => TLInwardNode) { + this { + LazyScope(s"${busName}ToTile${name.getOrElse("")}") { + SinkCardinality { implicit p => + FlipRendering { implicit p => + gen(p) :*= outwardNode + } + } + } + } + } } /** Provides buses that serve as attachment points, diff --git a/src/main/scala/coreplex/RocketCoreplex.scala b/src/main/scala/coreplex/RocketCoreplex.scala index 442b655d..7cf9b155 100644 --- a/src/main/scala/coreplex/RocketCoreplex.scala +++ b/src/main/scala/coreplex/RocketCoreplex.scala @@ -3,67 +3,54 @@ package freechips.rocketchip.coreplex import Chisel._ +import chisel3.internal.sourceinfo.SourceInfo import freechips.rocketchip.config.{Field, Parameters} import freechips.rocketchip.devices.tilelink._ import freechips.rocketchip.devices.debug.{HasPeripheryDebug, HasPeripheryDebugModuleImp} import freechips.rocketchip.diplomacy._ import freechips.rocketchip.tile._ import freechips.rocketchip.tilelink._ +import freechips.rocketchip.interrupts._ import freechips.rocketchip.util._ -case class TLNodeChain(in: TLInwardNode, out: TLOutwardNode) - // TODO: how specific are these to RocketTiles? case class TileMasterPortParams( addBuffers: Int = 0, blockerCtrlAddr: Option[BigInt] = None, cork: Option[Boolean] = None) { - def adapterChain(coreplex: HasPeripheryBus) - (implicit p: Parameters): () => TLNodeChain = { - - val blockerParams = blockerCtrlAddr.map(BasicBusBlockerParams(_, coreplex.pbus.beatBytes, coreplex.sbus.beatBytes, deadlock = true)) + def adapt(coreplex: HasPeripheryBus) + (masterNode: TLOutwardNode) + (implicit p: Parameters, sourceInfo: SourceInfo): TLOutwardNode = { val tile_master_cork = cork.map(u => (LazyModule(new TLCacheCork(unsafe = u)))) - val tile_master_blocker = blockerParams.map(bp => LazyModule(new BasicBusBlocker(bp))) + val tile_master_blocker = + blockerCtrlAddr + .map(BasicBusBlockerParams(_, coreplex.pbus.beatBytes, coreplex.sbus.beatBytes, deadlock = true)) + .map(bp => LazyModule(new BasicBusBlocker(bp))) val tile_master_fixer = LazyModule(new TLFIFOFixer(TLFIFOFixer.allUncacheable)) - val tile_master_buffer = LazyModule(new TLBufferChain(addBuffers)) tile_master_blocker.foreach { _.controlNode := coreplex.pbus.toVariableWidthSlaves } - - val nodes = List( - Some(tile_master_buffer.node), - Some(tile_master_fixer.node), - tile_master_blocker.map(_.node), - tile_master_cork.map(_.node) - ).flatMap(b=>b) - - nodes.init zip nodes.tail foreach { case(front, back) => front :=* back } - - () => TLNodeChain(in = nodes.last, out = nodes.head) + (Seq(tile_master_fixer.node) ++ TLBuffer.chain(addBuffers) + ++ tile_master_blocker.map(_.node) ++ tile_master_cork.map(_.node)) + .foldRight(masterNode)(_ :=* _) } } case class TileSlavePortParams( addBuffers: Int = 0, blockerCtrlAddr: Option[BigInt] = None) { - def adapterChain(coreplex: HasPeripheryBus) - (implicit p: Parameters): () => TLNodeChain = { - val blockerParams = blockerCtrlAddr.map(BasicBusBlockerParams(_, coreplex.pbus.beatBytes, coreplex.sbus.beatBytes)) - - val tile_slave_blocker = blockerParams.map(bp => LazyModule(new BasicBusBlocker(bp))) - val tile_slave_buffer = LazyModule(new TLBufferChain(addBuffers)) + def adapt(coreplex: HasPeripheryBus) + (slaveNode: TLInwardNode) + (implicit p: Parameters, sourceInfo: SourceInfo): TLInwardNode = { + val tile_slave_blocker = + blockerCtrlAddr + .map(BasicBusBlockerParams(_, coreplex.pbus.beatBytes, coreplex.sbus.beatBytes)) + .map(bp => LazyModule(new BasicBusBlocker(bp))) tile_slave_blocker.foreach { _.controlNode := coreplex.pbus.toVariableWidthSlaves } - - val nodes = List( - Some(tile_slave_buffer.node), - tile_slave_blocker.map(_.node) - ).flatMap(b=>b) - - nodes.init zip nodes.tail foreach { case(front, back) => front :=* back } - - () => TLNodeChain(in = nodes.last, out = nodes.head) + (Seq() ++ tile_slave_blocker.map(_.node) ++ TLBuffer.chain(addBuffers)) + .foldLeft(slaveNode)(_ :*= _) } } @@ -95,67 +82,55 @@ trait HasRocketTiles extends HasTiles case NumRocketTiles => crossingParams case _ => throw new Exception("RocketCrossingKey.size must == 1 or == RocketTilesKey.size") } + private val crossingTuples = localIntNodes.zip(tileParams).zip(crossings) // Make a wrapper for each tile that will wire it to coreplex devices and crossbars, // according to the specified type of clock crossing. - private val crossingTuples = localIntNodes.zip(tileParams).zip(crossings) val tiles: Seq[BaseTile] = crossingTuples.map { case ((lip, tp), crossing) => - val pWithExtra = p.alterPartial { - case TileKey => tp - case BuildRoCC => tp.rocc - case SharedMemoryTLEdge => sharedMemoryTLEdge - case RocketCrossingKey => List(crossing) - } + // For legacy reasons, it is convenient to store some state + // in the global Parameters about the specific tile being built now + val wrapper = LazyModule(new RocketTileWrapper( + params = tp, + crossing = crossing.crossingType + )(p.alterPartial { + case TileKey => tp + case BuildRoCC => tp.rocc + case SharedMemoryTLEdge => sharedMemoryTLEdge + case RocketCrossingKey => List(crossing) + }) + ).suggestName(tp.name) - val wrapper = crossing.crossingType match { - case SynchronousCrossing(params) => { - val wrapper = LazyModule(new SyncRocketTile(tp)(pWithExtra)) - sbus.fromSyncTiles(params, crossing.master.adapterChain(this), tp.name) :=* wrapper.masterNode - FlipRendering { implicit p => wrapper.slaveNode :*= pbus.toSyncSlaves(crossing.slave.adapterChain(this), tp.name) } - wrapper - } - case AsynchronousCrossing(depth, sync) => { - val wrapper = LazyModule(new AsyncRocketTile(tp)(pWithExtra)) - sbus.fromAsyncTiles(depth, sync, crossing.master.adapterChain(this), tp.name) :=* wrapper.masterNode - FlipRendering { implicit p => wrapper.slaveNode :*= pbus.toAsyncSlaves(sync, crossing.slave.adapterChain(this), tp.name) } - wrapper - } - case RationalCrossing(direction) => { - val wrapper = LazyModule(new RationalRocketTile(tp)(pWithExtra)) - sbus.fromRationalTiles(direction, crossing.master.adapterChain(this), tp.name) :=* wrapper.masterNode - FlipRendering { implicit p => wrapper.slaveNode :*= pbus.toRationalSlaves(crossing.slave.adapterChain(this), tp.name) } - wrapper - } - } - tp.name.foreach(wrapper.suggestName) // Try to stabilize this name for downstream tools + // Connect the master ports of the tile to the system bus + sbus.fromTile(tp.name) { implicit p => crossing.master.adapt(this)(wrapper.crossTLOut :=* wrapper.masterNode) } - // Local Interrupts must be synchronized to the core clock - // before being passed into this module. - // This allows faster latency for interrupts which are already synchronized. - // The CLINT and PLIC outputs interrupts that are synchronous to the periphery clock, - // so may or may not need to be synchronized depending on the Tile's crossing type. - // Debug interrupt is definitely asynchronous in all cases. - val asyncIntXbar = LazyModule(new IntXbar) - asyncIntXbar.suggestName("asyncIntXbar") - asyncIntXbar.intnode := debug.intnode // debug - wrapper.asyncIntNode := asyncIntXbar.intnode + // Connect the slave ports of the tile to the periphery bus + pbus.toTile(tp.name) { implicit p => crossing.slave.adapt(this)(wrapper.slaveNode :*= wrapper.crossTLIn) } - val periphIntXbar = LazyModule(new IntXbar) - periphIntXbar.suggestName("periphIntXbar") - periphIntXbar.intnode := clint.intnode // msip+mtip - periphIntXbar.intnode := plic.intnode // meip - if (tp.core.useVM) periphIntXbar.intnode := plic.intnode // seip - wrapper.periphIntNode := periphIntXbar.intnode + // Handle all the different types of interrupts crossing to or from the tile: + // 1. Debug interrupt is definitely asynchronous in all cases. + // 2. The CLINT and PLIC output interrupts are synchronous to the periphery clock, + // so might need to be synchronized depending on the Tile's crossing type. + // 3. Local Interrupts are required to already be synchronous to the tile clock. + // 4. Interrupts coming out of the tile are sent to the PLIC, + // so might need to be synchronized depending on the Tile's crossing type. + // NOTE: The order of calls to := matters! They must match how interrupts + // are decoded from rocket.intNode inside the tile. - val coreIntXbar = LazyModule(new IntXbar) - coreIntXbar.suggestName("coreIntXbar") - lip.foreach { coreIntXbar.intnode := _ } // lip - wrapper.coreIntNode := coreIntXbar.intnode + wrapper.intXbar.intnode := wrapper { IntSyncCrossingSink(3) } := debug.intnode // 1. always async crossign - wrapper.intOutputNode.foreach { case int => - val rocketIntXing = LazyModule(new IntXing(wrapper.outputInterruptXingLatency)) - FlipRendering { implicit p => rocketIntXing.intnode := int } - plic.intnode := rocketIntXing.intnode + // 2. clint+plic conditionak crossing + val periphIntNode = SourceCardinality { implicit p => wrapper.intXbar.intnode :=? wrapper.crossIntIn } + periphIntNode := clint.intnode // msip+mtip + periphIntNode := plic.intnode // meip + if (tp.core.useVM) periphIntNode := plic.intnode // seip + + lip.foreach { wrapper.intXbar.intnode := _ } // 3. lip never crosses + + // From core to PLIC + wrapper.rocket.intOutputNode.foreach { i => // 4. conditional crossing + FlipRendering { implicit p => SourceCardinality { implicit p => + plic.intnode :=? wrapper.crossIntOut :=? i + } } } wrapper diff --git a/src/main/scala/coreplex/SystemBus.scala b/src/main/scala/coreplex/SystemBus.scala index c07138f9..138adc81 100644 --- a/src/main/scala/coreplex/SystemBus.scala +++ b/src/main/scala/coreplex/SystemBus.scala @@ -39,9 +39,7 @@ class SystemBus(params: SystemBusParams)(implicit p: Parameters) extends TLBusWr def toSplitSlaves: TLOutwardNode = outwardSplitNode def toPeripheryBus(addBuffers: Int = 0): TLOutwardNode = { - val (in, out) = bufferChain(addBuffers, name = Some("pbus")) - in := pbus_fixer.node - out + TLBuffer.chain(addBuffers).foldRight(pbus_fixer.node:TLOutwardNode)(_ := _) } val toMemoryBus: TLOutwardNode = outwardNode @@ -52,34 +50,14 @@ class SystemBus(params: SystemBusParams)(implicit p: Parameters) extends TLBusWr def fromFrontBus: TLInwardNode = master_splitter.node - def fromSyncTiles(params: BufferParams, adapt: () => TLNodeChain, name: Option[String] = None): TLInwardNode = { - val adapters = adapt() // wanted to be called inside SystemBus scope - val tile_sink = LazyModule(new TLBuffer(params)) - name.foreach { n => tile_sink.suggestName(s"${busName}_${n}_TLBuffer") } - - adapters.in :=* tile_sink.node - master_splitter.node :=* adapters.out - tile_sink.node - } - - def fromRationalTiles(dir: RationalDirection, adapt: () => TLNodeChain, name: Option[String] = None): TLRationalInwardNode = { - val adapters = adapt() // wanted to be called inside SystemBus scope - val tile_sink = LazyModule(new TLRationalCrossingSink(direction = dir)) - name.foreach { n => tile_sink.suggestName(s"${busName}_${n}_TLRationalCrossingSink") } - - adapters.in :=* tile_sink.node - master_splitter.node :=* adapters.out - tile_sink.node - } - - def fromAsyncTiles(depth: Int, sync: Int, adapt: () => TLNodeChain, name: Option[String] = None): TLAsyncInwardNode = { - val adapters = adapt() // wanted to be called inside SystemBus scope - val tile_sink = LazyModule(new TLAsyncCrossingSink(depth, sync)) - name.foreach { n => tile_sink.suggestName(s"${busName}_${n}_TLAsyncCrossingSink") } - - adapters.in :=* tile_sink.node - master_splitter.node :=* adapters.out - tile_sink.node + def fromTile(name: Option[String])(gen: Parameters => TLOutwardNode) { + this { + LazyScope(s"${busName}FromTile${name.getOrElse("")}") { + SourceCardinality { implicit p => + master_splitter.node :=* gen(p) + } + } + } } def fromSyncPorts(params: BufferParams = BufferParams.default, name: Option[String] = None): TLInwardNode = { diff --git a/src/main/scala/coreplex/package.scala b/src/main/scala/coreplex/package.scala new file mode 100644 index 00000000..fbd07861 --- /dev/null +++ b/src/main/scala/coreplex/package.scala @@ -0,0 +1,12 @@ +// See LICENSE.SiFive for license details. + +package freechips.rocketchip + +import freechips.rocketchip.tilelink.TLOutwardNode +import freechips.rocketchip.interrupts.IntOutwardNode + +package object coreplex +{ + implicit class TLCrossableNode(val node: TLOutwardNode) + implicit class IntCrossableNode(val node: IntOutwardNode) +} diff --git a/src/main/scala/devices/debug/Debug.scala b/src/main/scala/devices/debug/Debug.scala index 37f85ff2..68f45745 100644 --- a/src/main/scala/devices/debug/Debug.scala +++ b/src/main/scala/devices/debug/Debug.scala @@ -9,6 +9,7 @@ import freechips.rocketchip.regmapper._ import freechips.rocketchip.rocket.Instructions import freechips.rocketchip.tile.XLen import freechips.rocketchip.tilelink._ +import freechips.rocketchip.interrupts._ import freechips.rocketchip.util._ /** Constant values used by both Debug Bus Response & Request @@ -392,7 +393,7 @@ class TLDebugModuleOuterAsync(device: Device)(implicit p: Parameters) extends La val dmiXbar = LazyModule (new TLXbar()) val dmOuter = LazyModule( new TLDebugModuleOuter(device)) - val intnode = dmOuter.intnode + val intnode: IntSyncOutwardNode = IntSyncCrossingSource(alreadyRegistered = true) :*= dmOuter.intnode val dmiInnerNode = TLAsyncCrossingSource()(dmiXbar.node) @@ -401,7 +402,7 @@ class TLDebugModuleOuterAsync(device: Device)(implicit p: Parameters) extends La lazy val module = new LazyModuleImp(this) { - val nComponents = intnode.out.size + val nComponents = dmOuter.intnode.edges.out.size val io = IO(new Bundle { val dmi = new DMIIO()(p).flip() @@ -1039,7 +1040,7 @@ class TLDebugModule(implicit p: Parameters) extends LazyModule { } val dmOuter = LazyModule(new TLDebugModuleOuterAsync(device)(p)) - val dmInner = LazyModule(new TLDebugModuleInnerAsync(device, () => {intnode.edges.out.size})(p)) + val dmInner = LazyModule(new TLDebugModuleInnerAsync(device, () => {dmOuter.dmOuter.intnode.edges.out.size})(p)) val node = dmInner.tlNode val intnode = dmOuter.intnode @@ -1047,7 +1048,7 @@ class TLDebugModule(implicit p: Parameters) extends LazyModule { dmInner.dmiNode := dmOuter.dmiInnerNode lazy val module = new LazyModuleImp(this) { - val nComponents = intnode.out.size + val nComponents = dmOuter.dmOuter.intnode.edges.out.size val io = IO(new Bundle { val ctrl = new DebugCtrlBundle(nComponents) diff --git a/src/main/scala/devices/tilelink/Clint.scala b/src/main/scala/devices/tilelink/Clint.scala index 0f80640b..9cc83030 100644 --- a/src/main/scala/devices/tilelink/Clint.scala +++ b/src/main/scala/devices/tilelink/Clint.scala @@ -9,6 +9,7 @@ import freechips.rocketchip.diplomacy._ import freechips.rocketchip.regmapper._ import freechips.rocketchip.tile.XLen import freechips.rocketchip.tilelink._ +import freechips.rocketchip.interrupts._ import freechips.rocketchip.util._ import scala.math.{min,max} diff --git a/src/main/scala/devices/tilelink/Plic.scala b/src/main/scala/devices/tilelink/Plic.scala index 73c9d681..a6ede5b7 100644 --- a/src/main/scala/devices/tilelink/Plic.scala +++ b/src/main/scala/devices/tilelink/Plic.scala @@ -10,6 +10,7 @@ import freechips.rocketchip.diplomacy._ import freechips.rocketchip.regmapper._ import freechips.rocketchip.tile.XLen import freechips.rocketchip.tilelink._ +import freechips.rocketchip.interrupts._ import freechips.rocketchip.util._ import scala.math.min diff --git a/src/main/scala/diplomacy/LazyModule.scala b/src/main/scala/diplomacy/LazyModule.scala index 396135be..48243e61 100644 --- a/src/main/scala/diplomacy/LazyModule.scala +++ b/src/main/scala/diplomacy/LazyModule.scala @@ -11,17 +11,26 @@ import scala.util.matching._ abstract class LazyModule()(implicit val p: Parameters) { - protected[diplomacy] var bindings = List[() => Unit]() protected[diplomacy] var children = List[LazyModule]() protected[diplomacy] var nodes = List[BaseNode]() protected[diplomacy] var info: SourceInfo = UnlocatableSourceInfo protected[diplomacy] val parent = LazyModule.scope + def parents: Seq[LazyModule] = parent match { + case None => Nil + case Some(x) => x +: x.parents + } + LazyModule.scope = Some(this) parent.foreach(p => p.children = this :: p.children) + // suggestedName accumulates Some(names), taking the final one. Nones are ignored. private var suggestedName: Option[String] = None - def suggestName(x: String) = suggestedName = Some(x) + def suggestName(x: String): this.type = suggestName(Some(x)) + def suggestName(x: Option[String]): this.type = { + x.foreach { n => suggestedName = Some(n) } + this + } private lazy val childNames = getClass.getMethods.filter { m => @@ -54,6 +63,7 @@ abstract class LazyModule()(implicit val p: Parameters) def name = valName.getOrElse(className) def line = sourceLine(info) + def instantiate() { } // a hook for running things in module scope (after children exist, but before dangles+auto exists) def module: LazyModuleImpLike def omitGraphML: Boolean = !nodes.exists(!_.omitGraphML) && !children.exists(!_.omitGraphML) @@ -154,6 +164,7 @@ sealed trait LazyModuleImpLike extends BaseModule implicit val sourceInfo = c.info Module(c.module).dangles } + wrapper.instantiate() val nodeDangles = wrapper.nodes.reverse.flatMap(_.instantiate()) val allDangles = nodeDangles ++ childDangles val pairing = SortedMap(allDangles.groupBy(_.source).toSeq:_*) @@ -168,7 +179,6 @@ sealed trait LazyModuleImpLike extends BaseModule if (d.flipped) { d.data <> io } else { io <> d.data } d.copy(data = io, name = wrapper.valName.getOrElse("anon") + "_" + d.name) } - wrapper.bindings.reverse.foreach { f => f () } (auto, dangles) } } diff --git a/src/main/scala/diplomacy/Nodes.scala b/src/main/scala/diplomacy/Nodes.scala index 40da6ab3..7c4b0eb8 100644 --- a/src/main/scala/diplomacy/Nodes.scala +++ b/src/main/scala/diplomacy/Nodes.scala @@ -98,6 +98,8 @@ abstract class BaseNode(implicit val valName: ValName) def omitGraphML = outputs.isEmpty && inputs.isEmpty lazy val nodedebugstring: String = "" + def parents: Seq[LazyModule] = lazyModule +: lazyModule.parents + def wirePrefix = { val camelCase = "([a-z])([A-Z])".r val decamel = camelCase.replaceAllIn(valName.name, _ match { case camelCase(l, h) => l + "_" + h }) @@ -108,8 +110,8 @@ abstract class BaseNode(implicit val valName: ValName) protected[diplomacy] def gci: Option[BaseNode] // greatest common inner protected[diplomacy] def gco: Option[BaseNode] // greatest common outer - protected[diplomacy] def inputs: Seq[(BaseNode, RenderedEdge)] - protected[diplomacy] def outputs: Seq[(BaseNode, RenderedEdge)] + def inputs: Seq[(BaseNode, RenderedEdge)] + def outputs: Seq[(BaseNode, RenderedEdge)] } object BaseNode @@ -117,22 +119,53 @@ object BaseNode protected[diplomacy] var serial = 0 } -// !!! rename the nodes we bind? -case class NodeHandle[DI, UI, BI <: Data, DO, UO, BO <: Data] +trait NoHandle +case object NoHandleObject extends NoHandle + +trait NodeHandle[DI, UI, BI <: Data, DO, UO, BO <: Data] + extends InwardNodeHandle[DI, UI, BI] with OutwardNodeHandle[DO, UO, BO] +{ + // connecting two full nodes => full node + override def := [DX, UX, BX <: Data](h: NodeHandle[DX, UX, BX, DI, UI, BI])(implicit p: Parameters, sourceInfo: SourceInfo): NodeHandle[DX, UX, BX, DO, UO, BO] = { bind(h, BIND_ONCE); NodeHandle(h, this) } + override def :*= [DX, UX, BX <: Data](h: NodeHandle[DX, UX, BX, DI, UI, BI])(implicit p: Parameters, sourceInfo: SourceInfo): NodeHandle[DX, UX, BX, DO, UO, BO] = { bind(h, BIND_STAR); NodeHandle(h, this) } + override def :=* [DX, UX, BX <: Data](h: NodeHandle[DX, UX, BX, DI, UI, BI])(implicit p: Parameters, sourceInfo: SourceInfo): NodeHandle[DX, UX, BX, DO, UO, BO] = { bind(h, BIND_QUERY); NodeHandle(h, this) } + override def :=? [DX, UX, BX <: Data](h: NodeHandle[DX, UX, BX, DI, UI, BI])(implicit p: Parameters, sourceInfo: SourceInfo): NodeHandle[DX, UX, BX, DO, UO, BO] = { bind(h, p(CardinalityInferenceDirectionKey)); NodeHandle(h, this) } + // connecting a full node with an output => an output + override def := (h: OutwardNodeHandle[DI, UI, BI])(implicit p: Parameters, sourceInfo: SourceInfo): OutwardNodeHandle[DO, UO, BO] = { bind(h, BIND_ONCE); this } + override def :*= (h: OutwardNodeHandle[DI, UI, BI])(implicit p: Parameters, sourceInfo: SourceInfo): OutwardNodeHandle[DO, UO, BO] = { bind(h, BIND_STAR); this } + override def :=* (h: OutwardNodeHandle[DI, UI, BI])(implicit p: Parameters, sourceInfo: SourceInfo): OutwardNodeHandle[DO, UO, BO] = { bind(h, BIND_QUERY); this } + override def :=? (h: OutwardNodeHandle[DI, UI, BI])(implicit p: Parameters, sourceInfo: SourceInfo): OutwardNodeHandle[DO, UO, BO] = { bind(h, p(CardinalityInferenceDirectionKey)); this } +} + +object NodeHandle +{ + def apply[DI, UI, BI <: Data, DO, UO, BO <: Data](i: InwardNodeHandle[DI, UI, BI], o: OutwardNodeHandle[DO, UO, BO]) = NodeHandlePair(i, o) +} + +case class NodeHandlePair[DI, UI, BI <: Data, DO, UO, BO <: Data] (inwardHandle: InwardNodeHandle[DI, UI, BI], outwardHandle: OutwardNodeHandle[DO, UO, BO]) - extends Object with InwardNodeHandle[DI, UI, BI] with OutwardNodeHandle[DO, UO, BO] + extends NodeHandle[DI, UI, BI, DO, UO, BO] { val inward = inwardHandle.inward val outward = outwardHandle.outward } -trait InwardNodeHandle[DI, UI, BI <: Data] +trait InwardNodeHandle[DI, UI, BI <: Data] extends NoHandle { - protected[diplomacy] val inward: InwardNode[DI, UI, BI] - def := (h: OutwardNodeHandle[DI, UI, BI])(implicit p: Parameters, sourceInfo: SourceInfo) { inward.:=(h)(p, sourceInfo) } - def :*= (h: OutwardNodeHandle[DI, UI, BI])(implicit p: Parameters, sourceInfo: SourceInfo) { inward.:*=(h)(p, sourceInfo) } - def :=* (h: OutwardNodeHandle[DI, UI, BI])(implicit p: Parameters, sourceInfo: SourceInfo) { inward.:=*(h)(p, sourceInfo) } - def :=? (h: OutwardNodeHandle[DI, UI, BI])(implicit p: Parameters, sourceInfo: SourceInfo) { inward.:=?(h)(p, sourceInfo) } + def inward: InwardNode[DI, UI, BI] + def parentsIn: Seq[LazyModule] = inward.parents + def bind(h: OutwardNodeHandle[DI, UI, BI], binding: NodeBinding)(implicit p: Parameters, sourceInfo: SourceInfo): Unit = inward.bind(h.outward, binding) + + // connecting an input node with a full nodes => an input node + def := [DX, UX, BX <: Data](h: NodeHandle[DX, UX, BX, DI, UI, BI])(implicit p: Parameters, sourceInfo: SourceInfo): InwardNodeHandle[DX, UX, BX] = { bind(h, BIND_ONCE); h } + def :*= [DX, UX, BX <: Data](h: NodeHandle[DX, UX, BX, DI, UI, BI])(implicit p: Parameters, sourceInfo: SourceInfo): InwardNodeHandle[DX, UX, BX] = { bind(h, BIND_STAR); h } + def :=* [DX, UX, BX <: Data](h: NodeHandle[DX, UX, BX, DI, UI, BI])(implicit p: Parameters, sourceInfo: SourceInfo): InwardNodeHandle[DX, UX, BX] = { bind(h, BIND_QUERY); h } + def :=? [DX, UX, BX <: Data](h: NodeHandle[DX, UX, BX, DI, UI, BI])(implicit p: Parameters, sourceInfo: SourceInfo): InwardNodeHandle[DX, UX, BX] = { bind(h, p(CardinalityInferenceDirectionKey)); h } + // connecting input node with output node => no node + def := (h: OutwardNodeHandle[DI, UI, BI])(implicit p: Parameters, sourceInfo: SourceInfo): NoHandle = { bind(h, BIND_ONCE); NoHandleObject } + def :*= (h: OutwardNodeHandle[DI, UI, BI])(implicit p: Parameters, sourceInfo: SourceInfo): NoHandle = { bind(h, BIND_STAR); NoHandleObject } + def :=* (h: OutwardNodeHandle[DI, UI, BI])(implicit p: Parameters, sourceInfo: SourceInfo): NoHandle = { bind(h, BIND_QUERY); NoHandleObject } + def :=? (h: OutwardNodeHandle[DI, UI, BI])(implicit p: Parameters, sourceInfo: SourceInfo): NoHandle = { bind(h, p(CardinalityInferenceDirectionKey)); NoHandleObject } } sealed trait NodeBinding @@ -140,9 +173,18 @@ case object BIND_ONCE extends NodeBinding case object BIND_QUERY extends NodeBinding case object BIND_STAR extends NodeBinding +object NodeBinding +{ + implicit def apply(card: CardinalityInferenceDirection.T): NodeBinding = card match { + case CardinalityInferenceDirection.SOURCE_TO_SINK => BIND_QUERY + case CardinalityInferenceDirection.SINK_TO_SOURCE => BIND_STAR + case CardinalityInferenceDirection.NO_INFERENCE => BIND_ONCE + } +} + trait InwardNode[DI, UI, BI <: Data] extends BaseNode with InwardNodeHandle[DI, UI, BI] { - protected[diplomacy] val inward = this + val inward = this protected[diplomacy] val numPI: Range.Inclusive require (!numPI.isEmpty, s"No number of inputs would be acceptable to ${name}${lazyModule.line}") @@ -165,16 +207,19 @@ trait InwardNode[DI, UI, BI <: Data] extends BaseNode with InwardNodeHandle[DI, protected[diplomacy] val iStar: Int protected[diplomacy] val iPortMapping: Seq[(Int, Int)] protected[diplomacy] val iParams: Seq[UI] + + protected[diplomacy] def bind(h: OutwardNode[DI, UI, BI], binding: NodeBinding)(implicit p: Parameters, sourceInfo: SourceInfo): Unit } -trait OutwardNodeHandle[DO, UO, BO <: Data] +trait OutwardNodeHandle[DO, UO, BO <: Data] extends NoHandle { - protected[diplomacy] val outward: OutwardNode[DO, UO, BO] + def outward: OutwardNode[DO, UO, BO] + def parentsOut: Seq[LazyModule] = outward.parents } trait OutwardNode[DO, UO, BO <: Data] extends BaseNode with OutwardNodeHandle[DO, UO, BO] { - protected[diplomacy] val outward = this + val outward = this protected[diplomacy] val numPO: Range.Inclusive require (!numPO.isEmpty, s"No number of outputs would be acceptable to ${name}${lazyModule.line}") @@ -211,7 +256,7 @@ sealed abstract class MixedNode[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data]( protected[diplomacy] val numPO: Range.Inclusive, protected[diplomacy] val numPI: Range.Inclusive)( implicit valName: ValName) - extends BaseNode with InwardNode[DI, UI, BI] with OutwardNode[DO, UO, BO] + extends BaseNode with NodeHandle[DI, UI, BI, DO, UO, BO] with InwardNode[DI, UI, BI] with OutwardNode[DO, UO, BO] { protected[diplomacy] def resolveStar(iKnown: Int, oKnown: Int, iStar: Int, oStar: Int): (Int, Int) protected[diplomacy] def mapParamsD(n: Int, p: Seq[DI]): Seq[DO] @@ -337,9 +382,9 @@ sealed abstract class MixedNode[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data]( } // connects the outward part of a node with the inward part of this node - private def bind(h: OutwardNodeHandle[DI, UI, BI], binding: NodeBinding)(implicit p: Parameters, sourceInfo: SourceInfo) { + protected[diplomacy] def bind(h: OutwardNode[DI, UI, BI], binding: NodeBinding)(implicit p: Parameters, sourceInfo: SourceInfo) { val x = this // x := y - val y = h.outward + val y = h val info = sourceLine(sourceInfo, " at ", "") val i = x.iPushed val o = y.oPushed @@ -350,23 +395,12 @@ sealed abstract class MixedNode[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data]( x.iPush(o, y, binding) } - override def := (h: OutwardNodeHandle[DI, UI, BI])(implicit p: Parameters, sourceInfo: SourceInfo) = bind(h, BIND_ONCE) - override def :*= (h: OutwardNodeHandle[DI, UI, BI])(implicit p: Parameters, sourceInfo: SourceInfo) = bind(h, BIND_STAR) - override def :=* (h: OutwardNodeHandle[DI, UI, BI])(implicit p: Parameters, sourceInfo: SourceInfo) = bind(h, BIND_QUERY) - override def :=? (h: OutwardNodeHandle[DI, UI, BI])(implicit p: Parameters, sourceInfo: SourceInfo) = { - p(CardinalityInferenceDirectionKey) match { - case CardinalityInferenceDirection.SOURCE_TO_SINK => this :=* h - case CardinalityInferenceDirection.SINK_TO_SOURCE => this :*= h - case CardinalityInferenceDirection.NO_INFERENCE => this := h - } - } - // meta-data for printing the node graph - protected[diplomacy] def inputs = (iPorts zip edgesIn) map { case ((_, n, p, _), e) => + def inputs = (iPorts zip edgesIn) map { case ((_, n, p, _), e) => val re = inner.render(e) (n, re.copy(flipped = re.flipped != p(RenderFlipped))) } - protected[diplomacy] def outputs = oPorts map { case (i, n, _, _) => (n, n.inputs(i)._2) } + def outputs = oPorts map { case (i, n, _, _) => (n, n.inputs(i)._2) } } abstract class MixedCustomNode[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data]( diff --git a/src/main/scala/groundtest/Coreplex.scala b/src/main/scala/groundtest/Coreplex.scala index ee8ebe6b..73a37433 100644 --- a/src/main/scala/groundtest/Coreplex.scala +++ b/src/main/scala/groundtest/Coreplex.scala @@ -25,8 +25,8 @@ class GroundTestCoreplex(implicit p: Parameters) extends BaseCoreplex }) )} - tiles.flatMap(_.dcacheOpt).foreach { - sbus.fromSyncTiles(BufferParams.default, TileMasterPortParams().adapterChain(this)) :=* _.node + tiles.flatMap(_.dcacheOpt).foreach { dc => + sbus.fromTile(None) { implicit p => TileMasterPortParams(addBuffers = 1).adapt(this)(dc.node) } } val pbusRAM = LazyModule(new TLRAM(AddressSet(testRamAddr, 0xffff), true, false, pbus.beatBytes)) diff --git a/src/main/scala/interrupts/Bundles.scala b/src/main/scala/interrupts/Bundles.scala new file mode 100644 index 00000000..86691f9e --- /dev/null +++ b/src/main/scala/interrupts/Bundles.scala @@ -0,0 +1,12 @@ +// See LICENSE.SiFive for license details. + +package freechips.rocketchip.interrupts + +import Chisel._ +import freechips.rocketchip.diplomacy._ +import freechips.rocketchip.util._ + +class SyncInterrupts(params: IntEdge) extends GenericParameterizedBundle(params) +{ + val sync = Vec(params.source.num, Bool()) +} diff --git a/src/main/scala/interrupts/Crossing.scala b/src/main/scala/interrupts/Crossing.scala new file mode 100644 index 00000000..d7f53a26 --- /dev/null +++ b/src/main/scala/interrupts/Crossing.scala @@ -0,0 +1,58 @@ +// See LICENSE.SiFive for license details. + +package freechips.rocketchip.interrupts + +import Chisel._ +import freechips.rocketchip.config.Parameters +import freechips.rocketchip.util.SynchronizerShiftReg +import freechips.rocketchip.diplomacy._ + +@deprecated("IntXing does not ensure interrupt source is glitch free. Use IntSyncSource and IntSyncSink", "rocket-chip 1.2") +class IntXing(sync: Int = 3)(implicit p: Parameters) extends LazyModule +{ + val intnode = IntAdapterNode() + + lazy val module = new LazyModuleImp(this) { + (intnode.in zip intnode.out) foreach { case ((in, _), (out, _)) => + out := SynchronizerShiftReg(in, sync) + } + } +} + +object IntSyncCrossingSource +{ + def apply(alreadyRegistered: Boolean = false)(implicit p: Parameters) = LazyModule(new IntSyncCrossingSource(alreadyRegistered)).node +} + + +class IntSyncCrossingSource(alreadyRegistered: Boolean = false)(implicit p: Parameters) extends LazyModule +{ + val node = IntSyncSourceNode(alreadyRegistered) + + lazy val module = new LazyModuleImp(this) { + (node.in zip node.out) foreach { case ((in, edgeIn), (out, edgeOut)) => + if (alreadyRegistered) { + out.sync := in + } else { + out.sync := RegNext(in) + } + } + } +} + + +class IntSyncCrossingSink(sync: Int = 3)(implicit p: Parameters) extends LazyModule +{ + val node = IntSyncSinkNode(sync) + + lazy val module = new LazyModuleImp(this) { + (node.in zip node.out) foreach { case ((in, edgeIn), (out, edgeOut)) => + out := SynchronizerShiftReg(in.sync, sync) + } + } +} + +object IntSyncCrossingSink +{ + def apply(sync: Int = 3)(implicit p: Parameters) = LazyModule(new IntSyncCrossingSink(sync)).node +} diff --git a/src/main/scala/interrupts/Nodes.scala b/src/main/scala/interrupts/Nodes.scala new file mode 100644 index 00000000..6b431cc2 --- /dev/null +++ b/src/main/scala/interrupts/Nodes.scala @@ -0,0 +1,68 @@ +// See LICENSE.SiFive for license details. + +package freechips.rocketchip.interrupts + +import Chisel._ +import chisel3.internal.sourceinfo.SourceInfo +import freechips.rocketchip.config.Parameters +import freechips.rocketchip.diplomacy._ + +object IntImp extends SimpleNodeImp[IntSourcePortParameters, IntSinkPortParameters, IntEdge, Vec[Bool]] +{ + def edge(pd: IntSourcePortParameters, pu: IntSinkPortParameters, p: Parameters, sourceInfo: SourceInfo) = IntEdge(pd, pu, p, sourceInfo) + def bundle(e: IntEdge) = Vec(e.source.num, Bool()) + def render(e: IntEdge) = RenderedEdge(colour = "#0000ff" /* blue */, label = e.source.sources.map(_.range.size).sum.toString, flipped = true) + + override def mixO(pd: IntSourcePortParameters, node: OutwardNode[IntSourcePortParameters, IntSinkPortParameters, Vec[Bool]]): IntSourcePortParameters = + pd.copy(sources = pd.sources.map { s => s.copy (nodePath = node +: s.nodePath) }) + override def mixI(pu: IntSinkPortParameters, node: InwardNode[IntSourcePortParameters, IntSinkPortParameters, Vec[Bool]]): IntSinkPortParameters = + pu.copy(sinks = pu.sinks.map { s => s.copy (nodePath = node +: s.nodePath) }) +} + +case class IntSourceNode(portParams: Seq[IntSourcePortParameters])(implicit valName: ValName) extends SourceNode(IntImp)(portParams) +case class IntSinkNode(portParams: Seq[IntSinkPortParameters])(implicit valName: ValName) extends SinkNode(IntImp)(portParams) +case class IntAdapterNode( + sourceFn: IntSourcePortParameters => IntSourcePortParameters = { s => s }, + sinkFn: IntSinkPortParameters => IntSinkPortParameters = { s => s }, + num: Range.Inclusive = 0 to 999)( + implicit valName: ValName) + extends AdapterNode(IntImp)(sourceFn, sinkFn, num) +case class IntIdentityNode()(implicit valName: ValName) extends IdentityNode(IntImp)() + +case class IntNexusNode( + sourceFn: Seq[IntSourcePortParameters] => IntSourcePortParameters, + sinkFn: Seq[IntSinkPortParameters] => IntSinkPortParameters, + numSourcePorts: Range.Inclusive = 0 to 128, + numSinkPorts: Range.Inclusive = 0 to 128)( + implicit valName: ValName) + extends NexusNode(IntImp)(sourceFn, sinkFn, numSourcePorts, numSinkPorts) + +object IntSyncImp extends SimpleNodeImp[IntSourcePortParameters, IntSinkPortParameters, IntEdge, SyncInterrupts] +{ + def edge(pd: IntSourcePortParameters, pu: IntSinkPortParameters, p: Parameters, sourceInfo: SourceInfo) = IntEdge(pd, pu, p, sourceInfo) + def bundle(e: IntEdge) = new SyncInterrupts(e) + def render(e: IntEdge) = RenderedEdge(colour = "#ff00ff" /* purple */, label = e.source.sources.map(_.range.size).sum.toString, flipped = true) + + override def mixO(pd: IntSourcePortParameters, node: OutwardNode[IntSourcePortParameters, IntSinkPortParameters, SyncInterrupts]): IntSourcePortParameters = + pd.copy(sources = pd.sources.map { s => s.copy (nodePath = node +: s.nodePath) }) + override def mixI(pu: IntSinkPortParameters, node: InwardNode[IntSourcePortParameters, IntSinkPortParameters, SyncInterrupts]): IntSinkPortParameters = + pu.copy(sinks = pu.sinks.map { s => s.copy (nodePath = node +: s.nodePath) }) +} + +case class IntSyncIdentityNode()(implicit valName: ValName) extends IdentityNode(IntSyncImp)() + +case class IntSyncSourceNode(alreadyRegistered: Boolean)(implicit valName: ValName) + extends MixedAdapterNode(IntImp, IntSyncImp)( + dFn = { p => p }, + uFn = { p => p }) +{ + override lazy val nodedebugstring = s"alreadyRegistered:${alreadyRegistered}" +} + +case class IntSyncSinkNode(sync: Int)(implicit valName: ValName) + extends MixedAdapterNode(IntSyncImp, IntImp)( + dFn = { p => p }, + uFn = { p => p }) +{ + override lazy val nodedebugstring = s"sync:${sync}" +} diff --git a/src/main/scala/interrupts/Parameters.scala b/src/main/scala/interrupts/Parameters.scala new file mode 100644 index 00000000..bb3e37a1 --- /dev/null +++ b/src/main/scala/interrupts/Parameters.scala @@ -0,0 +1,61 @@ +// See LICENSE.SiFive for license details. + +package freechips.rocketchip.interrupts + +import Chisel._ +import chisel3.internal.sourceinfo.SourceInfo +import freechips.rocketchip.config.Parameters +import freechips.rocketchip.diplomacy._ + +// A potentially empty half-open range; [start, end) +case class IntRange(start: Int, end: Int) +{ + require (start >= 0) + require (start <= end) + def size = end - start + def overlaps(x: IntRange) = start < x.end && x.start < end + def offset(x: Int) = IntRange(x+start, x+end) +} + +object IntRange +{ + implicit def apply(end: Int): IntRange = apply(0, end) +} + +case class IntSourceParameters( + range: IntRange, + resources: Seq[Resource] = Seq(), + nodePath: Seq[BaseNode] = Seq()) +{ + val name = nodePath.lastOption.map(_.lazyModule.name).getOrElse("disconnected") +} + +case class IntSinkParameters( + nodePath: Seq[BaseNode] = Seq()) +{ + val name = nodePath.lastOption.map(_.lazyModule.name).getOrElse("disconnected") +} + +case class IntSourcePortParameters(sources: Seq[IntSourceParameters]) +{ + val num = sources.map(_.range.size).sum + // The interrupts mapping must not overlap + sources.map(_.range).combinations(2).foreach { case Seq(a, b) => require (!a.overlaps(b)) } + // The interrupts must perfectly cover the range + require (sources.isEmpty || sources.map(_.range.end).max == num) +} +object IntSourcePortSimple +{ + def apply(num: Int = 1, ports: Int = 1, sources: Int = 1, resources: Seq[Resource] = Nil) = + if (num == 0) Nil else + Seq.fill(ports)(IntSourcePortParameters(Seq.fill(sources)(IntSourceParameters(range = IntRange(0, num), resources = resources)))) +} + +case class IntSinkPortParameters(sinks: Seq[IntSinkParameters]) +object IntSinkPortSimple +{ + def apply(ports: Int = 1, sinks: Int = 1) = + Seq.fill(ports)(IntSinkPortParameters(Seq.fill(sinks)(IntSinkParameters()))) +} + +case class IntEdge(source: IntSourcePortParameters, sink: IntSinkPortParameters, params: Parameters, sourceInfo: SourceInfo) diff --git a/src/main/scala/interrupts/Xbar.scala b/src/main/scala/interrupts/Xbar.scala new file mode 100644 index 00000000..556974ef --- /dev/null +++ b/src/main/scala/interrupts/Xbar.scala @@ -0,0 +1,23 @@ +// See LICENSE.SiFive for license details. + +package freechips.rocketchip.interrupts + +import Chisel._ +import freechips.rocketchip.config.Parameters +import freechips.rocketchip.diplomacy._ + +class IntXbar()(implicit p: Parameters) extends LazyModule +{ + val intnode = IntNexusNode( + sinkFn = { _ => IntSinkPortParameters(Seq(IntSinkParameters())) }, + sourceFn = { seq => + IntSourcePortParameters((seq zip seq.map(_.num).scanLeft(0)(_+_).init).map { + case (s, o) => s.sources.map(z => z.copy(range = z.range.offset(o))) + }.flatten) + }) + + lazy val module = new LazyModuleImp(this) { + val cat = intnode.in.map { case (i, e) => i.take(e.source.num) }.flatten + intnode.out.foreach { case (o, _) => o := cat } + } +} diff --git a/src/main/scala/interrupts/package.scala b/src/main/scala/interrupts/package.scala new file mode 100644 index 00000000..91b089db --- /dev/null +++ b/src/main/scala/interrupts/package.scala @@ -0,0 +1,15 @@ +// See LICENSE.SiFive for license details. + +package freechips.rocketchip + +import Chisel._ +import freechips.rocketchip.diplomacy._ + +package object interrupts +{ + type IntInwardNode = InwardNodeHandle[IntSourcePortParameters, IntSinkPortParameters, Vec[Bool]] + type IntOutwardNode = OutwardNodeHandle[IntSourcePortParameters, IntSinkPortParameters, Vec[Bool]] + type IntNode = NodeHandle[IntSourcePortParameters, IntSinkPortParameters, Vec[Bool], IntSourcePortParameters, IntSinkPortParameters, Vec[Bool]] + type IntSyncInwardNode = InwardNodeHandle[IntSourcePortParameters, IntSinkPortParameters, SyncInterrupts] + type IntSyncOutwardNode = OutwardNodeHandle[IntSourcePortParameters, IntSinkPortParameters, SyncInterrupts] +} diff --git a/src/main/scala/rocket/BusErrorUnit.scala b/src/main/scala/rocket/BusErrorUnit.scala index 7ba2bc2d..d3928fee 100644 --- a/src/main/scala/rocket/BusErrorUnit.scala +++ b/src/main/scala/rocket/BusErrorUnit.scala @@ -11,6 +11,7 @@ import freechips.rocketchip.tile._ import freechips.rocketchip.diplomacy._ import freechips.rocketchip.regmapper._ import freechips.rocketchip.tilelink._ +import freechips.rocketchip.interrupts._ trait BusErrors extends Bundle { def toErrorList: List[Option[Valid[UInt]]] diff --git a/src/main/scala/rocket/ScratchpadSlavePort.scala b/src/main/scala/rocket/ScratchpadSlavePort.scala index dd8d002b..b154dfbc 100644 --- a/src/main/scala/rocket/ScratchpadSlavePort.scala +++ b/src/main/scala/rocket/ScratchpadSlavePort.scala @@ -10,6 +10,7 @@ import freechips.rocketchip.coreplex.CacheBlockBytes import freechips.rocketchip.diplomacy._ import freechips.rocketchip.tile._ import freechips.rocketchip.tilelink._ +import freechips.rocketchip.interrupts._ import freechips.rocketchip.util._ class ScratchpadSlavePort(address: AddressSet, coreDataBytes: Int, usingAtomics: Boolean)(implicit p: Parameters) extends LazyModule { diff --git a/src/main/scala/tile/Interrupts.scala b/src/main/scala/tile/Interrupts.scala index dcccada3..0043089d 100644 --- a/src/main/scala/tile/Interrupts.scala +++ b/src/main/scala/tile/Interrupts.scala @@ -5,7 +5,7 @@ package freechips.rocketchip.tile import Chisel._ import freechips.rocketchip.config.Parameters -import freechips.rocketchip.tilelink.{IntSinkNode, IntSinkPortSimple} +import freechips.rocketchip.interrupts.{IntSinkNode, IntSinkPortSimple} import freechips.rocketchip.util._ class TileInterrupts(implicit p: Parameters) extends CoreBundle()(p) { diff --git a/src/main/scala/tile/RocketTile.scala b/src/main/scala/tile/RocketTile.scala index 98849fef..fbb0bf9c 100644 --- a/src/main/scala/tile/RocketTile.scala +++ b/src/main/scala/tile/RocketTile.scala @@ -9,6 +9,7 @@ import freechips.rocketchip.coreplex._ import freechips.rocketchip.diplomacy._ import freechips.rocketchip.rocket._ import freechips.rocketchip.tilelink._ +import freechips.rocketchip.interrupts._ import freechips.rocketchip.util._ case class RocketTileParams( @@ -18,11 +19,12 @@ case class RocketTileParams( rocc: Seq[RoCCParams] = Nil, btb: Option[BTBParams] = Some(BTBParams()), dataScratchpadBytes: Int = 0, - boundaryBuffers: Boolean = false, trace: Boolean = false, hcfOnUncorrectable: Boolean = false, name: Option[String] = Some("tile"), - hartid: Int = 0) extends TileParams { + hartid: Int = 0, + boundaryBuffers: Boolean = false // if synthesized with hierarchical PnR, cut feed-throughs? + ) extends TileParams { require(icache.isDefined) require(dcache.isDefined) } @@ -187,38 +189,44 @@ class RocketTileWrapperBundle[+L <: RocketTileWrapper](_outer: L) extends BaseTi val halt_and_catch_fire = _outer.rocket.module.io.halt_and_catch_fire.map(_.cloneType) } -abstract class RocketTileWrapper(rtp: RocketTileParams)(implicit p: Parameters) extends BaseTile(rtp) { - val rocket = LazyModule(new RocketTile(rtp)) - val asyncIntNode : IntInwardNode - val periphIntNode : IntInwardNode - val coreIntNode : IntInwardNode - val intOutputNode = rocket.intOutputNode +class RocketTileWrapper( + params: RocketTileParams, + val crossing: CoreplexClockCrossing) + (implicit p: Parameters) extends BaseTile(params) with HasCrossing { + + val rocket = LazyModule(new RocketTile(params)) + + // The buffers needed to cut feed-through paths are microarchitecture specific, so belong here + val masterBuffer = LazyModule(new TLBuffer(BufferParams.none, BufferParams.flow, BufferParams.none, BufferParams.flow, BufferParams(1))) + val masterNode: TLOutwardNode = crossing match { + case _: AsynchronousCrossing => rocket.masterNode + case SynchronousCrossing(b) => + require (!params.boundaryBuffers || (b.depth >= 1 && !b.flow && !b.pipe), "Buffer misconfiguration creates feed-through paths") + rocket.masterNode + case RationalCrossing(dir) => + require (dir != SlowToFast, "Misconfiguration? Core slower than fabric") + if (params.boundaryBuffers) { + masterBuffer.node :=* rocket.masterNode + } else { + rocket.masterNode + } + } + + val slaveBuffer = LazyModule(new TLBuffer(BufferParams.flow, BufferParams.none, BufferParams.none, BufferParams.none, BufferParams.none)) + val slaveNode: TLInwardNode = crossing match { + case _: SynchronousCrossing => rocket.slaveNode // requirement already checked + case _: AsynchronousCrossing => rocket.slaveNode + case _: RationalCrossing => + if (params.boundaryBuffers) { + DisableMonitors { implicit p => rocket.slaveNode :*= slaveBuffer.node } + } else { + rocket.slaveNode + } + } + val intXbar = LazyModule(new IntXbar) - rocket.intNode := intXbar.intnode - def optionalMasterBuffer(in: TLOutwardNode): TLOutwardNode = { - if (rtp.boundaryBuffers) { - val mbuf = LazyModule(new TLBuffer(BufferParams.none, BufferParams.flow, BufferParams.none, BufferParams.flow, BufferParams(1))) - mbuf.node :=* in - mbuf.node - } else { - in - } - } - - def optionalSlaveBuffer(out: TLInwardNode): TLInwardNode = { - if (rtp.boundaryBuffers) { - val sbuf = LazyModule(new TLBuffer(BufferParams.flow, BufferParams.none, BufferParams.none, BufferParams.none, BufferParams.none)) - DisableMonitors { implicit p => out :*= sbuf.node } - sbuf.node - } else { - out - } - } - - def outputInterruptXingLatency: Int - override lazy val module = new BaseTileModule(this, () => new RocketTileWrapperBundle(this)) { // signals that do not change based on crossing type: rocket.module.io.hartid := io.hartid @@ -227,76 +235,3 @@ abstract class RocketTileWrapper(rtp: RocketTileParams)(implicit p: Parameters) io.halt_and_catch_fire.foreach { _ := rocket.module.io.halt_and_catch_fire.get } } } - -class SyncRocketTile(rtp: RocketTileParams)(implicit p: Parameters) extends RocketTileWrapper(rtp) { - val masterNode = optionalMasterBuffer(rocket.masterNode) - val slaveNode = optionalSlaveBuffer(rocket.slaveNode) - - // Fully async interrupts need synchronizers. - // Others need no synchronization. - val xing = LazyModule(new IntXing(3)) - val asyncIntNode = xing.intnode - - val periphIntNode = IntIdentityNode() - val coreIntNode = IntIdentityNode() - - // order here matters - intXbar.intnode := xing.intnode - intXbar.intnode := periphIntNode - intXbar.intnode := coreIntNode - - def outputInterruptXingLatency = 0 -} - -class AsyncRocketTile(rtp: RocketTileParams)(implicit p: Parameters) extends RocketTileWrapper(rtp) { - val source = LazyModule(new TLAsyncCrossingSource) - source.node :=* rocket.masterNode - val masterNode = source.node - - val sink = LazyModule(new TLAsyncCrossingSink) - DisableMonitors { implicit p => rocket.slaveNode :*= sink.node } - val slaveNode = sink.node - - // Fully async interrupts need synchronizers, - // as do those coming from the periphery clock. - // Others need no synchronization. - val asyncXing = LazyModule(new IntXing(3)) - val periphXing = LazyModule(new IntXing(3)) - val asyncIntNode = asyncXing.intnode - val periphIntNode = periphXing.intnode - val coreIntNode = IntIdentityNode() - - // order here matters - intXbar.intnode := asyncXing.intnode - intXbar.intnode := periphXing.intnode - intXbar.intnode := coreIntNode - - def outputInterruptXingLatency = 3 -} - -class RationalRocketTile(rtp: RocketTileParams)(implicit p: Parameters) extends RocketTileWrapper(rtp) { - val source = LazyModule(new TLRationalCrossingSource) - source.node :=* optionalMasterBuffer(rocket.masterNode) - val masterNode = source.node - - val sink = LazyModule(new TLRationalCrossingSink(SlowToFast)) - DisableMonitors { implicit p => optionalSlaveBuffer(rocket.slaveNode) :*= sink.node } - val slaveNode = sink.node - - // Fully async interrupts need synchronizers. - // Those coming from periphery clock need a - // rational synchronizer. - // Others need no synchronization. - val asyncXing = LazyModule(new IntXing(3)) - val periphXing = LazyModule(new IntXing(1)) - val asyncIntNode = asyncXing.intnode - val periphIntNode = periphXing.intnode - val coreIntNode = IntIdentityNode() - - // order here matters - intXbar.intnode := asyncXing.intnode - intXbar.intnode := periphXing.intnode - intXbar.intnode := coreIntNode - - def outputInterruptXingLatency = 1 -} diff --git a/src/main/scala/tilelink/AsyncCrossing.scala b/src/main/scala/tilelink/AsyncCrossing.scala index 3e9c5c2f..60bb59cb 100644 --- a/src/main/scala/tilelink/AsyncCrossing.scala +++ b/src/main/scala/tilelink/AsyncCrossing.scala @@ -7,6 +7,7 @@ import chisel3.internal.sourceinfo.SourceInfo import freechips.rocketchip.config.Parameters import freechips.rocketchip.diplomacy._ import freechips.rocketchip.util._ +import freechips.rocketchip.coreplex.{CrossingWrapper, AsynchronousCrossing} class TLAsyncCrossingSource(sync: Int = 3)(implicit p: Parameters) extends LazyModule { @@ -85,6 +86,7 @@ object TLAsyncCrossingSink } } +@deprecated("TLAsyncCrossing is fragile. Use TLAsyncCrossingSource and TLAsyncCrossingSink", "rocket-chip 1.2") class TLAsyncCrossing(depth: Int = 8, sync: Int = 3)(implicit p: Parameters) extends LazyModule { val source = LazyModule(new TLAsyncCrossingSource(sync)) @@ -113,26 +115,19 @@ import freechips.rocketchip.unittest._ class TLRAMAsyncCrossing(txns: Int)(implicit p: Parameters) extends LazyModule { val model = LazyModule(new TLRAMModel("AsyncCrossing")) - val ram = LazyModule(new TLRAM(AddressSet(0x0, 0x3ff))) val fuzz = LazyModule(new TLFuzzer(txns)) - val cross = LazyModule(new TLAsyncCrossing) + val island = LazyModule(new CrossingWrapper(AsynchronousCrossing(8))) + val ram = island { LazyModule(new TLRAM(AddressSet(0x0, 0x3ff))) } model.node := fuzz.node - cross.node := TLFragmenter(4, 256)(TLDelayer(0.1)(model.node)) - ram.node := cross.node + ram.node := island.crossTLIn := TLFragmenter(4, 256)(TLDelayer(0.1)(model.node)) lazy val module = new LazyModuleImp(this) with UnitTestModule { io.finished := fuzz.module.io.finished // Shove the RAM into another clock domain val clocks = Module(new Pow2ClockDivider(2)) - ram.module.clock := clocks.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 + island.module.clock := clocks.io.clock_out } } diff --git a/src/main/scala/tilelink/Buffer.scala b/src/main/scala/tilelink/Buffer.scala index bd35a614..63c142c0 100644 --- a/src/main/scala/tilelink/Buffer.scala +++ b/src/main/scala/tilelink/Buffer.scala @@ -71,28 +71,10 @@ object TLBuffer buffer.node :=? x buffer.node } -} -class TLBufferChain(depth: Int)(implicit p: Parameters) extends LazyModule { - val buf_chain = List.fill(depth)(LazyModule(new TLBuffer(BufferParams.default))) - val node = if (depth > 0) { - (buf_chain.init zip buf_chain.tail) foreach { case (prev, next) => next.node :=? prev.node } - NodeHandle(buf_chain.head.node, buf_chain.last.node) - } else { - TLIdentityNode() - } - lazy val module = new LazyModuleImp(this) { } -} - -object TLBufferChain -{ - def apply(depth: Int)(x: TLOutwardNode)(implicit p: Parameters, sourceInfo: SourceInfo): TLOutwardNode = { - if (depth > 0) { - val buffer = LazyModule(new TLBufferChain(depth)) - buffer.node :=? x - buffer.node - } else { - x - } + def chain(depth: Int, name: Option[String] = None)(implicit p: Parameters): Seq[TLNode] = { + val buffers = Seq.fill(depth) { LazyModule(new TLBuffer()) } + name.foreach { n => buffers.zipWithIndex.foreach { case (b, i) => b.suggestName(s"${n}_${i}") } } + buffers.map(_.node) } } diff --git a/src/main/scala/tilelink/Bus.scala b/src/main/scala/tilelink/Bus.scala index 5f2d018b..b078358b 100644 --- a/src/main/scala/tilelink/Bus.scala +++ b/src/main/scala/tilelink/Bus.scala @@ -68,34 +68,24 @@ abstract class TLBusWrapper(params: TLBusParams, val busName: String)(implicit p protected def inwardNode: TLInwardNode = xbar.node protected def inwardBufNode: TLInwardNode = master_buffer.node - protected def bufferChain(depth: Int, name: Option[String] = None): (TLInwardNode, TLOutwardNode) = { - SourceCardinality { implicit p => - val chain = LazyModule(new TLBufferChain(depth)) - name.foreach { n => chain.suggestName(s"${busName}_${n}_TLBufferChain")} - (chain.node, chain.node) - } - } - def bufferFromMasters: TLInwardNode = inwardBufNode def bufferToSlaves: TLOutwardNode = outwardBufNode def toSyncSlaves(name: Option[String] = None, addBuffers: Int = 0): TLOutwardNode = SinkCardinality { implicit p => - TLBufferChain(addBuffers)(outwardBufNode) + TLBuffer.chain(addBuffers).foldRight(outwardBufNode)(_ :=? _) } def toAsyncSlaves(sync: Int = 3, name: Option[String] = None, addBuffers: Int = 0): TLAsyncOutwardNode = SinkCardinality { implicit p => val source = LazyModule(new TLAsyncCrossingSource(sync)) name.foreach{ n => source.suggestName(s"${busName}_${n}_TLAsyncCrossingSource")} - source.node :=? TLBufferChain(addBuffers)(outwardNode) - source.node + source.node :=? TLBuffer.chain(addBuffers).foldRight(outwardNode)(_ :=? _) } def toRationalSlaves(name: Option[String] = None, addBuffers: Int = 0): TLRationalOutwardNode = SinkCardinality { implicit p => val source = LazyModule(new TLRationalCrossingSource()) name.foreach{ n => source.suggestName(s"${busName}_${n}_TLRationalCrossingSource")} - source.node :=? TLBufferChain(addBuffers)(outwardNode) - source.node + source.node :=? TLBuffer.chain(addBuffers).foldRight(outwardNode)(_ :=? _) } def toVariableWidthSlaves: TLOutwardNode = outwardFragNode diff --git a/src/main/scala/tilelink/Fuzzer.scala b/src/main/scala/tilelink/Fuzzer.scala index c2e6a769..55678fb8 100644 --- a/src/main/scala/tilelink/Fuzzer.scala +++ b/src/main/scala/tilelink/Fuzzer.scala @@ -233,28 +233,16 @@ class TLFuzzRAM(txns: Int)(implicit p: Parameters) extends LazyModule val xbar = LazyModule(new TLXbar) val xbar2= LazyModule(new TLXbar) val fuzz = LazyModule(new TLFuzzer(txns)) - val cross = LazyModule(new TLAsyncCrossing) model.node := fuzz.node xbar2.node := TLAtomicAutomata()(model.node) ram2.node := TLFragmenter(16, 256)(xbar2.node) xbar.node := TLWidthWidget(16)(TLHintHandler()(xbar2.node)) - cross.node := TLFragmenter(4, 256)(TLBuffer()(xbar.node)) - ram.node := cross.node + ram.node := TLFragmenter(4, 256)(TLBuffer()(xbar.node)) gpio.node := TLFragmenter(4, 32)(TLBuffer()(xbar.node)) lazy val module = new LazyModuleImp(this) with UnitTestModule { io.finished := fuzz.module.io.finished - - // Shove the RAM into another clock domain - val clocks = Module(new Pow2ClockDivider(2)) - ram.module.clock := clocks.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 } } diff --git a/src/main/scala/tilelink/IntNodes.scala b/src/main/scala/tilelink/IntNodes.scala deleted file mode 100644 index 2cc6aa61..00000000 --- a/src/main/scala/tilelink/IntNodes.scala +++ /dev/null @@ -1,121 +0,0 @@ -// See LICENSE.SiFive for license details. - -package freechips.rocketchip.tilelink - -import Chisel._ -import chisel3.internal.sourceinfo.SourceInfo -import freechips.rocketchip.config.Parameters -import freechips.rocketchip.diplomacy._ -import freechips.rocketchip.util.SynchronizerShiftReg -import scala.collection.mutable.ListBuffer -import scala.math.max - -// A potentially empty half-open range; [start, end) -case class IntRange(start: Int, end: Int) -{ - require (start >= 0) - require (start <= end) - def size = end - start - def overlaps(x: IntRange) = start < x.end && x.start < end - def offset(x: Int) = IntRange(x+start, x+end) -} - -object IntRange -{ - implicit def apply(end: Int): IntRange = apply(0, end) -} - -case class IntSourceParameters( - range: IntRange, - resources: Seq[Resource] = Seq(), - nodePath: Seq[BaseNode] = Seq()) -{ - val name = nodePath.lastOption.map(_.lazyModule.name).getOrElse("disconnected") -} - -case class IntSinkParameters( - nodePath: Seq[BaseNode] = Seq()) -{ - val name = nodePath.lastOption.map(_.lazyModule.name).getOrElse("disconnected") -} - -case class IntSourcePortParameters(sources: Seq[IntSourceParameters]) -{ - val num = sources.map(_.range.size).sum - // The interrupts mapping must not overlap - sources.map(_.range).combinations(2).foreach { case Seq(a, b) => require (!a.overlaps(b)) } - // The interrupts must perfectly cover the range - require (sources.isEmpty || sources.map(_.range.end).max == num) -} -object IntSourcePortSimple -{ - def apply(num: Int = 1, ports: Int = 1, sources: Int = 1, resources: Seq[Resource] = Nil) = - if (num == 0) Nil else - Seq.fill(ports)(IntSourcePortParameters(Seq.fill(sources)(IntSourceParameters(range = IntRange(0, num), resources = resources)))) -} - -case class IntSinkPortParameters(sinks: Seq[IntSinkParameters]) -object IntSinkPortSimple -{ - def apply(ports: Int = 1, sinks: Int = 1) = - Seq.fill(ports)(IntSinkPortParameters(Seq.fill(sinks)(IntSinkParameters()))) -} - -case class IntEdge(source: IntSourcePortParameters, sink: IntSinkPortParameters, params: Parameters, sourceInfo: SourceInfo) - -object IntImp extends SimpleNodeImp[IntSourcePortParameters, IntSinkPortParameters, IntEdge, Vec[Bool]] -{ - def edge(pd: IntSourcePortParameters, pu: IntSinkPortParameters, p: Parameters, sourceInfo: SourceInfo) = IntEdge(pd, pu, p, sourceInfo) - def bundle(e: IntEdge) = Vec(e.source.num, Bool()) - def render(e: IntEdge) = RenderedEdge(colour = "#0000ff" /* blue */, label = e.source.sources.map(_.range.size).sum.toString, flipped = true) - - override def mixO(pd: IntSourcePortParameters, node: OutwardNode[IntSourcePortParameters, IntSinkPortParameters, Vec[Bool]]): IntSourcePortParameters = - pd.copy(sources = pd.sources.map { s => s.copy (nodePath = node +: s.nodePath) }) - override def mixI(pu: IntSinkPortParameters, node: InwardNode[IntSourcePortParameters, IntSinkPortParameters, Vec[Bool]]): IntSinkPortParameters = - pu.copy(sinks = pu.sinks.map { s => s.copy (nodePath = node +: s.nodePath) }) -} - -case class IntSourceNode(portParams: Seq[IntSourcePortParameters])(implicit valName: ValName) extends SourceNode(IntImp)(portParams) -case class IntSinkNode(portParams: Seq[IntSinkPortParameters])(implicit valName: ValName) extends SinkNode(IntImp)(portParams) -case class IntAdapterNode( - sourceFn: IntSourcePortParameters => IntSourcePortParameters = { s => s }, - sinkFn: IntSinkPortParameters => IntSinkPortParameters = { s => s }, - num: Range.Inclusive = 0 to 999)( - implicit valName: ValName) - extends AdapterNode(IntImp)(sourceFn, sinkFn, num) -case class IntIdentityNode()(implicit valName: ValName) extends IdentityNode(IntImp)() - -case class IntNexusNode( - sourceFn: Seq[IntSourcePortParameters] => IntSourcePortParameters, - sinkFn: Seq[IntSinkPortParameters] => IntSinkPortParameters, - numSourcePorts: Range.Inclusive = 0 to 128, - numSinkPorts: Range.Inclusive = 0 to 128)( - implicit valName: ValName) - extends NexusNode(IntImp)(sourceFn, sinkFn, numSourcePorts, numSinkPorts) - -class IntXbar()(implicit p: Parameters) extends LazyModule -{ - val intnode = IntNexusNode( - sinkFn = { _ => IntSinkPortParameters(Seq(IntSinkParameters())) }, - sourceFn = { seq => - IntSourcePortParameters((seq zip seq.map(_.num).scanLeft(0)(_+_).init).map { - case (s, o) => s.sources.map(z => z.copy(range = z.range.offset(o))) - }.flatten) - }) - - lazy val module = new LazyModuleImp(this) { - val cat = intnode.in.map { case (i, e) => i.take(e.source.num) }.flatten - intnode.out.foreach { case (o, _) => o := cat } - } -} - -class IntXing(sync: Int = 3)(implicit p: Parameters) extends LazyModule -{ - val intnode = IntAdapterNode() - - lazy val module = new LazyModuleImp(this) { - (intnode.in zip intnode.out) foreach { case ((in, _), (out, _)) => - out := SynchronizerShiftReg(in, sync) - } - } -} diff --git a/src/main/scala/tilelink/RationalCrossing.scala b/src/main/scala/tilelink/RationalCrossing.scala index c448774d..a44bc8ce 100644 --- a/src/main/scala/tilelink/RationalCrossing.scala +++ b/src/main/scala/tilelink/RationalCrossing.scala @@ -97,6 +97,7 @@ object TLRationalCrossingSink } } +@deprecated("TLRationalCrossing is fragile. Use TLRationalCrossingSource and TLRationalCrossingSink", "rocket-chip 1.2") class TLRationalCrossing(direction: RationalDirection = Symmetric)(implicit p: Parameters) extends LazyModule { val source = LazyModule(new TLRationalCrossingSource) diff --git a/src/main/scala/tilelink/RegisterRouter.scala b/src/main/scala/tilelink/RegisterRouter.scala index dc49e797..44df9669 100644 --- a/src/main/scala/tilelink/RegisterRouter.scala +++ b/src/main/scala/tilelink/RegisterRouter.scala @@ -6,6 +6,7 @@ import Chisel._ import freechips.rocketchip.config.Parameters import freechips.rocketchip.diplomacy._ import freechips.rocketchip.regmapper._ +import freechips.rocketchip.interrupts._ import freechips.rocketchip.util.HeterogeneousBag import scala.math.{min,max} diff --git a/src/main/scala/tilelink/package.scala b/src/main/scala/tilelink/package.scala index e0a165b7..4dc8cc24 100644 --- a/src/main/scala/tilelink/package.scala +++ b/src/main/scala/tilelink/package.scala @@ -9,13 +9,11 @@ package object tilelink { type TLInwardNode = InwardNodeHandle[TLClientPortParameters, TLManagerPortParameters, TLBundle] type TLOutwardNode = OutwardNodeHandle[TLClientPortParameters, TLManagerPortParameters, TLBundle] + type TLNode = NodeHandle[TLClientPortParameters, TLManagerPortParameters, TLBundle, TLClientPortParameters, TLManagerPortParameters, TLBundle] type TLAsyncInwardNode = InwardNodeHandle[TLAsyncClientPortParameters, TLAsyncManagerPortParameters, TLAsyncBundle] type TLAsyncOutwardNode = OutwardNodeHandle[TLAsyncClientPortParameters, TLAsyncManagerPortParameters, TLAsyncBundle] type TLRationalInwardNode = InwardNodeHandle[TLRationalClientPortParameters, TLRationalManagerPortParameters, TLRationalBundle] type TLRationalOutwardNode = OutwardNodeHandle[TLRationalClientPortParameters, TLRationalManagerPortParameters, TLRationalBundle] type TLMixedNode = MixedNode[TLClientPortParameters, TLManagerPortParameters, TLEdgeIn, TLBundle, TLClientPortParameters, TLManagerPortParameters, TLEdgeOut, TLBundle] - - type IntInwardNode = InwardNodeHandle[IntSourcePortParameters, IntSinkPortParameters, Vec[Bool]] - type IntOutwardNode = OutwardNodeHandle[IntSourcePortParameters, IntSinkPortParameters, Vec[Bool]] } diff --git a/src/main/scala/util/Crossing.scala b/src/main/scala/util/Crossing.scala index f6bab310..7d238ab5 100644 --- a/src/main/scala/util/Crossing.scala +++ b/src/main/scala/util/Crossing.scala @@ -19,118 +19,3 @@ class CrossingIO[T <: Data](gen: T) extends Bundle { abstract class Crossing[T <: Data] extends Module { val io: CrossingIO[T] } - -class AsyncScope extends Module { val io = new Bundle } -object AsyncScope { def apply() = Module(new AsyncScope) } - -object AsyncDecoupledCrossing -{ - // takes from_source from the 'from' clock domain and puts it into the 'to' clock domain - def apply[T <: Data](from_clock: Clock, from_reset: Bool, from_source: ReadyValidIO[T], to_clock: Clock, to_reset: Bool, depth: Int = 8, sync: Int = 3): DecoupledIO[T] = { - val crossing = Module(new AsyncQueue(from_source.bits, depth, sync)).io - crossing.enq_clock := from_clock - crossing.enq_reset := from_reset - crossing.enq <> from_source - crossing.deq_clock := to_clock - crossing.deq_reset := to_reset - crossing.deq - } -} - -object AsyncDecoupledTo -{ - // takes source from your clock domain and puts it into the 'to' clock domain - def apply[T <: Data](to_clock: Clock, to_reset: Bool, source: ReadyValidIO[T], depth: Int = 8, sync: Int = 3): DecoupledIO[T] = { - val scope = AsyncScope() - AsyncDecoupledCrossing(scope.clock, scope.reset, source, to_clock, to_reset, depth, sync) - } -} - -object AsyncDecoupledFrom -{ - // takes from_source from the 'from' clock domain and puts it into your clock domain - def apply[T <: Data](from_clock: Clock, from_reset: Bool, from_source: ReadyValidIO[T], depth: Int = 8, sync: Int = 3): DecoupledIO[T] = { - val scope = AsyncScope() - AsyncDecoupledCrossing(from_clock, from_reset, from_source, scope.clock, scope.reset, depth, sync) - } -} - -object PostQueueIrrevocablize -{ - def apply[T <: Data](deq: DecoupledIO[T]): IrrevocableIO[T] = { - val irr = Wire(new IrrevocableIO(deq.bits)) - irr.bits := deq.bits - irr.valid := deq.valid - deq.ready := irr.ready - irr - } -} - -object AsyncIrrevocableCrossing { - def apply[T <: Data](from_clock: Clock, from_reset: Bool, from_source: ReadyValidIO[T], to_clock: Clock, to_reset: Bool, depth: Int = 8, sync: Int = 3): IrrevocableIO[T] = { - PostQueueIrrevocablize(AsyncDecoupledCrossing(from_clock, from_reset, from_source, to_clock, to_reset, depth, sync)) - } -} - -object AsyncIrrevocableTo -{ - // takes source from your clock domain and puts it into the 'to' clock domain - def apply[T <: Data](to_clock: Clock, to_reset: Bool, source: ReadyValidIO[T], depth: Int = 8, sync: Int = 3): IrrevocableIO[T] = { - PostQueueIrrevocablize(AsyncDecoupledTo(to_clock, to_reset, source, depth, sync)) - } -} - -object AsyncIrrevocableFrom -{ - // takes from_source from the 'from' clock domain and puts it into your clock domain - def apply[T <: Data](from_clock: Clock, from_reset: Bool, from_source: ReadyValidIO[T], depth: Int = 8, sync: Int = 3): IrrevocableIO[T] = { - PostQueueIrrevocablize(AsyncDecoupledFrom(from_clock, from_reset, from_source, depth, sync)) - } -} - -/** - * This helper object synchronizes a level-sensitive signal from one - * clock domain to another. - */ -object LevelSyncCrossing { - class SynchronizerBackend(sync: Int, _clock: Clock) extends Module(Some(_clock)) { - val io = new Bundle { - val in = Bool(INPUT) - val out = Bool(OUTPUT) - } - - io.out := SynchronizerShiftReg(io.in, sync) - } - - class SynchronizerFrontend(_clock: Clock) extends Module(Some(_clock)) { - val io = new Bundle { - val in = Bool(INPUT) - val out = Bool(OUTPUT) - } - - io.out := RegNext(io.in) - } - - def apply(from_clock: Clock, to_clock: Clock, in: Bool, sync: Int = 2): Bool = { - val front = Module(new SynchronizerFrontend(from_clock)) - val back = Module(new SynchronizerBackend(sync, to_clock)) - - front.io.in := in - back.io.in := front.io.out - back.io.out - } -} - -object LevelSyncTo { - def apply(to_clock: Clock, in: Bool, sync: Int = 2): Bool = { - val scope = AsyncScope() - LevelSyncCrossing(scope.clock, to_clock, in, sync) - } -} - -object LevelSyncFrom { - def apply(from_clock: Clock, in: Bool, sync: Int = 2): Bool = { - val scope = AsyncScope() - LevelSyncCrossing(from_clock, scope.clock, in, sync) - } -}