diff --git a/chisel3 b/chisel3 index 16426b3a..bb240453 160000 --- a/chisel3 +++ b/chisel3 @@ -1 +1 @@ -Subproject commit 16426b3a68d85ce7dd9655b0ce773431eb69fc74 +Subproject commit bb240453abf96b4c2d75ebb2cdc7e3159068431d diff --git a/src/main/scala/junctions/unittests/UnitTest.scala b/src/main/scala/junctions/unittests/UnitTest.scala index f206e945..77dd146b 100644 --- a/src/main/scala/junctions/unittests/UnitTest.scala +++ b/src/main/scala/junctions/unittests/UnitTest.scala @@ -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) diff --git a/src/main/scala/uncore/tilelink2/Buffer.scala b/src/main/scala/uncore/tilelink2/Buffer.scala index 84d6c21b..adfe4c4b 100644 --- a/src/main/scala/uncore/tilelink2/Buffer.scala +++ b/src/main/scala/uncore/tilelink2/Buffer.scala @@ -14,25 +14,23 @@ class TLBuffer(entries: Int = 2, pipe: Boolean = false) extends LazyModule val in = node.bundleIn val out = node.bundleOut } - - val in = io.in(0) - val out = io.out(0) - - 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) { - in .b <> Queue(out.b, entries, pipe) - out.c <> Queue(in .c, entries, pipe) - out.e <> Queue(out.e, entries, pipe) - } else { - in.b.valid := Bool(false) - in.c.ready := Bool(true) - in.e.ready := Bool(true) - out.b.ready := Bool(true) - out.c.valid := Bool(false) - out.e.valid := Bool(false) + ((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) + + 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) + } else { + in.b.valid := Bool(false) + in.c.ready := Bool(true) + in.e.ready := Bool(true) + out.b.ready := Bool(true) + out.c.valid := Bool(false) + out.e.valid := Bool(false) + } } } } diff --git a/src/main/scala/uncore/tilelink2/Edges.scala b/src/main/scala/uncore/tilelink2/Edges.scala index 558af74b..ceab2ca5 100644 --- a/src/main/scala/uncore/tilelink2/Edges.scala +++ b/src/main/scala/uncore/tilelink2/Edges.scala @@ -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 diff --git a/src/main/scala/uncore/tilelink2/Fragmenter.scala b/src/main/scala/uncore/tilelink2/Fragmenter.scala index aa0efc5e..32fd539b 100644 --- a/src/main/scala/uncore/tilelink2/Fragmenter.scala +++ b/src/main/scala/uncore/tilelink2/Fragmenter.scala @@ -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 diff --git a/src/main/scala/uncore/tilelink2/Fuzzer.scala b/src/main/scala/uncore/tilelink2/Fuzzer.scala new file mode 100644 index 00000000..b2a734f8 --- /dev/null +++ b/src/main/scala/uncore/tilelink2/Fuzzer.scala @@ -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 +} diff --git a/src/main/scala/uncore/tilelink2/HintHandler.scala b/src/main/scala/uncore/tilelink2/HintHandler.scala index b8938fbf..3d57f03c 100644 --- a/src/main/scala/uncore/tilelink2/HintHandler.scala +++ b/src/main/scala/uncore/tilelink2/HintHandler.scala @@ -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) { diff --git a/src/main/scala/uncore/tilelink2/Monitor.scala b/src/main/scala/uncore/tilelink2/Monitor.scala index a74a6c20..763ce04e 100644 --- a/src/main/scala/uncore/tilelink2/Monitor.scala +++ b/src/main/scala/uncore/tilelink2/Monitor.scala @@ -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) diff --git a/src/main/scala/uncore/tilelink2/Parameters.scala b/src/main/scala/uncore/tilelink2/Parameters.scala index 355fdb3a..fb36c41d 100644 --- a/src/main/scala/uncore/tilelink2/Parameters.scala +++ b/src/main/scala/uncore/tilelink2/Parameters.scala @@ -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,13 +177,14 @@ 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 // Does this Port manage this ID/address? def contains(address: UInt) = find(address).reduce(_ || _) def containsById(id: UInt) = findById(id).reduce(_ || _) - + private def safety_helper(member: TLManagerParameters => TransferSizes)(address: UInt, lgSize: UInt) = { val allSame = managers.map(member(_) == member(managers(0))).reduce(_ && _) if (allSame) member(managers(0)).containsLg(lgSize) else { @@ -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,15 +252,15 @@ 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)) - + // Synthesizable lookup methods def find(id: UInt) = Vec(clients.map(_.sourceId.contains(id))) def contains(id: UInt) = find(id).reduce(_ || _) - + private def safety_helper(member: TLClientParameters => TransferSizes)(id: UInt, lgSize: UInt) = { val allSame = clients.map(member(_) == member(clients(0))).reduce(_ && _) if (allSame) member(clients(0)).containsLg(lgSize) else { @@ -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), diff --git a/src/main/scala/uncore/tilelink2/RAMModel.scala b/src/main/scala/uncore/tilelink2/RAMModel.scala new file mode 100644 index 00000000..ce3ddb70 --- /dev/null +++ b/src/main/scala/uncore/tilelink2/RAMModel.scala @@ -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) + } + } + } +} diff --git a/src/main/scala/uncore/tilelink2/RegisterRouter.scala b/src/main/scala/uncore/tilelink2/RegisterRouter.scala index a3b61da7..ce46fc11 100644 --- a/src/main/scala/uncore/tilelink2/RegisterRouter.scala +++ b/src/main/scala/uncore/tilelink2/RegisterRouter.scala @@ -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) } diff --git a/src/main/scala/uncore/tilelink2/RegisterRouterTest.scala b/src/main/scala/uncore/tilelink2/RegisterRouterTest.scala new file mode 100644 index 00000000..65dde1d3 --- /dev/null +++ b/src/main/scala/uncore/tilelink2/RegisterRouterTest.scala @@ -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) diff --git a/src/main/scala/uncore/unittests/Tests.scala b/src/main/scala/uncore/unittests/Tests.scala index bcd9de37..c59194b6 100644 --- a/src/main/scala/uncore/unittests/Tests.scala +++ b/src/main/scala/uncore/unittests/Tests.scala @@ -81,5 +81,6 @@ object UncoreUnitTests { Seq( Module(new SmiConverterTest), Module(new ROMSlaveTest), - Module(new TileLinkRAMTest)) + Module(new TileLinkRAMTest), + Module(new uncore.tilelink2.TLFuzzRAMTest)) }