1
0

Merge pull request #279 from ucb-bar/monitor

TL2 Monitor and Fuzzer
This commit is contained in:
Henry Cook 2016-09-13 14:04:23 -07:00 committed by GitHub
commit 28982ab569
13 changed files with 811 additions and 78 deletions

@ -1 +1 @@
Subproject commit 16426b3a68d85ce7dd9655b0ce773431eb69fc74
Subproject commit bb240453abf96b4c2d75ebb2cdc7e3159068431d

View File

@ -4,12 +4,14 @@ import Chisel._
import junctions._
import cde.{Field, Parameters}
abstract class UnitTest extends Module {
trait HasUnitTestIO {
val io = new Bundle {
val finished = Bool(OUTPUT)
val start = Bool(INPUT)
}
}
abstract class UnitTest extends Module with HasUnitTestIO {
when (io.start) {
printf(s"Started UnitTest ${this.getClass.getSimpleName}\n")
}
@ -37,7 +39,7 @@ class UnitTestSuite(implicit p: Parameters) extends Module {
state := Mux(test_idx === UInt(tests.size - 1), s_done, s_start)
}
val timer = Module(new Timer(1000, tests.size))
val timer = Module(new Timer(100000, tests.size))
timer.io.start.valid := Bool(false)
timer.io.stop.valid := Bool(false)

View File

@ -15,14 +15,11 @@ class TLBuffer(entries: Int = 2, pipe: Boolean = false) extends LazyModule
val out = node.bundleOut
}
val in = io.in(0)
val out = io.out(0)
((io.in zip io.out) zip (node.edgesIn zip node.edgesOut)) foreach { case ((in, out), (edgeIn, edgeOut)) =>
out.a <> Queue(in .a, entries, pipe)
in .d <> Queue(out.d, entries, pipe)
val edge = node.edgesOut(0) // same as edgeIn(0)
if (edge.manager.anySupportAcquire && edge.client.anySupportProbe) {
if (edgeOut.manager.anySupportAcquire && edgeOut.client.anySupportProbe) {
in .b <> Queue(out.b, entries, pipe)
out.c <> Queue(in .c, entries, pipe)
out.e <> Queue(out.e, entries, pipe)
@ -36,6 +33,7 @@ class TLBuffer(entries: Int = 2, pipe: Boolean = false) extends LazyModule
}
}
}
}
object TLBuffer
{

View File

@ -47,7 +47,9 @@ class TLEdge(
Cat(helper(lgBytes).map(_._1).reverse)
}
def addr_lo(mask: UInt): UInt = {
// !!! make sure to align addr_lo for PutPartials with 0 masks
def addr_lo(mask: UInt, lgSize: UInt): UInt = {
val sizeOH1 = UIntToOH1(lgSize, log2Up(manager.beatBytes))
// Almost OHToUInt, but bits set => bits not set
def helper(mask: UInt, width: Int): UInt = {
if (width <= 1) {
@ -61,7 +63,11 @@ class TLEdge(
Cat(!lo.orR, helper(hi | lo, mid))
}
}
helper(mask, bundle.dataBits/8)
helper(mask, bundle.dataBits/8) & ~sizeOH1
}
def full_mask(imask: UInt, lgSize: UInt): UInt = {
mask(addr_lo(imask, lgSize), lgSize)
}
def staticHasData(bundle: TLChannel): Option[Boolean] = {
@ -167,13 +173,22 @@ class TLEdge(
def addr_lo(x: TLDataChannel): UInt = {
x match {
case a: TLBundleA => addr_lo(a.mask)
case b: TLBundleB => addr_lo(b.mask)
case a: TLBundleA => addr_lo(a.mask, a.size)
case b: TLBundleB => addr_lo(b.mask, b.size)
case c: TLBundleC => c.addr_lo
case d: TLBundleD => d.addr_lo
}
}
def full_mask(x: TLDataChannel): UInt = {
x match {
case a: TLBundleA => full_mask(a.mask, a.size)
case b: TLBundleB => full_mask(b.mask, b.size)
case c: TLBundleC => mask(c.addr_lo, c.size)
case d: TLBundleD => mask(d.addr_lo, d.size)
}
}
def address(x: TLAddrChannel): UInt = {
val hi = x match {
case a: TLBundleA => a.addr_hi
@ -192,7 +207,17 @@ class TLEdge(
val cutoff = log2Ceil(manager.beatBytes)
val small = if (manager.maxTransfer <= manager.beatBytes) Bool(true) else size <= UInt(cutoff)
val decode = UIntToOH(size, maxLgSize+1) >> cutoff
Mux(!hasData || small, UInt(1), decode)
Mux(hasData, decode | small.asUInt, UInt(1))
}
}
}
def numBeats1(x: TLChannel): UInt = {
x match {
case _: TLBundleE => UInt(0)
case bundle: TLDataChannel => {
val decode = UIntToOH1(size(bundle), maxLgSize) >> log2Ceil(manager.beatBytes)
Mux(hasData(bundle), decode, UInt(0))
}
}
}
@ -353,7 +378,7 @@ class TLEdgeOut(
def Hint(fromSource: UInt, toAddress: UInt, lgSize: UInt, param: UInt) = {
require (manager.anySupportHint)
val legal = manager.supportsHint(toAddress)
val legal = manager.supportsHint(toAddress, lgSize)
val a = Wire(new TLBundleA(bundle))
a.opcode := TLMessages.Hint
a.param := param
@ -546,7 +571,7 @@ class TLEdgeIn(
def Hint(fromAddress: UInt, toSource: UInt, lgSize: UInt, param: UInt) = {
require (client.anySupportHint)
val legal = client.supportsHint(toSource)
val legal = client.supportsHint(toSource, lgSize)
val b = Wire(new TLBundleB(bundle))
b.opcode := TLMessages.Hint
b.param := param
@ -590,7 +615,7 @@ class TLEdgeIn(
d
}
def HintAck(a: TLBundleA, sink: UInt = UInt(0)): TLBundleD = HintAck(address(a), sink, a.source, a.size)
def HintAck(a: TLBundleA, fromSink: UInt): TLBundleD = HintAck(address(a), fromSink, a.source, a.size)
def HintAck(fromAddress: UInt, fromSink: UInt, toSource: UInt, lgSize: UInt) = {
val d = Wire(new TLBundleD(bundle))
d.opcode := TLMessages.HintAck

View File

@ -34,7 +34,8 @@ class TLFragmenter(minSize: Int, maxSize: Int, alwaysMin: Boolean = false) exten
supportsLogical = expandTransfer(m.supportsLogical),
supportsGet = expandTransfer(m.supportsGet),
supportsPutFull = expandTransfer(m.supportsPutFull),
supportsPutPartial = expandTransfer(m.supportsPutPartial))
supportsPutPartial = expandTransfer(m.supportsPutPartial),
supportsHint = expandTransfer(m.supportsHint))
def mapClient(c: TLClientParameters) = c.copy(
sourceId = IdRange(c.sourceId.start << fragmentBits, c.sourceId.end << fragmentBits),
// since we break Acquires, none of these work either:
@ -44,7 +45,7 @@ class TLFragmenter(minSize: Int, maxSize: Int, alwaysMin: Boolean = false) exten
supportsGet = TransferSizes.none,
supportsPutFull = TransferSizes.none,
supportsPutPartial = TransferSizes.none,
supportsHint = false)
supportsHint = TransferSizes.none)
val node = TLAdapterNode(
clientFn = { case Seq(c) => c.copy(clients = c.clients.map(mapClient)) },
@ -163,7 +164,7 @@ class TLFragmenter(minSize: Int, maxSize: Int, alwaysMin: Boolean = false) exten
}
// Swallow up non-data ack fragments
val drop = (out.d.bits.opcode === TLMessages.AccessAck) && (dFragnum =/= UInt(0))
val drop = !dHasData && (dFragnum =/= UInt(0))
out.d.ready := in.d.ready || drop
in.d.valid := out.d.valid && !drop
in.d.bits := out.d.bits // pass most stuff unchanged

View File

@ -0,0 +1,230 @@
// See LICENSE for license details.
package uncore.tilelink2
import Chisel._
import chisel3.util.LFSR16
import junctions.unittests._
class IDMapGenerator(numIds: Int) extends Module {
val w = log2Up(numIds)
val io = new Bundle {
val free = Decoupled(UInt(width = w)).flip
val alloc = Decoupled(UInt(width = w))
}
// True indicates that the id is available
val bitmap = RegInit(Vec.fill(numIds){Bool(true)})
io.free.ready := Bool(true)
assert(!io.free.valid || !bitmap(io.free.bits)) // No double freeing
val mask = bitmap.scanLeft(Bool(false))(_||_).init
val select = mask zip bitmap map { case(m,b) => !m && b }
io.alloc.bits := OHToUInt(select)
io.alloc.valid := bitmap.reduce(_||_)
when (io.alloc.fire()) {
bitmap(io.alloc.bits) := Bool(false)
}
when (io.free.fire()) {
bitmap(io.free.bits) := Bool(true)
}
}
object LFSR64
{
private var counter = 0
private def next: Int = {
counter += 1
counter
}
def apply(increment: Bool = Bool(true), seed: Int = next): UInt =
{
val wide = 64
val lfsr = RegInit(UInt((seed * 0xDEADBEEFCAFEBAB1L) >>> 1, width = wide))
val xor = lfsr(0) ^ lfsr(1) ^ lfsr(3) ^ lfsr(4)
when (increment) { lfsr := Cat(xor, lfsr(wide-1,1)) }
lfsr
}
}
trait HasNoiseMakerIO
{
val io = new Bundle {
val inc = Bool(INPUT)
val random = UInt(OUTPUT)
}
}
class LFSRNoiseMaker(wide: Int) extends Module with HasNoiseMakerIO
{
val lfsrs = Seq.fill((wide+63)/64) { LFSR64(io.inc) }
io.random := Cat(lfsrs)(wide-1,0)
}
object LFSRNoiseMaker {
def apply(wide: Int, increment: Bool = Bool(true)): UInt = {
val nm = Module(new LFSRNoiseMaker(wide))
nm.io.inc := increment
nm.io.random
}
}
/** TLFuzzer drives test traffic over TL2 links. It generates a sequence of randomized
* requests, and issues legal ones into the DUT. TODO: Currently the fuzzer only generates
* memory operations, not permissions transfers.
* @param nOperations is the total number of operations that the fuzzer must complete for the test to pass
* @param inFlight is the number of operations that can be in-flight to the DUT concurrently
* @param noiseMaker is a function that supplies a random UInt of a given width every time inc is true
*/
class TLFuzzer(
nOperations: Int,
inFlight: Int = 32,
noiseMaker: (Int, Bool) => UInt = LFSRNoiseMaker.apply _) extends LazyModule
{
val node = TLClientNode(TLClientParameters(sourceId = IdRange(0,inFlight)))
lazy val module = new LazyModuleImp(this) {
val io = new Bundle {
val out = node.bundleOut
val finished = Bool()
}
val out = io.out(0)
val edge = node.edgesOut(0)
// Extract useful parameters from the TL edge
val endAddress = edge.manager.maxAddress + 1
val maxTransfer = edge.manager.maxTransfer
val beatBytes = edge.manager.beatBytes
val maxLgBeats = log2Up(maxTransfer/beatBytes)
val addressBits = log2Up(endAddress)
val sizeBits = edge.bundle.sizeBits
val dataBits = edge.bundle.dataBits
// Progress through operations
val num_reqs = Reg(init = UInt(nOperations-1, log2Up(nOperations)))
val num_resps = Reg(init = UInt(nOperations-1, log2Up(nOperations)))
io.finished := num_resps === UInt(0)
// Progress within each operation
val a = out.a.bits
val a_beats1 = edge.numBeats1(a)
val a_counter = RegInit(UInt(0, width = maxLgBeats))
val a_counter1 = a_counter - UInt(1)
val a_first = a_counter === UInt(0)
val a_last = a_counter === UInt(1) || a_beats1 === UInt(0)
val req_done = out.a.fire() && a_last
val d = out.d.bits
val d_beats1 = edge.numBeats1(d)
val d_counter = RegInit(UInt(0, width = maxLgBeats))
val d_counter1 = d_counter - UInt(1)
val d_first = d_counter === UInt(0)
val d_last = d_counter === UInt(1) || d_beats1 === UInt(0)
val resp_done = out.d.fire() && d_last
// Source ID generation
val idMap = Module(new IDMapGenerator(inFlight))
val alloc = Queue.irrevocable(idMap.io.alloc, 1, pipe = true)
val src = alloc.bits
alloc.ready := req_done
idMap.io.free.valid := resp_done
idMap.io.free.bits := out.d.bits.source
// Increment random number generation for the following subfields
val inc = Wire(Bool())
val inc_beat = Wire(Bool())
val arth_op = noiseMaker(3, inc)
val log_op = noiseMaker(2, inc)
val amo_size = UInt(2) + noiseMaker(1, inc) // word or dword
val size = noiseMaker(sizeBits, inc)
val addr = noiseMaker(addressBits, inc) & ~UIntToOH1(size, addressBits)
val mask = noiseMaker(beatBytes, inc_beat) & edge.mask(addr, size)
val data = noiseMaker(dataBits, inc_beat)
// Actually generate specific TL messages when it is legal to do so
val (glegal, gbits) = edge.Get(src, addr, size)
val (pflegal, pfbits) = if(edge.manager.anySupportPutFull) {
edge.Put(src, addr, size, data)
} else { (glegal, gbits) }
val (pplegal, ppbits) = if(edge.manager.anySupportPutPartial) {
edge.Put(src, addr, size, data, mask)
} else { (glegal, gbits) }
val (alegal, abits) = if(edge.manager.anySupportArithmetic) {
edge.Arithmetic(src, addr, size, data, arth_op)
} else { (glegal, gbits) }
val (llegal, lbits) = if(edge.manager.anySupportLogical) {
edge.Logical(src, addr, size, data, log_op)
} else { (glegal, gbits) }
val (hlegal, hbits) = if(edge.manager.anySupportHint) {
edge.Hint(src, addr, size, UInt(0))
} else { (glegal, gbits) }
// Pick a specific message to try to send
val a_type_sel = noiseMaker(3, inc)
val legal = MuxLookup(a_type_sel, glegal, Seq(
UInt("b000") -> glegal,
UInt("b001") -> pflegal,
UInt("b010") -> pplegal,
UInt("b011") -> alegal,
UInt("b100") -> llegal,
UInt("b101") -> hlegal))
val bits = MuxLookup(a_type_sel, gbits, Seq(
UInt("b000") -> gbits,
UInt("b001") -> pfbits,
UInt("b010") -> ppbits,
UInt("b011") -> abits,
UInt("b100") -> lbits,
UInt("b101") -> hbits))
// Wire both the used and un-used channel signals
out.a.valid := legal && alloc.valid && num_reqs =/= UInt(0)
out.a.bits := bits
out.b.ready := Bool(true)
out.c.valid := Bool(false)
out.d.ready := Bool(true)
out.e.valid := Bool(false)
// Increment the various progress-tracking states
inc := !legal || req_done
inc_beat := !legal || out.a.fire()
when (out.a.fire()) {
a_counter := Mux(a_first, a_beats1, a_counter1)
when(a_last) { num_reqs := num_reqs - UInt(1) }
}
when (out.d.fire()) {
d_counter := Mux(d_first, d_beats1, d_counter1)
when(d_last) { num_resps := num_resps - UInt(1) }
}
}
}
class TLFuzzRAM extends LazyModule
{
val model = LazyModule(new TLRAMModel)
val ram = LazyModule(new TLRAM(AddressSet(0, 0x3ff)))
val gpio = LazyModule(new RRTest1(0x400))
val xbar = LazyModule(new TLXbar)
val fuzz = LazyModule(new TLFuzzer(5000))
model.node := fuzz.node
xbar.node := TLWidthWidget(TLHintHandler(model.node), 16)
ram.node := TLFragmenter(TLBuffer(xbar.node), 4, 256)
gpio.node := TLFragmenter(TLBuffer(xbar.node), 4, 32)
lazy val module = new LazyModuleImp(this) with HasUnitTestIO {
io.finished := fuzz.module.io.finished
}
}
class TLFuzzRAMTest extends UnitTest {
val dut = Module(LazyModule(new TLFuzzRAM).module)
io.finished := dut.io.finished
}

View File

@ -9,8 +9,8 @@ import chisel3.internal.sourceinfo.SourceInfo
class TLHintHandler(supportManagers: Boolean = true, supportClients: Boolean = false, passthrough: Boolean = true) extends LazyModule
{
val node = TLAdapterNode(
clientFn = { case Seq(c) => if (supportClients) c.copy(clients = c.clients .map(_.copy(supportsHint = true))) else c },
managerFn = { case Seq(m) => if (supportManagers) m.copy(managers = m.managers.map(_.copy(supportsHint = true))) else m })
clientFn = { case Seq(c) => if (!supportClients) c else c.copy(clients = c.clients .map(_.copy(supportsHint = TransferSizes(1, c.maxTransfer)))) },
managerFn = { case Seq(m) => if (!supportManagers) m else m.copy(managers = m.managers.map(_.copy(supportsHint = TransferSizes(1, m.maxTransfer)))) })
lazy val module = new LazyModuleImp(this) {
val io = new Bundle {
@ -27,16 +27,26 @@ class TLHintHandler(supportManagers: Boolean = true, supportClients: Boolean = f
val bce = edgeOut.manager.anySupportAcquire && edgeIn.client.anySupportProbe
require (!supportClients || bce)
if (supportManagers) {
val handleA = if (passthrough) !edgeOut.manager.supportsHint(edgeIn.address(in.a.bits)) else Bool(true)
// Does it even make sense to add the HintHandler?
val smartClients = edgeIn.client.clients.map(_.supportsHint.max == edgeIn.client.maxTransfer).reduce(_&&_)
val smartManagers = edgeOut.manager.managers.map(_.supportsHint.max == edgeOut.manager.maxTransfer).reduce(_&&_)
if (supportManagers && !smartManagers) {
val address = edgeIn.address(in.a.bits)
val handleA = if (passthrough) !edgeOut.manager.supportsHint(address, edgeIn.size(in.a.bits)) else Bool(true)
val bypassD = handleA && in.a.bits.opcode === TLMessages.Hint
// Prioritize existing D traffic over HintAck
in.d.valid := out.d.valid || (bypassD && in.a.valid)
out.d.ready := in.d.ready
in.d.bits := Mux(out.d.valid, out.d.bits, edgeIn.HintAck(in.a.bits))
// Prioritize existing D traffic over HintAck (and finish multibeat xfers)
val beats1 = edgeOut.numBeats1(out.d.bits)
val counter = RegInit(UInt(0, width = log2Up(edgeOut.manager.maxTransfer/edgeOut.manager.beatBytes)))
val first = counter === UInt(0)
when (out.d.fire()) { counter := Mux(first, beats1, counter - UInt(1)) }
in.a.ready := Mux(bypassD, in.d.ready && !out.d.valid, out.a.ready)
in.d.valid := out.d.valid || (bypassD && in.a.valid && first)
out.d.ready := in.d.ready
in.d.bits := Mux(out.d.valid, out.d.bits, edgeIn.HintAck(in.a.bits, edgeOut.manager.findId(address)))
in.a.ready := Mux(bypassD, in.d.ready && first && !out.d.valid, out.a.ready)
out.a.valid := in.a.valid && !bypassD
out.a.bits := in.a.bits
} else {
@ -49,16 +59,21 @@ class TLHintHandler(supportManagers: Boolean = true, supportClients: Boolean = f
in.d.bits := out.d.bits
}
if (supportClients) {
val handleB = if (passthrough) !edgeIn.client.supportsHint(out.b.bits.source) else Bool(true)
if (supportClients && !smartClients) {
val handleB = if (passthrough) !edgeIn.client.supportsHint(out.b.bits.source, edgeOut.size(out.b.bits)) else Bool(true)
val bypassC = handleB && out.b.bits.opcode === TLMessages.Hint
// Prioritize existing C traffic over HintAck
out.c.valid := in.c.valid || (bypassC && in.b.valid)
// Prioritize existing C traffic over HintAck (and finish multibeat xfers)
val beats1 = edgeIn.numBeats1(in.c.bits)
val counter = RegInit(UInt(0, width = log2Up(edgeIn.client.maxTransfer/edgeIn.manager.beatBytes)))
val first = counter === UInt(0)
when (in.c.fire()) { counter := Mux(first, beats1, counter - UInt(1)) }
out.c.valid := in.c.valid || (bypassC && in.b.valid && first)
in.c.ready := out.c.ready
out.c.bits := Mux(in.c.valid, in.c.bits, edgeOut.HintAck(out.b.bits))
out.b.ready := Mux(bypassC, out.c.ready && !in.c.valid, in.b.ready)
out.b.ready := Mux(bypassC, out.c.ready && first && !in.c.valid, in.b.ready)
in.b.valid := out.b.valid && !bypassC
in.b.bits := out.b.bits
} else if (bce) {

View File

@ -20,7 +20,7 @@ object TLMonitor
// Reuse these subexpressions to save some firrtl lines
val source_ok = edge.client.contains(bundle.source)
val is_aligned = edge.isHiAligned(bundle.addr_hi, bundle.size)
val mask = edge.mask(edge.addr_lo(bundle.mask), bundle.size)
val mask = edge.full_mask(bundle)
when (bundle.opcode === TLMessages.Acquire) {
assert (edge.manager.supportsAcquire(edge.address(bundle), bundle.size), "'A' channel carries Acquire type unsupported by manager" + extra)
@ -53,7 +53,6 @@ object TLMonitor
assert (is_aligned, "'A' channel PutPartial address not aligned to size" + extra)
assert (bundle.param === UInt(0), "'A' channel PutPartial carries invalid param" + extra)
assert ((bundle.mask & ~mask) === UInt(0), "'A' channel PutPartial contains invalid mask" + extra)
assert (bundle.mask =/= UInt(0), "'A' channel PutPartial has a zero mask" + extra)
}
when (bundle.opcode === TLMessages.ArithmeticData) {
@ -73,7 +72,7 @@ object TLMonitor
}
when (bundle.opcode === TLMessages.Hint) {
assert (edge.manager.supportsHint(edge.address(bundle)), "'A' channel carries Hint type unsupported by manager" + extra)
assert (edge.manager.supportsHint(edge.address(bundle), bundle.size), "'A' channel carries Hint type unsupported by manager" + extra)
assert (source_ok, "'A' channel Hint carries invalid source ID" + extra)
assert (is_aligned, "'A' channel Hint address not aligned to size" + extra)
assert (bundle.mask === mask, "'A' channel Hint contains invalid mask" + extra)
@ -86,7 +85,7 @@ object TLMonitor
// Reuse these subexpressions to save some firrtl lines
val address_ok = edge.manager.contains(bundle.source)
val is_aligned = edge.isHiAligned(bundle.addr_hi, bundle.size)
val mask = edge.mask(edge.addr_lo(bundle.mask), bundle.size)
val mask = edge.full_mask(bundle)
when (bundle.opcode === TLMessages.Probe) {
assert (edge.client.supportsProbe(bundle.source, bundle.size), "'B' channel carries Probe type unsupported by client" + extra)
@ -119,7 +118,6 @@ object TLMonitor
assert (is_aligned, "'B' channel PutPartial address not aligned to size" + extra)
assert (bundle.param === UInt(0), "'B' channel PutPartial carries invalid param" + extra)
assert ((bundle.mask & ~mask) === UInt(0), "'B' channel PutPartial contains invalid mask" + extra)
assert (bundle.mask =/= UInt(0), "'B' channel PutPartial has a zero mask" + extra)
}
when (bundle.opcode === TLMessages.ArithmeticData) {
@ -139,7 +137,7 @@ object TLMonitor
}
when (bundle.opcode === TLMessages.Hint) {
assert (edge.client.supportsHint(bundle.source), "'B' channel carries Hint type unsupported by client" + extra)
assert (edge.client.supportsHint(bundle.source, bundle.size), "'B' channel carries Hint type unsupported by client" + extra)
assert (address_ok, "'B' channel Hint carries unmanaged address" + extra)
assert (is_aligned, "'B' channel Hint address not aligned to size" + extra)
assert (bundle.mask === mask, "'B' channel Hint contains invalid mask" + extra)

View File

@ -110,16 +110,14 @@ case class TLManagerParameters(
supportsGet: TransferSizes = TransferSizes.none,
supportsPutFull: TransferSizes = TransferSizes.none,
supportsPutPartial: TransferSizes = TransferSizes.none,
supportsHint: Boolean = false,
supportsHint: TransferSizes = TransferSizes.none,
// If fifoId=Some, all accesses sent to the same fifoId are executed and ACK'd in FIFO order
fifoId: Option[Int] = None)
{
address.combinations(2).foreach({ case Seq(x,y) =>
require (!x.overlaps(y))
})
address.foreach({ case a =>
require (supportsAcquire.none || a.alignment1 >= supportsAcquire.max-1)
})
require (supportsPutFull.contains(supportsPutPartial))
// Largest support transfer of all types
val maxTransfer = List(
@ -161,7 +159,7 @@ case class TLManagerPortParameters(managers: Seq[TLManagerParameters], beatBytes
val allSupportGet = managers.map(_.supportsGet) .reduce(_ intersect _)
val allSupportPutFull = managers.map(_.supportsPutFull) .reduce(_ intersect _)
val allSupportPutPartial = managers.map(_.supportsPutPartial).reduce(_ intersect _)
val allSupportHint = managers.map(_.supportsHint) .reduce(_ && _)
val allSupportHint = managers.map(_.supportsHint) .reduce(_ intersect _)
// Operation supported by at least one outward Managers
val anySupportAcquire = managers.map(!_.supportsAcquire.none) .reduce(_ || _)
@ -170,7 +168,7 @@ case class TLManagerPortParameters(managers: Seq[TLManagerParameters], beatBytes
val anySupportGet = managers.map(!_.supportsGet.none) .reduce(_ || _)
val anySupportPutFull = managers.map(!_.supportsPutFull.none) .reduce(_ || _)
val anySupportPutPartial = managers.map(!_.supportsPutPartial.none).reduce(_ || _)
val anySupportHint = managers.map( _.supportsHint) .reduce(_ || _)
val anySupportHint = managers.map(!_.supportsHint.none) .reduce(_ || _)
// These return Option[TLManagerParameters] for your convenience
def find(address: BigInt) = managers.find(_.address.exists(_.contains(address)))
@ -179,6 +177,7 @@ case class TLManagerPortParameters(managers: Seq[TLManagerParameters], beatBytes
// Synthesizable lookup methods
def find(address: UInt) = Vec(managers.map(_.address.map(_.contains(address)).reduce(_ || _)))
def findById(id: UInt) = Vec(managers.map(_.sinkId.contains(id)))
def findId(address: UInt) = Mux1H(find(address), managers.map(m => UInt(m.sinkId.start)))
// !!! need a cheaper version of find, where we assume a valid address match exists
@ -200,11 +199,7 @@ case class TLManagerPortParameters(managers: Seq[TLManagerParameters], beatBytes
val supportsGet = safety_helper(_.supportsGet) _
val supportsPutFull = safety_helper(_.supportsPutFull) _
val supportsPutPartial = safety_helper(_.supportsPutPartial) _
def supportsHint(address: UInt) = {
if (allSupportHint) Bool(true) else {
Mux1H(find(address), managers.map(m => Bool(m.supportsHint)))
}
}
val supportsHint = safety_helper(_.supportsHint) _
}
case class TLClientParameters(
@ -216,8 +211,10 @@ case class TLClientParameters(
supportsGet: TransferSizes = TransferSizes.none,
supportsPutFull: TransferSizes = TransferSizes.none,
supportsPutPartial: TransferSizes = TransferSizes.none,
supportsHint: Boolean = false)
supportsHint: TransferSizes = TransferSizes.none)
{
require (supportsPutFull.contains(supportsPutPartial))
val maxTransfer = List(
supportsProbe.max,
supportsArithmetic.max,
@ -246,7 +243,7 @@ case class TLClientPortParameters(clients: Seq[TLClientParameters]) {
val allSupportGet = clients.map(_.supportsGet) .reduce(_ intersect _)
val allSupportPutFull = clients.map(_.supportsPutFull) .reduce(_ intersect _)
val allSupportPutPartial = clients.map(_.supportsPutPartial).reduce(_ intersect _)
val allSupportHint = clients.map(_.supportsHint) .reduce(_ && _)
val allSupportHint = clients.map(_.supportsHint) .reduce(_ intersect _)
// Operation is supported by at least one client
val anySupportProbe = clients.map(!_.supportsProbe.none) .reduce(_ || _)
@ -255,7 +252,7 @@ case class TLClientPortParameters(clients: Seq[TLClientParameters]) {
val anySupportGet = clients.map(!_.supportsGet.none) .reduce(_ || _)
val anySupportPutFull = clients.map(!_.supportsPutFull.none) .reduce(_ || _)
val anySupportPutPartial = clients.map(!_.supportsPutPartial.none).reduce(_ || _)
val anySupportHint = clients.map( _.supportsHint) .reduce(_ || _)
val anySupportHint = clients.map(!_.supportsHint.none) .reduce(_ || _)
// These return Option[TLClientParameters] for your convenience
def find(id: Int) = clients.find(_.sourceId.contains(id))
@ -278,11 +275,7 @@ case class TLClientPortParameters(clients: Seq[TLClientParameters]) {
val supportsGet = safety_helper(_.supportsGet) _
val supportsPutFull = safety_helper(_.supportsPutFull) _
val supportsPutPartial = safety_helper(_.supportsPutPartial) _
def supportsHint(id: UInt) = {
if (allSupportHint) Bool(true) else {
Mux1H(find(id), clients.map(c => Bool(c.supportsHint)))
}
}
val supportsHint = safety_helper(_.supportsHint) _
}
case class TLBundleParameters(
@ -322,7 +315,7 @@ case class TLEdgeParameters(
require (maxTransfer >= manager.beatBytes)
val bundle = TLBundleParameters(
addrHiBits = log2Up(manager.maxAddress + 1) - log2Up(manager.beatBytes),
addrHiBits = log2Up(manager.maxAddress + 1) - log2Ceil(manager.beatBytes),
dataBits = manager.beatBytes * 8,
sourceBits = log2Up(client.endSourceId),
sinkBits = log2Up(manager.endSinkId),

View File

@ -0,0 +1,249 @@
// See LICENSE for license details.
package uncore.tilelink2
import Chisel._
import chisel3.util.LFSR16
class TLRAMModel extends LazyModule
{
val node = TLIdentityNode()
lazy val module = new LazyModuleImp(this) {
val io = new Bundle {
val in = node.bundleIn
val out = node.bundleOut
}
// !!! support multiple clients via clock division
require (io.out.size == 1)
val in = io.in(0)
val out = io.out(0)
val edge = node.edgesIn(0)
val endAddress = edge.manager.maxAddress + 1
val endSourceId = edge.client.endSourceId
val maxTransfer = edge.manager.maxTransfer
val beatBytes = edge.manager.beatBytes
val endAddressHi = (endAddress / beatBytes).intValue
val maxLgBeats = log2Up(maxTransfer/beatBytes)
val shift = log2Ceil(beatBytes)
val decTrees = log2Ceil(maxTransfer/beatBytes)
val addressBits = log2Up(endAddress)
val countBits = log2Up(endSourceId)
val sizeBits = edge.bundle.sizeBits
// Reset control logic
val wipeIndex = RegInit(UInt(0, width = log2Ceil(endAddressHi) + 1))
val wipe = !wipeIndex(log2Ceil(endAddressHi))
wipeIndex := wipeIndex + wipe.asUInt
// Block traffic while wiping Mems
in.a.ready := out.a.ready && !wipe
out.a.valid := in.a.valid && !wipe
out.a.bits := in.a.bits
out.d.ready := in.d.ready && !wipe
in.d.valid := out.d.valid && !wipe
in.d.bits := out.d.bits
// BCE unsupported
in.b.valid := Bool(false)
out.c.valid := Bool(false)
out.e.valid := Bool(false)
out.b.ready := Bool(true)
in.c.ready := Bool(true)
in.e.ready := Bool(true)
class ByteMonitor extends Bundle {
val valid = Bool()
val value = UInt(width = 8)
}
class FlightMonitor extends Bundle {
val base = UInt(width = addressBits)
val size = UInt(width = sizeBits)
val opcode = UInt(width = 3)
}
// Infer as simple dual port BRAM/M10k with write-first/new-data semantics (bypass needed)
val shadow = Seq.fill(beatBytes) { Mem(endAddressHi, new ByteMonitor) }
val inc_bytes = Seq.fill(beatBytes) { Mem(endAddressHi, UInt(width = countBits)) }
val dec_bytes = Seq.fill(beatBytes) { Mem(endAddressHi, UInt(width = countBits)) }
val inc_trees = Seq.tabulate(decTrees) { i => Mem(endAddressHi >> (i+1), UInt(width = countBits)) }
val dec_trees = Seq.tabulate(decTrees) { i => Mem(endAddressHi >> (i+1), UInt(width = countBits)) }
val shadow_wen = Wire(init = Fill(beatBytes, wipe))
val inc_bytes_wen = Wire(init = Fill(beatBytes, wipe))
val dec_bytes_wen = Wire(init = Fill(beatBytes, wipe))
val inc_trees_wen = Wire(init = Fill(decTrees, wipe))
val dec_trees_wen = Wire(init = Fill(decTrees, wipe))
// This requires either distributed memory or registers (no register on either input or output)
val flight = Mem(endSourceId, new FlightMonitor)
// We want to cross flight data from A to D in the same cycle (for combinational TL2 devices)
val a_flight = Wire(new FlightMonitor)
a_flight.base := edge.address(in.a.bits)
a_flight.size := edge.size(in.a.bits)
a_flight.opcode := in.a.bits.opcode
flight.write(in.a.bits.source, a_flight)
val bypass = in.a.valid && in.a.bits.source === out.d.bits.source
val d_flight = RegNext(Mux(bypass, a_flight, flight.read(out.d.bits.source)))
// Process A access requests
val a = Reg(next = in.a.bits)
val a_fire = Reg(next = in.a.fire(), init = Bool(false))
val a_beats1 = edge.numBeats1(a)
val a_size = edge.size(a)
val a_sizeOH = UIntToOH(a_size)
val a_counter = RegInit(UInt(0, width = maxLgBeats))
val a_counter1 = a_counter - UInt(1)
val a_first = a_counter === UInt(0)
val a_addr_hi = a.addr_hi | (a_beats1 & ~a_counter1)
val a_base = edge.address(a)
val a_mask = edge.mask(a_base, a_size)
// Grab the concurrency state we need
val a_inc_bytes = inc_bytes.map(_.read(a_addr_hi))
val a_dec_bytes = dec_bytes.map(_.read(a_addr_hi))
val a_inc_trees = inc_trees.zipWithIndex.map{ case (m, i) => m.read(a_addr_hi >> (i+1)) }
val a_dec_trees = dec_trees.zipWithIndex.map{ case (m, i) => m.read(a_addr_hi >> (i+1)) }
val a_inc_tree = a_inc_trees.fold(UInt(0))(_ + _)
val a_dec_tree = a_dec_trees.fold(UInt(0))(_ + _)
val a_inc = a_inc_bytes.map(_ + a_inc_tree)
val a_dec = a_dec_bytes.map(_ + a_dec_tree)
when (a_fire) {
// Record the request so we can handle it's response
a_counter := Mux(a_first, a_beats1, a_counter1)
// !!! atomics
assert (a.opcode =/= TLMessages.Acquire)
// Increase the per-byte flight counter for the whole transaction
when (a_first && a.opcode =/= TLMessages.Hint) {
when (a_size <= UInt(shift)) {
inc_bytes_wen := a_mask
}
inc_trees_wen := a_sizeOH >> (shift+1)
}
when (a.opcode === TLMessages.PutFullData || a.opcode === TLMessages.PutPartialData) {
shadow_wen := a.mask
for (i <- 0 until beatBytes) {
val busy = a_inc(i) - a_dec(i) - (!a_first).asUInt
val byte = a.data(8*(i+1)-1, 8*i)
when (a.mask(i)) {
printf("P 0x%x := 0x%x #%d\n", a_addr_hi << shift | UInt(i), byte, busy)
}
}
}
}
val a_waddr = Mux(wipe, wipeIndex, a_addr_hi)
for (i <- 0 until beatBytes) {
val data = Wire(new ByteMonitor)
val busy = a_inc(i) - a_dec(i) - (!a_first).asUInt
data.valid := Mux(wipe, Bool(false), busy === UInt(0))
data.value := a.data(8*(i+1)-1, 8*i)
when (shadow_wen(i)) {
shadow(i).write(a_waddr, data)
}
}
for (i <- 0 until beatBytes) {
val data = Mux(wipe, UInt(0), a_inc_bytes(i) + UInt(1))
when (inc_bytes_wen(i)) {
inc_bytes(i).write(a_waddr, data)
}
}
for (i <- 0 until inc_trees.size) {
val data = Mux(wipe, UInt(0), a_inc_trees(i) + UInt(1))
when (inc_trees_wen(i)) {
inc_trees(i).write(a_waddr >> (i+1), data)
}
}
// Process D access responses
val d = RegNext(out.d.bits)
val d_fire = Reg(next = out.d.fire(), init = Bool(false))
val d_bypass = a_fire && d.source === a.source
val d_beats1 = edge.numBeats1(d)
val d_size = edge.size(d)
val d_sizeOH = UIntToOH(d_size)
val d_counter = RegInit(UInt(0, width = maxLgBeats))
val d_counter1 = d_counter - UInt(1)
val d_first = d_counter === UInt(0)
val d_last = d_counter === UInt(1) || d_beats1 === UInt(0)
val d_base = d_flight.base
val d_addr_hi = d_base >> shift | (d_beats1 & ~d_counter1)
val d_mask = edge.mask(d_base, d_size)
// Grab the concurrency state we need
val d_inc_bytes = inc_bytes.map(_.read(d_addr_hi))
val d_dec_bytes = dec_bytes.map(_.read(d_addr_hi))
val d_inc_trees = inc_trees.zipWithIndex.map{ case (m, i) => m.read(d_addr_hi >> (i+1)) }
val d_dec_trees = dec_trees.zipWithIndex.map{ case (m, i) => m.read(d_addr_hi >> (i+1)) }
val d_inc_tree = d_inc_trees.fold(UInt(0))(_ + _)
val d_dec_tree = d_dec_trees.fold(UInt(0))(_ + _)
val d_inc = d_inc_bytes.map(_ + d_inc_tree)
val d_dec = d_dec_bytes.map(_ + d_dec_tree)
val d_shadow = shadow.map(_.read(d_addr_hi))
when (d_fire) {
assert (d_size === d_flight.size)
d_counter := Mux(d_first, d_beats1, d_counter1)
when (d_flight.opcode === TLMessages.Hint) {
assert (d.opcode === TLMessages.HintAck)
}
// Decreaes the per-byte flight counter for the whole transaction
when (d_last && d_flight.opcode =/= TLMessages.Hint) {
when (d_size <= UInt(shift)) {
dec_bytes_wen := d_mask
}
dec_trees_wen := d_sizeOH >> (shift+1)
}
when (d_flight.opcode === TLMessages.PutFullData || d_flight.opcode === TLMessages.PutPartialData) {
assert (d.opcode === TLMessages.AccessAck)
}
// !!! atomics
when (d_flight.opcode === TLMessages.Get) {
assert (d.opcode === TLMessages.AccessAckData)
for (i <- 0 until beatBytes) {
val got = d.data(8*(i+1)-1, 8*i)
val shadow = Wire(init = d_shadow(i))
when (d_mask(i)) {
when (!shadow.valid) {
printf("G 0x%x := undefined\n", d_addr_hi << shift | UInt(i))
} .otherwise {
printf("G 0x%x := 0x%x\n", d_addr_hi << shift | UInt(i), got)
assert (shadow.value === got)
}
}
}
}
}
val d_waddr = Mux(wipe, wipeIndex, d_addr_hi)
for (i <- 0 until beatBytes) {
val data = Mux(wipe, UInt(0), d_dec_bytes(i) + UInt(1))
when (dec_bytes_wen(i)) {
dec_bytes(i).write(d_waddr, data)
}
}
for (i <- 0 until dec_trees.size) {
val data = Mux(wipe, UInt(0), d_dec_trees(i) + UInt(1))
when (dec_trees_wen(i)) {
dec_trees(i).write(d_waddr >> (i+1), data)
}
}
}
}

View File

@ -105,9 +105,8 @@ class TLRegisterRouter[B <: TLRegBundleBase, M <: LazyModuleImp]
(moduleBuilder: (=> B, TLRegisterRouterBase) => M)
extends TLRegisterRouterBase(AddressSet(base, size-1), interrupts, concurrency, beatBytes)
{
require (size % 4096 == 0) // devices should be 4K aligned
require (isPow2(size))
require (size >= 4096)
// require (size >= 4096) ... not absolutely required, but highly recommended
lazy val module = moduleBuilder(bundleBuilder(TLRegBundleArg(intnode.bundleOut, node.bundleIn)), this)
}

View File

@ -0,0 +1,222 @@
// See LICENSE for license details.
package uncore.tilelink2
import Chisel._
object LFSR16Seed
{
def apply(seed: Int): UInt =
{
val width = 16
val lfsr = Reg(init=UInt((seed*0x7231) % 65536, width))
lfsr := Cat(lfsr(0)^lfsr(2)^lfsr(3)^lfsr(5), lfsr(width-1,1))
lfsr
}
}
class RRTestCombinational(val bits: Int, rvalid: Bool => Bool, wready: Bool => Bool) extends Module
{
val io = new Bundle {
val rvalid = Bool(OUTPUT)
val rready = Bool(INPUT)
val rdata = UInt(OUTPUT, width = bits)
val wvalid = Bool(INPUT)
val wready = Bool(OUTPUT)
val wdata = UInt(INPUT, width = bits)
}
val rfire = io.rvalid && io.rready
val wfire = io.wvalid && io.wready
val reg = Reg(UInt(width = bits))
io.rvalid := rvalid(rfire)
io.wready := wready(wfire)
io.rdata := Mux(rfire, reg, UInt(0))
when (wfire) { reg := io.wdata }
}
object RRTestCombinational
{
private var seed = 42
def always: Bool => Bool = _ => Bool(true)
def random: Bool => Bool = {
seed = seed + 1
_ => LFSR16Seed(seed)(0)
}
def delay(x: Int): Bool => Bool = { fire =>
val reg = RegInit(UInt(0, width = log2Ceil(x+1)))
val ready = reg === UInt(0)
reg := Mux(fire, UInt(x), Mux(ready, UInt(0), reg - UInt(1)))
ready
}
def combo(bits: Int, rvalid: Bool => Bool, wready: Bool => Bool): RegField = {
val combo = Module(new RRTestCombinational(bits, rvalid, wready))
RegField(bits,
RegReadFn { ready => combo.io.rready := ready; (combo.io.rvalid, combo.io.rdata) },
RegWriteFn { (valid, data) => combo.io.wvalid := valid; combo.io.wdata := data; combo.io.wready })
}
}
class RRTestRequest(val bits: Int,
rflow: (Bool, Bool, UInt) => (Bool, Bool, UInt),
wflow: (Bool, Bool, UInt) => (Bool, Bool, UInt)) extends Module
{
val io = new Bundle {
val rivalid = Bool(INPUT)
val riready = Bool(OUTPUT)
val rovalid = Bool(OUTPUT)
val roready = Bool(INPUT)
val rdata = UInt(OUTPUT, width = bits)
val wivalid = Bool(INPUT)
val wiready = Bool(OUTPUT)
val wovalid = Bool(OUTPUT)
val woready = Bool(INPUT)
val wdata = UInt(INPUT, width = bits)
}
val (riready, rovalid, _) = rflow(io.rivalid, io.roready, UInt(0, width = 1))
val (wiready, wovalid, wdata) = wflow(io.wivalid, io.woready, io.wdata)
val reg = Reg(UInt(width = bits))
io.riready := riready
io.rovalid := rovalid
io.wiready := wiready
io.wovalid := wovalid
val rofire = io.roready && rovalid
val wofire = io.woready && wovalid
io.rdata := Mux(rofire, reg, UInt(0))
when (wofire) { reg := wdata }
}
object RRTestRequest
{
private var seed = 1231
def pipe(x: Int): (Bool, Bool, UInt) => (Bool, Bool, UInt) = { (ivalid, oready, idata) =>
val full = RegInit(Vec.fill(x)(Bool(false)))
val ready = Wire(Vec(x, Bool()))
val data = Reg(Vec(x, UInt(width = idata.getWidth)))
// Construct a classic bubble-filling pipeline
ready(x-1) := oready || !full(x-1)
when (ready(0)) { data(0) := idata }
when (ready(0)) { full(0) := ivalid }
((ready.init zip ready.tail) zip full.init) foreach { case ((self, next), full) =>
self := next || !full
}
((data.init zip data.tail) zip ready.tail) foreach { case ((prev, self), ready) =>
when (ready) { self := prev }
}
((full.init zip full.tail) zip ready.tail) foreach { case ((prev, self), ready) =>
when (ready) { self := prev }
}
(ready(0), full(x-1), data(x-1))
}
def busy: (Bool, Bool, UInt) => (Bool, Bool, UInt) = {
seed = seed + 1
(ivalid, oready, idata) => {
val lfsr = LFSR16Seed(seed)
val busy = RegInit(Bool(false))
val data = Reg(UInt(width = idata.getWidth))
val progress = lfsr(0)
when (progress) {
busy := Mux(busy, !oready, ivalid)
}
val iready = progress && !busy
val ovalid = progress && busy
when (ivalid && iready) { data := idata }
(iready, ovalid, data)
}
}
def request(bits: Int,
rflow: (Bool, Bool, UInt) => (Bool, Bool, UInt),
wflow: (Bool, Bool, UInt) => (Bool, Bool, UInt)): RegField = {
val request = Module(new RRTestRequest(bits, rflow, wflow))
RegField(bits,
RegReadFn { (rivalid, roready) =>
request.io.rivalid := rivalid
request.io.roready := roready
(request.io.riready, request.io.rovalid, request.io.rdata) },
RegWriteFn { (wivalid, woready, wdata) =>
request.io.wivalid := wivalid
request.io.woready := woready
request.io.wdata := wdata
(request.io.wiready, request.io.wovalid) })
}
}
object RRTest0Map
{
import RRTestCombinational._
def aa(bits: Int) = combo(bits, always, always)
def ar(bits: Int) = combo(bits, always, random)
def ad(bits: Int) = combo(bits, always, delay(11))
def ae(bits: Int) = combo(bits, always, delay(5))
def ra(bits: Int) = combo(bits, random, always)
def rr(bits: Int) = combo(bits, random, random)
def rd(bits: Int) = combo(bits, random, delay(11))
def re(bits: Int) = combo(bits, random, delay(5))
def da(bits: Int) = combo(bits, delay(5), always)
def dr(bits: Int) = combo(bits, delay(5), random)
def dd(bits: Int) = combo(bits, delay(5), delay(5))
def de(bits: Int) = combo(bits, delay(5), delay(11))
def ea(bits: Int) = combo(bits, delay(11), always)
def er(bits: Int) = combo(bits, delay(11), random)
def ed(bits: Int) = combo(bits, delay(11), delay(5))
def ee(bits: Int) = combo(bits, delay(11), delay(11))
// All fields must respect byte alignment, or else it won't behave like an SRAM
val map = Seq(
0 -> Seq(aa(8), ar(8), ad(8), ae(8)),
1 -> Seq(ra(8), rr(8), rd(8), re(8)),
2 -> Seq(da(8), dr(8), dd(8), de(8)),
3 -> Seq(ea(8), er(8), ed(8), ee(8)),
4 -> Seq(aa(3), ar(5), ad(1), ae(7), ra(2), rr(6), rd(4), re(4)),
5 -> Seq(da(3), dr(5), dd(1), de(7), ea(2), er(6), ed(4), ee(4)),
6 -> Seq(aa(8), rr(8), dd(8), ee(8)),
7 -> Seq(ar(8), rd(8), de(8), ea(8)))
}
object RRTest1Map
{
import RRTestRequest._
def pp(bits: Int) = request(bits, pipe(3), pipe(3))
def pb(bits: Int) = request(bits, pipe(3), busy)
def bp(bits: Int) = request(bits, busy, pipe(3))
def bb(bits: Int) = request(bits, busy, busy)
val map = RRTest0Map.map.take(6) ++ Seq(
6 -> Seq(pp(8), pb(8), bp(8), bb(8)),
7 -> Seq(pp(3), pb(5), bp(1), bb(7), pb(5), bp(3), pp(4), bb(4)))
}
trait RRTest0Bundle
{
}
trait RRTest0Module extends HasRegMap
{
regmap(RRTest0Map.map:_*)
}
class RRTest0(address: BigInt) extends TLRegisterRouter(address, 0, 32, Some(0), 4)(
new TLRegBundle((), _) with RRTest0Bundle)(
new TLRegModule((), _, _) with RRTest0Module)
trait RRTest1Bundle
{
}
trait RRTest1Module extends HasRegMap
{
regmap(RRTest1Map.map:_*)
}
class RRTest1(address: BigInt) extends TLRegisterRouter(address, 0, 32, Some(6), 4)(
new TLRegBundle((), _) with RRTest1Bundle)(
new TLRegModule((), _, _) with RRTest1Module)

View File

@ -81,5 +81,6 @@ object UncoreUnitTests {
Seq(
Module(new SmiConverterTest),
Module(new ROMSlaveTest),
Module(new TileLinkRAMTest))
Module(new TileLinkRAMTest),
Module(new uncore.tilelink2.TLFuzzRAMTest))
}