From 5562241a50f5ffcffb03d4860f0e6c26f69348c9 Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Fri, 3 Jun 2016 17:12:12 -0700 Subject: [PATCH] comparator: a new TileLink stress-tester --- groundtest/src/main/scala/comparator.scala | 307 +++++++++++++++++++++ 1 file changed, 307 insertions(+) create mode 100644 groundtest/src/main/scala/comparator.scala diff --git a/groundtest/src/main/scala/comparator.scala b/groundtest/src/main/scala/comparator.scala new file mode 100644 index 00000000..76a792da --- /dev/null +++ b/groundtest/src/main/scala/comparator.scala @@ -0,0 +1,307 @@ +package groundtest + +import Chisel._ +import uncore._ +import junctions._ +import rocket._ +import scala.util.Random +import cde.{Parameters, Field} + +case class ComparatorParameters( + targets: Seq[Long], + width: Int, + operations: Int, + atomics: Boolean) +case object ComparatorKey extends Field[ComparatorParameters] + +trait HasComparatorParameters { + implicit val p: Parameters + val comparatorParams = p(ComparatorKey) + val targets = comparatorParams.targets + val nTargets = targets.size + val targetWidth = comparatorParams.width + val nOperations = comparatorParams.operations + val atomics = comparatorParams.atomics +} + +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 + } +} + +object NoiseMaker +{ + def apply(wide: Int, increment: Bool = Bool(true)): UInt = { + val output = Wire(Vec((wide+63)/64, UInt())) + output.zipWithIndex.foreach { case (v,i) => v := LFSR64() } + output.toBits()(wide-1, 0) + } +} + +object MaskMaker +{ + def apply(wide: Int, bits: UInt): UInt = + Vec.tabulate(wide) {UInt(_) < bits} .toBits() .asUInt() +} + +class ComparatorSource(implicit val p: Parameters) extends Module + with HasComparatorParameters + with HasTileLinkParameters +{ + val io = new Bundle { + val out = Valid(new Acquire) + val finished = Bool(OUTPUT) + } + + // Output exactly nOperations of Acquires + val counter = RegInit(UInt(nOperations)) + val finished = RegInit(Bool(false)) + val valid = RegInit(Bool(false)) + + valid := Bool(true) + counter := counter - (!finished && valid).asUInt + when (counter === UInt(1)) { finished := Bool(true) } + + io.finished := finished + io.out.valid := !finished && valid + + // Generate random operand sizes + val raw_operand_size = NoiseMaker(2) | UInt(0, M_SZ) + val max_operand_size = UInt(log2Up(tlDataBytes)) + val get_operand_size = Mux(raw_operand_size > max_operand_size, max_operand_size, raw_operand_size) + val atomic_operand_size = Mux(NoiseMaker(1)(0), MT_W, MT_D) + + // Generate random, but valid addr_bytes + val raw_addr_byte = NoiseMaker(tlByteAddrBits) + val get_addr_byte = raw_addr_byte & ~MaskMaker(tlByteAddrBits, get_operand_size) + val atomic_addr_byte = raw_addr_byte & ~MaskMaker(tlByteAddrBits, atomic_operand_size) + + // Only allow some of the possible choices (M_XA_MAXU untested) + val atomic_opcode = MuxLookup(NoiseMaker(3), M_XA_SWAP, Array( + UInt("b000") -> M_XA_ADD, + UInt("b001") -> M_XA_XOR, + UInt("b010") -> M_XA_OR, + UInt("b011") -> M_XA_AND, + UInt("b100") -> M_XA_MIN, + UInt("b101") -> M_XA_MAX, + UInt("b110") -> M_XA_MINU, + UInt("b111") -> M_XA_SWAP)) + + // Addr_block range + val addr_block_mask = MaskMaker(tlBlockAddrBits, UInt(targetWidth-tlBeatAddrBits-tlByteAddrBits)) + + // Generate some random values + val addr_block = NoiseMaker(tlBlockAddrBits) & addr_block_mask + val addr_beat = NoiseMaker(tlBeatAddrBits) + val wmask = NoiseMaker(tlDataBytes) + val data = NoiseMaker(tlDataBits) + val client_xact_id = UInt(0) // filled by Client + + // Random transactions + val get = Get(client_xact_id, addr_block, addr_beat, get_addr_byte, get_operand_size, Bool(false)) + val getBlock = GetBlock(client_xact_id, addr_block) + val put = Put(client_xact_id, addr_block, addr_beat, data, wmask) + val putBlock = PutBlock(client_xact_id, addr_block, UInt(0), data, SInt(-1, tlDataBytes).asUInt) + val putAtomic = PutAtomic(client_xact_id, addr_block, addr_beat, atomic_addr_byte, atomic_opcode, atomic_operand_size, data) + val putPrefetch = PutPrefetch(client_xact_id, addr_block) + val getPrefetch = GetPrefetch(client_xact_id, addr_block) + val optAtomic = if (atomics) putAtomic else put + + // Generate a random a_type + io.out.bits := (new Acquire).fromBits(MuxLookup(NoiseMaker(3), get.toBits, Array( + UInt("b000") -> get.toBits, + UInt("b001") -> getBlock.toBits, + UInt("b010") -> put.toBits, + UInt("b011") -> putBlock.toBits, + UInt("b100") -> optAtomic.toBits, + UInt("b101") -> getPrefetch.toBits, + UInt("b110") -> putPrefetch.toBits))) + + // We must initially putBlock all of memory to have a consistent starting state + val final_addr_block = addr_block_mask + UInt(1) + val wipe_addr_block = RegInit(UInt(0, width = tlBlockAddrBits)) + val done_wipe = wipe_addr_block === final_addr_block + + // Override whatever else we were going to do if we are wiping + when (!done_wipe) { + io.out.bits := PutBlock(client_xact_id, wipe_addr_block, UInt(0), data, SInt(-1, tlDataBytes).asUInt) + when (valid) { + wipe_addr_block := wipe_addr_block + UInt(1) + } + } +} + +class ComparatorClient(val target: Long)(implicit val p: Parameters) extends Module + with HasComparatorParameters + with HasTileLinkParameters +{ + val io = new Bundle { + val in = Decoupled(new Acquire).flip + val tl = new ClientUncachedTileLinkIO() + val out = Decoupled(new Grant) + val finished = Bool(OUTPUT) + } + + val xacts = tlMaxClientXacts + val offset = (UInt(target) >> UInt(tlBeatAddrBits+tlByteAddrBits)) + + // Track the status of inflight requests + val issued = RegInit(Vec.fill(xacts) {Bool(false)}) + val ready = RegInit(Vec.fill(xacts) {Bool(false)}) + val result = Reg(Vec(xacts, new Grant)) + + // Big enough to never stall the broadcast Source + // (test code not synthesized => big SRAMs are OK) + val buffer = Queue(io.in, nOperations) + val queue = Module(new Queue(io.tl.acquire.bits.client_xact_id, xacts)) + + val isMultiOut = buffer.bits.hasMultibeatData() + val isMultiIn = io.tl.grant.bits.hasMultibeatData() + + val beatOut = RegInit(UInt(0, width = tlBeatAddrBits)) + val lastBeat = UInt(tlDataBeats-1) + val isFirstBeatOut= Mux(isMultiOut, beatOut === UInt(0), Bool(true)) + val isLastBeatOut = Mux(isMultiOut, beatOut === lastBeat, Bool(true)) + val isLastBeatIn = Mux(isMultiIn, io.tl.grant.bits.addr_beat === lastBeat, Bool(true)) + + // Remove this once HoldUnless is in chisel3 + def holdUnless[T <: Data](in : T, enable: Bool): T = Mux(!enable, RegEnable(in, enable), in) + + // Potentially issue a request, using a free xact id + // NOTE: we may retract valid and change xact_id on a !ready (allowed by spec) + val allow_acq = NoiseMaker(1)(0) && issued.map(!_).reduce(_ || _) + val xact_id = holdUnless(PriorityEncoder(issued.map(!_)), isFirstBeatOut) + buffer.ready := allow_acq && io.tl.acquire.ready && isLastBeatOut + io.tl.acquire.valid := allow_acq && buffer.valid + io.tl.acquire.bits := buffer.bits + io.tl.acquire.bits.addr_block := buffer.bits.addr_block + offset + io.tl.acquire.bits.client_xact_id := xact_id + when (isMultiOut) { + io.tl.acquire.bits.addr_beat := beatOut + io.tl.acquire.bits.data := buffer.bits.data * (beatOut + UInt(1)) // mix the data up a bit + } + + when (io.tl.acquire.fire()) { + issued(xact_id) := isLastBeatOut + when (isMultiOut) { beatOut := beatOut + UInt(1) } + } + + // Remember the xact ID so we can return results in-order + queue.io.enq.valid := io.tl.acquire.fire() && isLastBeatOut + queue.io.enq.bits := xact_id + assert (queue.io.enq.ready || !queue.io.enq.valid) // should be big enough + + // Capture the results from the manager + io.tl.grant.ready := NoiseMaker(1)(0) + when (io.tl.grant.fire()) { + val id = io.tl.grant.bits.client_xact_id + assert (!ready(id)) // got same xact_id twice? + ready(id) := isLastBeatIn + result(id) := io.tl.grant.bits + } + + // Bad xact_id returned if ready but not issued! + assert ((ready zip issued) map {case (r,i) => i || !r} reduce (_ && _)) + + // When we have the next grant result, send it to the sink + val next_id = queue.io.deq.bits + queue.io.deq.ready := io.out.ready && ready(next_id) // TODO: only compares last getBlock + io.out.valid := queue.io.deq.valid && ready(next_id) + io.out.bits := result(queue.io.deq.bits) + + when (io.out.fire()) { + ready(next_id) := Bool(false) + issued(next_id) := Bool(false) + } + + io.finished := !buffer.valid && !issued.reduce(_ || _) +} + +class ComparatorSink(implicit val p: Parameters) extends Module + with HasComparatorParameters + with HasTileLinkParameters +{ + val io = new Bundle { + val in = Vec(nTargets, Decoupled(new Grant)).flip + val finished = Bool(OUTPUT) + } + + // could use a smaller Queue here, but would couple targets flow controls together + val queues = io.in.map(Queue(_, nOperations)) + + io.finished := queues.map(!_.valid).reduce(_ && _) + val all_valid = queues.map(_.valid).reduce(_ && _) + queues.foreach(_.ready := all_valid) + + val base = queues(0).bits + def check(g: Grant) = { + assert (g.is_builtin_type, "grant not builtin") + assert (base.g_type === g.g_type, "g_type mismatch") + assert (base.addr_beat === g.addr_beat || !g.is(Grant.getDataBlockType), "addr_beat mismatch") + assert (base.data === g.data || !g.hasData(), "data mismatch") + } + when (all_valid) { + queues.map(_.bits).foreach(check) + } +} + +class ComparatorCore(implicit val p: Parameters) extends Module + with HasComparatorParameters + with HasTileLinkParameters +{ + val io = new Bundle { + val uncached = Vec(nTargets, new ClientUncachedTileLinkIO) + val finished = Bool(OUTPUT) + } + + val source = Module(new ComparatorSource) + val sink = Module(new ComparatorSink) + val clients = targets.zipWithIndex.map { case (target, index) => + val client = Module(new ComparatorClient(target)) + assert (client.io.in.ready) // must accept + client.io.in := source.io.out + io.uncached(index) <> client.io.tl + sink.io.in(index) <> client.io.out + client + } + + io.finished := source.io.finished && sink.io.finished && clients.map(_.io.finished).reduce(_ && _) +} + +class ComparatorTile(resetSignal: Bool)(implicit val p: Parameters) extends Tile(resetSignal)(p) + with HasComparatorParameters + with HasTileLinkParameters +{ + // Make sure we are configured correctly + require (nCachedTileLinkPorts == 1) + require (nUncachedTileLinkPorts == nTargets + 1) + + val core = Module(new ComparatorCore) + val finisher = Module(new GroundTestFinisher) + + // Connect 0..nTargets-1 to core + (io.uncached zip core.io.uncached) map { case (u, c) => u <> c } + io.uncached(nTargets) <> finisher.io.mem + finisher.io.finished := core.io.finished + + // Work-around cachedClients must be >= 1 issue + io.cached(0).acquire.valid := Bool(false) + io.cached(0).grant.ready := Bool(false) + io.cached(0).finish.valid := Bool(false) + io.cached(0).probe.ready := io.cached(0).release.ready + io.cached(0).release.valid := io.cached(0).probe.valid + io.cached(0).release.bits := ClientMetadata.onReset.makeRelease(io.cached(0).probe.bits) +}