From 4fd9d66c537671f0950687cac327c4513c9c8eed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Soria?= Date: Mon, 24 Jul 2023 16:20:10 +0200 Subject: [PATCH] tests,mem-ruby: Enhance ruby false sharing test with Atomics New ruby mem test includes a percentages of AMOs that will be executed randomly in ruby mem test Change-Id: Ie95ed78e59ea773ce6b59060eaece3701fe4478c --- configs/example/ruby_mem_test.py | 11 ++++- src/cpu/testers/memtest/MemTest.py | 1 + src/cpu/testers/memtest/memtest.cc | 72 +++++++++++++++++++++++++----- src/cpu/testers/memtest/memtest.hh | 4 ++ src/mem/request.hh | 7 +++ 5 files changed, 81 insertions(+), 14 deletions(-) diff --git a/configs/example/ruby_mem_test.py b/configs/example/ruby_mem_test.py index c90950107e..27751376fc 100644 --- a/configs/example/ruby_mem_test.py +++ b/configs/example/ruby_mem_test.py @@ -62,6 +62,12 @@ parser.add_argument( default=0, help="percentage of accesses that should be functional", ) +parser.add_argument( + "--atomic", + type=int, + default=30, + help="percentage of accesses that should be atomic", +) parser.add_argument( "--suppress-func-errors", action="store_true", @@ -105,6 +111,7 @@ cpus = [ max_loads=args.maxloads, percent_functional=args.functional, percent_uncacheable=0, + percent_atomic=args.atomic, progress_interval=args.progress, suppress_func_errors=args.suppress_func_errors, ) @@ -133,7 +140,7 @@ else: dmas = [] dma_ports = [] -for (i, dma) in enumerate(dmas): +for i, dma in enumerate(dmas): dma_ports.append(dma.test) Ruby.create_system(args, False, system, dma_ports=dma_ports) @@ -155,7 +162,7 @@ system.ruby.randomization = True assert len(cpus) == len(system.ruby._cpu_ports) -for (i, cpu) in enumerate(cpus): +for i, cpu in enumerate(cpus): # # Tie the cpu memtester ports to the correct system ports # diff --git a/src/cpu/testers/memtest/MemTest.py b/src/cpu/testers/memtest/MemTest.py index 24bd974804..15c329ee85 100644 --- a/src/cpu/testers/memtest/MemTest.py +++ b/src/cpu/testers/memtest/MemTest.py @@ -63,6 +63,7 @@ class MemTest(ClockedObject): percent_reads = Param.Percent(65, "Percentage reads") percent_functional = Param.Percent(50, "Percentage functional accesses") percent_uncacheable = Param.Percent(10, "Percentage uncacheable") + percent_atomic = Param.Percent(50, "Percentage atomics") # Determine how often to print progress messages and what timeout # to use for checking progress of both requests and responses diff --git a/src/cpu/testers/memtest/memtest.cc b/src/cpu/testers/memtest/memtest.cc index 7c256d8642..a84bf67cd9 100644 --- a/src/cpu/testers/memtest/memtest.cc +++ b/src/cpu/testers/memtest/memtest.cc @@ -94,6 +94,7 @@ MemTest::MemTest(const Params &p) percentReads(p.percent_reads), percentFunctional(p.percent_functional), percentUncacheable(p.percent_uncacheable), + percentAtomic(p.percent_atomic), requestorId(p.system->getRequestorId(this)), blockSize(p.system->cacheLineSize()), blockAddrMask(blockSize - 1), @@ -115,6 +116,7 @@ MemTest::MemTest(const Params &p) // set up counters numReads = 0; numWrites = 0; + numAtomics = 0; // kick things into action schedule(tickEvent, curTick()); @@ -142,7 +144,7 @@ MemTest::completeRequest(PacketPtr pkt, bool functional) outstandingAddrs.erase(remove_addr); DPRINTF(MemTest, "Completing %s at address %x (blk %x) %s\n", - pkt->isWrite() ? "write" : "read", + pkt->isWrite() ? pkt->isAtomicOp() ? "atomic" : "write" : "read", req->getPaddr(), blockAlign(req->getPaddr()), pkt->isError() ? "error" : "success"); @@ -153,7 +155,25 @@ MemTest::completeRequest(PacketPtr pkt, bool functional) panic( "%s access failed at %#x\n", pkt->isWrite() ? "Write" : "Read", req->getPaddr()); } else { - if (pkt->isRead()) { + if (pkt->isAtomicOp()) { + uint8_t ref_data = referenceData[req->getPaddr()]; + if (pkt_data[0] != ref_data) { + panic("%s: read of %x (blk %x) @ cycle %d " + "returns %x, expected %x\n", name(), + req->getPaddr(), blockAlign(req->getPaddr()), curTick(), + pkt_data[0], ref_data); + } + DPRINTF(MemTest, + "Completing atomic at address %x (blk %x) value %x\n", + req->getPaddr(), blockAlign(req->getPaddr()), + pkt_data[0]); + + referenceData[req->getPaddr()] = + atomicPendingData[req->getPaddr()]; + + numAtomics++; + stats.numAtomics++; + } else if (pkt->isRead()) { uint8_t ref_data = referenceData[req->getPaddr()]; if (pkt_data[0] != ref_data) { panic("%s: read of %x (blk %x) @ cycle %d " @@ -167,9 +187,10 @@ MemTest::completeRequest(PacketPtr pkt, bool functional) if (numReads == (uint64_t)nextProgressMessage) { ccprintf(std::cerr, - "%s: completed %d read, %d write accesses @%d\n", - name(), numReads, numWrites, curTick()); - nextProgressMessage += progressInterval; + "%s: completed %d read, %d write, " + "%d atomic accesses @%d\n", + name(), numReads, numWrites, numAtomics, curTick()); + nextProgressMessage += progressInterval; } if (maxLoads != 0 && numReads >= maxLoads) @@ -205,7 +226,9 @@ MemTest::MemTestStats::MemTestStats(statistics::Group *parent) ADD_STAT(numReads, statistics::units::Count::get(), "number of read accesses completed"), ADD_STAT(numWrites, statistics::units::Count::get(), - "number of write accesses completed") + "number of write accesses completed"), + ADD_STAT(numAtomics, statistics::units::Count::get(), + "number of atomic accesses completed") { } @@ -221,6 +244,8 @@ MemTest::tick() unsigned cmd = random_mt.random(0, 100); uint8_t data = random_mt.random(); bool uncacheable = random_mt.random(0, 100) < percentUncacheable; + bool do_atomic = (random_mt.random(0, 100) < percentAtomic) && + !uncacheable; unsigned base = random_mt.random(0, 1); Request::Flags flags; Addr paddr; @@ -281,13 +306,36 @@ MemTest::tick() pkt = new Packet(req, MemCmd::ReadReq); pkt->dataDynamic(pkt_data); } else { - DPRINTF(MemTest, "Initiating %swrite at addr %x (blk %x) value %x\n", - do_functional ? "functional " : "", req->getPaddr(), - blockAlign(req->getPaddr()), data); + if (do_atomic) { + DPRINTF(MemTest, + "Initiating atomic at addr %x (blk %x) value %x\n", + req->getPaddr(), blockAlign(req->getPaddr()), data); - pkt = new Packet(req, MemCmd::WriteReq); - pkt->dataDynamic(pkt_data); - pkt_data[0] = data; + TypedAtomicOpFunctor *_amo_op = + new AtomicGeneric3Op( + data, data, + [](uint8_t* b, uint8_t a, uint8_t c){ + *b = c; + }); + assert(_amo_op); + AtomicOpFunctorPtr amo_op = AtomicOpFunctorPtr(_amo_op); + req->setAtomicOpFunctor(std::move(amo_op)); + req->setFlags(Request::ATOMIC_RETURN_OP); + + pkt = new Packet(req, MemCmd::WriteReq); + pkt->dataDynamic(pkt_data); + pkt_data[0] = data; + atomicPendingData[req->getPaddr()] = data; + } else { + DPRINTF(MemTest, + "Initiating %swrite at addr %x (blk %x) value %x\n", + do_functional ? "functional " : "", req->getPaddr(), + blockAlign(req->getPaddr()), data); + + pkt = new Packet(req, MemCmd::WriteReq); + pkt->dataDynamic(pkt_data); + pkt_data[0] = data; + } } // there is no point in ticking if we are waiting for a retry diff --git a/src/cpu/testers/memtest/memtest.hh b/src/cpu/testers/memtest/memtest.hh index 3fd1674191..ee512048c1 100644 --- a/src/cpu/testers/memtest/memtest.hh +++ b/src/cpu/testers/memtest/memtest.hh @@ -131,6 +131,7 @@ class MemTest : public ClockedObject const unsigned percentReads; const unsigned percentFunctional; const unsigned percentUncacheable; + const unsigned percentAtomic; /** Request id for all generated traffic */ RequestorID requestorId; @@ -138,6 +139,7 @@ class MemTest : public ClockedObject unsigned int id; std::unordered_set outstandingAddrs; + std::unordered_map atomicPendingData; // store the expected value for the addresses we have touched std::unordered_map referenceData; @@ -169,6 +171,7 @@ class MemTest : public ClockedObject uint64_t numReads; uint64_t numWrites; + uint64_t numAtomics; const uint64_t maxLoads; const bool atomic; @@ -180,6 +183,7 @@ class MemTest : public ClockedObject MemTestStats(statistics::Group *parent); statistics::Scalar numReads; statistics::Scalar numWrites; + statistics::Scalar numAtomics; } stats; /** diff --git a/src/mem/request.hh b/src/mem/request.hh index 491aad0241..df249ac249 100644 --- a/src/mem/request.hh +++ b/src/mem/request.hh @@ -757,6 +757,13 @@ class Request : public Extensible return atomicOpFunctor.get(); } + void + setAtomicOpFunctor(AtomicOpFunctorPtr amo_op) + { + atomicOpFunctor = std::move(amo_op); + } + + /** * Accessor for hardware transactional memory abort cause. */