axi4: implement a diplomatic AXI4 clock crossing (#1049)
This commit is contained in:
parent
b64609bfe8
commit
f82e441426
119
src/main/scala/amba/axi4/AsyncCrossing.scala
Normal file
119
src/main/scala/amba/axi4/AsyncCrossing.scala
Normal file
@ -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
|
||||||
|
}
|
@ -4,7 +4,7 @@ package freechips.rocketchip.amba.axi4
|
|||||||
|
|
||||||
import Chisel._
|
import Chisel._
|
||||||
import chisel3.util.Irrevocable
|
import chisel3.util.Irrevocable
|
||||||
import freechips.rocketchip.util.GenericParameterizedBundle
|
import freechips.rocketchip.util._
|
||||||
|
|
||||||
abstract class AXI4BundleBase(params: AXI4BundleParameters) extends GenericParameterizedBundle(params)
|
abstract class AXI4BundleBase(params: AXI4BundleParameters) extends GenericParameterizedBundle(params)
|
||||||
|
|
||||||
@ -76,3 +76,14 @@ object AXI4Bundle
|
|||||||
{
|
{
|
||||||
def apply(params: AXI4BundleParameters) = new AXI4Bundle(params)
|
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
|
||||||
|
}
|
||||||
|
@ -28,3 +28,25 @@ case class AXI4AdapterNode(
|
|||||||
implicit valName: ValName)
|
implicit valName: ValName)
|
||||||
extends AdapterNode(AXI4Imp)(masterFn, slaveFn, numPorts)
|
extends AdapterNode(AXI4Imp)(masterFn, slaveFn, numPorts)
|
||||||
case class AXI4IdentityNode()(implicit valName: ValName) extends IdentityNode(AXI4Imp)()
|
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) })
|
||||||
|
@ -131,3 +131,12 @@ case class AXI4EdgeParameters(
|
|||||||
{
|
{
|
||||||
val bundle = AXI4BundleParameters(master, slave)
|
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))
|
||||||
|
}
|
||||||
|
@ -8,4 +8,5 @@ import freechips.rocketchip.diplomacy.OutwardNodeHandle
|
|||||||
package object axi4
|
package object axi4
|
||||||
{
|
{
|
||||||
type AXI4OutwardNode = OutwardNodeHandle[AXI4MasterPortParameters, AXI4SlavePortParameters, AXI4Bundle]
|
type AXI4OutwardNode = OutwardNodeHandle[AXI4MasterPortParameters, AXI4SlavePortParameters, AXI4Bundle]
|
||||||
|
type AXI4AsyncOutwardNode = OutwardNodeHandle[AXI4AsyncMasterPortParameters, AXI4AsyncSlavePortParameters, AXI4AsyncBundle]
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,8 @@ class WithAMBAUnitTests extends Config((site, here, up) => {
|
|||||||
Module(new APBBridgeTest(false,txns=6*txns, timeout=timeout)),
|
Module(new APBBridgeTest(false,txns=6*txns, timeout=timeout)),
|
||||||
Module(new AXI4LiteFuzzRAMTest(txns=6*txns, timeout=timeout)),
|
Module(new AXI4LiteFuzzRAMTest(txns=6*txns, timeout=timeout)),
|
||||||
Module(new AXI4FullFuzzRAMTest(txns=3*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) => {
|
class WithTLSimpleUnitTests extends Config((site, here, up) => {
|
||||||
|
Loading…
Reference in New Issue
Block a user