From f82e441426bb29ef79bb45a55c8d7b3f3b7636c2 Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Thu, 12 Oct 2017 00:05:45 -0700 Subject: [PATCH] axi4: implement a diplomatic AXI4 clock crossing (#1049) --- src/main/scala/amba/axi4/AsyncCrossing.scala | 119 +++++++++++++++++++ src/main/scala/amba/axi4/Bundles.scala | 13 +- src/main/scala/amba/axi4/Nodes.scala | 22 ++++ src/main/scala/amba/axi4/Parameters.scala | 9 ++ src/main/scala/amba/axi4/package.scala | 1 + src/main/scala/unittest/Configs.scala | 3 +- 6 files changed, 165 insertions(+), 2 deletions(-) create mode 100644 src/main/scala/amba/axi4/AsyncCrossing.scala diff --git a/src/main/scala/amba/axi4/AsyncCrossing.scala b/src/main/scala/amba/axi4/AsyncCrossing.scala new file mode 100644 index 00000000..04b3dc9f --- /dev/null +++ b/src/main/scala/amba/axi4/AsyncCrossing.scala @@ -0,0 +1,119 @@ +// See LICENSE.SiFive for license details. + +package freechips.rocketchip.amba.axi4 + +import Chisel._ +import chisel3.internal.sourceinfo.SourceInfo +import freechips.rocketchip.config.Parameters +import freechips.rocketchip.diplomacy._ +import freechips.rocketchip.tilelink._ +import freechips.rocketchip.util._ + +class AXI4AsyncCrossingSource(sync: Int = 3)(implicit p: Parameters) extends LazyModule +{ + val node = AXI4AsyncSourceNode(sync) + + lazy val module = new LazyModuleImp(this) { + (node.in zip node.out) foreach { case ((in, edgeIn), (out, edgeOut)) => + val depth = edgeOut.slave.depth + + out.ar <> ToAsyncBundle(in.ar, depth, sync) + out.aw <> ToAsyncBundle(in.aw, depth, sync) + out. w <> ToAsyncBundle(in. w, depth, sync) + in .r <> FromAsyncBundle(out.r, sync) + in .b <> FromAsyncBundle(out.b, sync) + } + } +} + +class AXI4AsyncCrossingSink(depth: Int = 8, sync: Int = 3)(implicit p: Parameters) extends LazyModule +{ + val node = AXI4AsyncSinkNode(depth, sync) + + lazy val module = new LazyModuleImp(this) { + (node.in zip node.out) foreach { case ((in, edgeIn), (out, edgeOut)) => + out.ar <> FromAsyncBundle(in.ar, sync) + out.aw <> FromAsyncBundle(in.aw, sync) + out. w <> FromAsyncBundle(in. w, sync) + in .r <> ToAsyncBundle(out.r, depth, sync) + in .b <> ToAsyncBundle(out.b, depth, sync) + } + } +} + +object AXI4AsyncCrossingSource +{ + // applied to the AXI4 source node; y.node := AXI4AsyncCrossingSource()(x.node) + def apply(sync: Int = 3)(x: AXI4OutwardNode)(implicit p: Parameters, sourceInfo: SourceInfo): AXI4AsyncOutwardNode = { + val source = LazyModule(new AXI4AsyncCrossingSource(sync)) + source.node :=? x + source.node + } +} + +object AXI4AsyncCrossingSink +{ + // applied to the AXI4 source node; y.node := AXI4AsyncCrossingSink()(x.node) + def apply(depth: Int = 8, sync: Int = 3)(x: AXI4AsyncOutwardNode)(implicit p: Parameters, sourceInfo: SourceInfo): AXI4OutwardNode = { + val sink = LazyModule(new AXI4AsyncCrossingSink(depth, sync)) + sink.node :=? x + sink.node + } +} + +class AXI4AsyncCrossing(depth: Int = 8, sync: Int = 3)(implicit p: Parameters) extends LazyModule +{ + val source = LazyModule(new AXI4AsyncCrossingSource(sync)) + val sink = LazyModule(new AXI4AsyncCrossingSink(depth, sync)) + val node = NodeHandle(source.node, sink.node) + + sink.node := source.node + + lazy val module = new LazyModuleImp(this) { + val io = IO(new Bundle { + val in_clock = Clock(INPUT) + val in_reset = Bool(INPUT) + val out_clock = Clock(INPUT) + val out_reset = Bool(INPUT) + }) + + source.module.clock := io.in_clock + source.module.reset := io.in_reset + sink.module.clock := io.out_clock + sink.module.reset := io.out_reset + } +} + +/** Synthesizeable unit tests */ +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(beatBytes = 4)) + val cross = LazyModule(new AXI4AsyncCrossing) + + model.node := fuzz.node + toaxi.node := model.node + cross.node := toaxi.node + ram.node := cross.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 + } +} + +class AXI4RAMAsyncCrossingTest(txns: Int = 5000, timeout: Int = 500000)(implicit p: Parameters) extends UnitTest(timeout) { + io.finished := Module(LazyModule(new AXI4RAMAsyncCrossing(txns)).module).io.finished +} diff --git a/src/main/scala/amba/axi4/Bundles.scala b/src/main/scala/amba/axi4/Bundles.scala index d0a8d174..f588e38d 100644 --- a/src/main/scala/amba/axi4/Bundles.scala +++ b/src/main/scala/amba/axi4/Bundles.scala @@ -4,7 +4,7 @@ package freechips.rocketchip.amba.axi4 import Chisel._ import chisel3.util.Irrevocable -import freechips.rocketchip.util.GenericParameterizedBundle +import freechips.rocketchip.util._ abstract class AXI4BundleBase(params: AXI4BundleParameters) extends GenericParameterizedBundle(params) @@ -76,3 +76,14 @@ object AXI4Bundle { def apply(params: AXI4BundleParameters) = new AXI4Bundle(params) } + +class AXI4AsyncBundleBase(params: AXI4AsyncBundleParameters) extends GenericParameterizedBundle(params) + +class AXI4AsyncBundle(params: AXI4AsyncBundleParameters) extends AXI4AsyncBundleBase(params) +{ + val aw = new AsyncBundle(params.depth, new AXI4BundleAW(params.base)) + val w = new AsyncBundle(params.depth, new AXI4BundleW (params.base)) + val b = new AsyncBundle(params.depth, new AXI4BundleB (params.base)).flip + val ar = new AsyncBundle(params.depth, new AXI4BundleAR(params.base)) + val r = new AsyncBundle(params.depth, new AXI4BundleR (params.base)).flip +} diff --git a/src/main/scala/amba/axi4/Nodes.scala b/src/main/scala/amba/axi4/Nodes.scala index 1227f710..0c9f7f86 100644 --- a/src/main/scala/amba/axi4/Nodes.scala +++ b/src/main/scala/amba/axi4/Nodes.scala @@ -28,3 +28,25 @@ case class AXI4AdapterNode( implicit valName: ValName) extends AdapterNode(AXI4Imp)(masterFn, slaveFn, numPorts) case class AXI4IdentityNode()(implicit valName: ValName) extends IdentityNode(AXI4Imp)() + +object AXI4AsyncImp extends SimpleNodeImp[AXI4AsyncMasterPortParameters, AXI4AsyncSlavePortParameters, AXI4AsyncEdgeParameters, AXI4AsyncBundle] +{ + def edge(pd: AXI4AsyncMasterPortParameters, pu: AXI4AsyncSlavePortParameters, p: Parameters, sourceInfo: SourceInfo) = AXI4AsyncEdgeParameters(pd, pu, p, sourceInfo) + def bundle(e: AXI4AsyncEdgeParameters) = new AXI4AsyncBundle(e.bundle) + def render(e: AXI4AsyncEdgeParameters) = RenderedEdge(colour = "#ff0000" /* red */, label = e.slave.depth.toString) + + override def mixO(pd: AXI4AsyncMasterPortParameters, node: OutwardNode[AXI4AsyncMasterPortParameters, AXI4AsyncSlavePortParameters, AXI4AsyncBundle]): AXI4AsyncMasterPortParameters = + pd.copy(base = pd.base.copy(masters = pd.base.masters.map { c => c.copy (nodePath = node +: c.nodePath) })) + override def mixI(pu: AXI4AsyncSlavePortParameters, node: InwardNode[AXI4AsyncMasterPortParameters, AXI4AsyncSlavePortParameters, AXI4AsyncBundle]): AXI4AsyncSlavePortParameters = + pu.copy(base = pu.base.copy(slaves = pu.base.slaves.map { m => m.copy (nodePath = node +: m.nodePath) })) +} + +case class AXI4AsyncSourceNode(sync: Int)(implicit valName: ValName) + extends MixedAdapterNode(AXI4Imp, AXI4AsyncImp)( + dFn = { p => AXI4AsyncMasterPortParameters(p) }, + uFn = { p => p.base.copy(minLatency = sync+1) }) // discard cycles in other clock domain + +case class AXI4AsyncSinkNode(depth: Int, sync: Int)(implicit valName: ValName) + extends MixedAdapterNode(AXI4AsyncImp, AXI4Imp)( + dFn = { p => p.base }, + uFn = { p => AXI4AsyncSlavePortParameters(depth, p) }) diff --git a/src/main/scala/amba/axi4/Parameters.scala b/src/main/scala/amba/axi4/Parameters.scala index c58cc4d1..23ca0a30 100644 --- a/src/main/scala/amba/axi4/Parameters.scala +++ b/src/main/scala/amba/axi4/Parameters.scala @@ -131,3 +131,12 @@ case class AXI4EdgeParameters( { val bundle = AXI4BundleParameters(master, slave) } + +case class AXI4AsyncSlavePortParameters(depth: Int, base: AXI4SlavePortParameters) { require(isPow2(depth)) } +case class AXI4AsyncMasterPortParameters(base: AXI4MasterPortParameters) + +case class AXI4AsyncBundleParameters(depth: Int, base: AXI4BundleParameters) { require (isPow2(depth)) } +case class AXI4AsyncEdgeParameters(master: AXI4AsyncMasterPortParameters, slave: AXI4AsyncSlavePortParameters, params: Parameters, sourceInfo: SourceInfo) +{ + val bundle = AXI4AsyncBundleParameters(slave.depth, AXI4BundleParameters(master.base, slave.base)) +} diff --git a/src/main/scala/amba/axi4/package.scala b/src/main/scala/amba/axi4/package.scala index 75fef97a..af72e46f 100644 --- a/src/main/scala/amba/axi4/package.scala +++ b/src/main/scala/amba/axi4/package.scala @@ -8,4 +8,5 @@ import freechips.rocketchip.diplomacy.OutwardNodeHandle package object axi4 { type AXI4OutwardNode = OutwardNodeHandle[AXI4MasterPortParameters, AXI4SlavePortParameters, AXI4Bundle] + type AXI4AsyncOutwardNode = OutwardNodeHandle[AXI4AsyncMasterPortParameters, AXI4AsyncSlavePortParameters, AXI4AsyncBundle] } diff --git a/src/main/scala/unittest/Configs.scala b/src/main/scala/unittest/Configs.scala index 1af3bb80..da17facc 100644 --- a/src/main/scala/unittest/Configs.scala +++ b/src/main/scala/unittest/Configs.scala @@ -30,7 +30,8 @@ class WithAMBAUnitTests extends Config((site, here, up) => { Module(new APBBridgeTest(false,txns=6*txns, timeout=timeout)), Module(new AXI4LiteFuzzRAMTest(txns=6*txns, timeout=timeout)), Module(new AXI4FullFuzzRAMTest(txns=3*txns, timeout=timeout)), - Module(new AXI4BridgeTest( txns=3*txns, timeout=timeout))) } + Module(new AXI4BridgeTest( txns=3*txns, timeout=timeout)), + Module(new AXI4RAMAsyncCrossingTest(txns=3*txns, timeout=timeout))) } }) class WithTLSimpleUnitTests extends Config((site, here, up) => {