From 176d3c890c3bb62b252454bd27798bda88dd52d8 Mon Sep 17 00:00:00 2001 From: Howard Mao Date: Mon, 14 Dec 2015 19:45:20 -0800 Subject: [PATCH] add some more regression tests --- groundtest/src/main/scala/regression.scala | 273 ++++++++++++++++++++- 1 file changed, 265 insertions(+), 8 deletions(-) diff --git a/groundtest/src/main/scala/regression.scala b/groundtest/src/main/scala/regression.scala index 984e59bd..41a0f540 100644 --- a/groundtest/src/main/scala/regression.scala +++ b/groundtest/src/main/scala/regression.scala @@ -3,13 +3,18 @@ package groundtest import Chisel._ import uncore._ import junctions.{MMIOBase, ParameterizedBundle} -import cde.Parameters +import rocket.HellaCacheIO +import cde.{Parameters, Field} -class RegressionIO(implicit val p: Parameters) extends GroundTestIO()(p) { +class RegressionIO(implicit val p: Parameters) extends ParameterizedBundle()(p) { val start = Bool(INPUT) + val cache = new HellaCacheIO + val mem = new ClientUncachedTileLinkIO + val finished = Bool(OUTPUT) } -abstract class Regression(implicit val p: Parameters) extends Module { +abstract class Regression(implicit val p: Parameters) + extends Module with HasTileLinkParameters { val io = new RegressionIO } @@ -19,9 +24,7 @@ abstract class Regression(implicit val p: Parameters) extends Module { * same time. Repeating this sequence enough times will cause a queue to * get filled up and deadlock the system. */ -class IOGetAfterPutBlockRegression(implicit p: Parameters) - extends Regression()(p) with HasTileLinkParameters { - +class IOGetAfterPutBlockRegression(implicit p: Parameters) extends Regression()(p) { val nRuns = 7 val run = Reg(init = UInt(0, log2Up(nRuns + 1))) @@ -71,10 +74,262 @@ class IOGetAfterPutBlockRegression(implicit p: Parameters) io.finished := (run === UInt(nRuns)) } +/* This was a bug with merging two PutBlocks to the same address in the L2. + * The transactor would start accepting beats of the second transaction but + * acknowledge both of them when the first one finished. + * This caused the state to go funky since the next time around it would + * start the put in the middle */ +class PutBlockMergeRegression(implicit p: Parameters) + extends Regression()(p) with HasTileLinkParameters { + val s_idle :: s_put :: s_wait :: s_done :: Nil = Enum(Bits(), 4) + val state = Reg(init = s_idle) + + io.cache.req.valid := Bool(false) + + val l2params = p.alterPartial({ case CacheName => "L2Bank" }) + val nSets = l2params(NSets) + val addr_blocks = Vec(UInt(0), UInt(0), UInt(nSets)) + val nSteps = addr_blocks.size + val (acq_beat, acq_done) = Counter(io.mem.acquire.fire(), tlDataBeats) + val (send_cnt, send_done) = Counter(acq_done, nSteps) + val (ack_cnt, ack_done) = Counter(io.mem.grant.fire(), nSteps) + + io.mem.acquire.valid := (state === s_put) + io.mem.acquire.bits := PutBlock( + client_xact_id = send_cnt, + addr_block = addr_blocks(send_cnt), + addr_beat = acq_beat, + data = Cat(send_cnt, acq_beat)) + io.mem.grant.ready := Bool(true) + + when (state === s_idle && io.start) { state := s_put } + when (send_done) { state := s_wait } + when (ack_done) { state := s_done } + + io.finished := (state === s_done) +} + +/* Make sure the L2 does "the right thing" when a put is sent no-alloc but + * the block is already in cache. It should just treat the request as a + * regular allocating put */ +class NoAllocPutHitRegression(implicit p: Parameters) extends Regression()(p) { + val (s_idle :: s_prefetch :: s_put :: s_get :: + s_wait :: s_done :: Nil) = Enum(Bits(), 6) + val state = Reg(init = s_idle) + + val acq = io.mem.acquire.bits + val gnt = io.mem.grant.bits + + val (put_beat, put_done) = Counter(io.mem.acquire.fire() && acq.hasData(), tlDataBeats) + val acked = Reg(init = UInt(0, tlDataBeats + 2)) + + val addr_block = UInt(2) + val test_data = UInt(0x3446) + + val prefetch_acq = GetPrefetch( + client_xact_id = UInt(0), + addr_block = addr_block) + val put_acq = PutBlock( + client_xact_id = UInt(1), + addr_block = addr_block, + addr_beat = put_beat, + data = test_data, + alloc = Bool(false)) + val get_acq = GetBlock( + client_xact_id = UInt(2), + addr_block = addr_block) + + io.mem.acquire.valid := (state === s_prefetch) || (state === s_get) || (state === s_put) + io.mem.acquire.bits := MuxBundle(get_acq, Seq( + (state === s_prefetch) -> prefetch_acq, + (state === s_put) -> put_acq)) + io.mem.grant.ready := Bool(true) + + when (state === s_idle && io.start) { state := s_prefetch } + when (state === s_prefetch && io.mem.acquire.ready) { state := s_put } + when (put_done) { state := s_get } + when (state === s_get && io.mem.acquire.ready) { state := s_wait } + when (state === s_wait && acked.andR) { state := s_done } + + when (io.mem.grant.fire()) { + switch (gnt.client_xact_id) { + is (UInt(0)) { acked := acked | UInt(1 << tlDataBeats) } + is (UInt(1)) { acked := acked | UInt(1 << (tlDataBeats + 1)) } + is (UInt(2)) { acked := acked | UIntToOH(gnt.addr_beat) } + } + } + + assert(!io.mem.grant.valid || !gnt.hasData() || gnt.data === test_data, + "NoAllocPutHitRegression: data does not match") + + io.finished := (state === s_done) + io.cache.req.valid := Bool(false) +} + +/* Make sure each no-alloc put triggers a request to outer memory. + * Unfortunately, there's no way to verify that this works except by looking + * at the waveform */ +class RepeatedNoAllocPutRegression(implicit p: Parameters) extends Regression()(p) { + io.cache.req.valid := Bool(false) + + val nPuts = 2 + val (put_beat, put_done) = Counter(io.mem.acquire.fire(), tlDataBeats) + val (req_cnt, req_done) = Counter(put_done, nPuts) + + val sending = Reg(init = Bool(false)) + val acked = Reg(init = UInt(0, nPuts)) + + when (!sending && io.start) { sending := Bool(true) } + when (sending && req_done) { sending := Bool(false) } + + io.mem.acquire.valid := sending + io.mem.acquire.bits := PutBlock( + client_xact_id = req_cnt, + addr_block = UInt(5), + addr_beat = put_beat, + data = Cat(req_cnt, UInt(0, 8)), + alloc = Bool(false)) + io.mem.grant.ready := Bool(true) + + when (io.mem.grant.fire()) { + acked := acked | UIntToOH(io.mem.grant.bits.client_xact_id) + } + + io.finished := acked.andR +} + +/* Make sure write masking works properly. + * This test assumes the memory is initialized to zero at the beginning. + * This is true for the test harnesses, but not on the FPGA. + * Technically, what should be done is to fill up a single set until the + * first block is evicted, and then do the write-masked puts. + * But this is annoying, and I doubt we will ever run these regression + * tests on the FPGA. So ... */ +class WriteMaskedPutBlockRegression(implicit p: Parameters) extends Regression()(p) { + io.cache.req.valid := Bool(false) + + val s_idle :: s_put :: s_get :: s_wait :: s_done :: Nil = Enum(Bits(), 5) + val state = Reg(init = s_idle) + + val nPuts = 2 + val (put_beat, put_block_done) = Counter( + io.mem.acquire.fire() && io.mem.acquire.bits.hasData(), tlDataBeats) + val (put_cnt, puts_done) = Counter(put_block_done, nPuts) + + val data_bytes = Vec.tabulate(nPuts) { i => UInt(i, 8) } + val data_beat = Fill(tlDataBytes, data_bytes(put_cnt)) + val acked = Reg(init = UInt(0, nPuts + 1)) + + val put_acq = PutBlock( + client_xact_id = put_cnt, + addr_block = UInt(7), + addr_beat = put_beat, + data = data_beat, + wmask = UIntToOH(put_cnt)) + + val get_acq = Get( + client_xact_id = UInt(nPuts), + addr_block = UInt(7), + addr_beat = UInt(0), + addr_byte = UInt(0), + operand_size = MT_D, + alloc = Bool(false)) + + io.mem.acquire.valid := (state === s_put || state === s_get) + io.mem.acquire.bits := Mux(state === s_get, get_acq, put_acq) + io.mem.grant.ready := Bool(true) + + when (io.mem.grant.fire()) { + acked := acked | UIntToOH(io.mem.grant.bits.client_xact_id) + } + + when (state === s_idle && io.start) { state := s_put } + when (puts_done) { state := s_get } + when (state === s_get && io.mem.acquire.ready) { state := s_wait } + when (state === s_wait && acked.andR) { state := s_done } + + io.finished := (state === s_done) + + assert(!io.mem.grant.valid || !io.mem.grant.bits.hasData() || + io.mem.grant.bits.data === data_bytes.toBits, + "WriteMaskedPutBlockRegression: data does not match") +} + +/* Make sure a prefetch that hits returns immediately. */ +class PrefetchHitRegression(implicit p: Parameters) extends Regression()(p) { + io.cache.req.valid := Bool(false) + + val sending = Reg(init = Bool(false)) + val nPrefetches = 2 + val (pf_cnt, pf_done) = Counter(io.mem.acquire.fire(), nPrefetches) + val acked = Reg(init = UInt(0, nPrefetches)) + + val acq_bits = Vec( + PutPrefetch(client_xact_id = UInt(0), addr_block = UInt(12)), + GetPrefetch(client_xact_id = UInt(1), addr_block = UInt(12))) + + io.mem.acquire.valid := sending + io.mem.acquire.bits := acq_bits(pf_cnt) + io.mem.grant.ready := Bool(true) + + when (io.mem.grant.fire()) { + acked := acked | UIntToOH(io.mem.grant.bits.client_xact_id) + } + + when (!sending && io.start) { sending := Bool(true) } + when (sending && pf_done) { sending := Bool(false) } + + io.finished := acked.andR +} + +/* This tests the sort of access the pattern that Hwacha uses. + * Instead of using PutBlock/GetBlock, it uses word-sized puts and gets. + * Each request has the same client_xact_id, but there are multiple in flight. + * The responses therefore must come back in the order they are sent. */ +class SequentialSameIdGetRegression(implicit p: Parameters) extends Regression()(p) { + io.cache.req.valid := Bool(false) + + val sending = Reg(init = Bool(false)) + val finished = Reg(init = Bool(false)) + + val (send_cnt, send_done) = Counter(io.mem.acquire.fire(), tlDataBeats) + val (recv_cnt, recv_done) = Counter(io.mem.grant.fire(), tlDataBeats) + + when (!sending && io.start) { sending := Bool(true) } + when (send_done) { sending := Bool(false) } + when (recv_done) { finished := Bool(true) } + + io.mem.acquire.valid := sending + io.mem.acquire.bits := Get( + client_xact_id = UInt(0), + addr_block = UInt(9), + addr_beat = send_cnt) + io.mem.grant.ready := !finished + + io.finished := finished + + assert(!io.mem.grant.valid || io.mem.grant.bits.addr_beat === recv_cnt, + "SequentialSameIdGetRegression: grant received out of order") +} + +object RegressionTests { + def cacheRegressions(implicit p: Parameters) = Seq( + Module(new PutBlockMergeRegression), + Module(new NoAllocPutHitRegression), + Module(new RepeatedNoAllocPutRegression), + Module(new WriteMaskedPutBlockRegression), + Module(new PrefetchHitRegression), + Module(new SequentialSameIdGetRegression)) + def broadcastRegressions(implicit p: Parameters) = Seq( + Module(new IOGetAfterPutBlockRegression), + Module(new WriteMaskedPutBlockRegression)) +} + +case object GroundTestRegressions extends Field[Parameters => Seq[Regression]] + class RegressionTest(implicit p: Parameters) extends GroundTest()(p) { - val regressions = Seq( - Module(new IOGetAfterPutBlockRegression)) + val regressions = p(GroundTestRegressions)(p) val regressIOs = Vec(regressions.map(_.io)) val regress_idx = Reg(init = UInt(0, log2Up(regressions.size + 1))) val all_done = (regress_idx === UInt(regressions.size)) @@ -112,4 +367,6 @@ class RegressionTest(implicit p: Parameters) extends GroundTest()(p) { val timeout = Timer(5000, start, cur_regression.finished) assert(!timeout, "Regression timed out") + + assert(!(all_done && io.mem.grant.valid), "Getting grant after test completion") }