memtest.cc revision 3283
1/* 2 * Copyright (c) 2002-2005 The Regents of The University of Michigan 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer; 9 * redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution; 12 * neither the name of the copyright holders nor the names of its 13 * contributors may be used to endorse or promote products derived from 14 * this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 * 28 * Authors: Erik Hallnor 29 * Steve Reinhardt 30 */ 31 32// FIX ME: make trackBlkAddr use blocksize from actual cache, not hard coded 33 34#include <iomanip> 35#include <set> 36#include <string> 37#include <vector> 38 39#include "base/misc.hh" 40#include "base/statistics.hh" 41//#include "cpu/simple_thread.hh" 42#include "cpu/memtest/memtest.hh" 43//#include "mem/cache/base_cache.hh" 44//#include "mem/physical.hh" 45#include "sim/builder.hh" 46#include "sim/sim_events.hh" 47#include "sim/stats.hh" 48#include "mem/packet.hh" 49#include "mem/request.hh" 50#include "mem/port.hh" 51#include "mem/mem_object.hh" 52 53using namespace std; 54 55int TESTER_ALLOCATOR=0; 56 57bool 58MemTest::CpuPort::recvTiming(Packet *pkt) 59{ 60 memtest->completeRequest(pkt); 61 return true; 62} 63 64Tick 65MemTest::CpuPort::recvAtomic(Packet *pkt) 66{ 67 panic("MemTest doesn't expect recvAtomic callback!"); 68 return curTick; 69} 70 71void 72MemTest::CpuPort::recvFunctional(Packet *pkt) 73{ 74 //Do nothing if we see one come through 75 if (curTick != 0)//Supress warning durring initialization 76 warn("Functional Writes not implemented in MemTester\n"); 77 //Need to find any response values that intersect and update 78 return; 79} 80 81void 82MemTest::CpuPort::recvStatusChange(Status status) 83{ 84 if (status == RangeChange) 85 return; 86 87 panic("MemTest doesn't expect recvStatusChange callback!"); 88} 89 90void 91MemTest::CpuPort::recvRetry() 92{ 93 memtest->doRetry(); 94} 95 96void 97MemTest::sendPkt(Packet *pkt) { 98 if (atomic) { 99 cachePort.sendAtomic(pkt); 100 pkt->makeAtomicResponse(); 101 completeRequest(pkt); 102 } 103 else if (!cachePort.sendTiming(pkt)) { 104 accessRetry = true; 105 retryPkt = pkt; 106 } 107 108} 109 110MemTest::MemTest(const string &name, 111// MemInterface *_cache_interface, 112// PhysicalMemory *main_mem, 113// PhysicalMemory *check_mem, 114 unsigned _memorySize, 115 unsigned _percentReads, 116// unsigned _percentCopies, 117 unsigned _percentUncacheable, 118 unsigned _progressInterval, 119 unsigned _percentSourceUnaligned, 120 unsigned _percentDestUnaligned, 121 Addr _traceAddr, 122 Counter _max_loads, 123 bool _atomic) 124 : MemObject(name), 125 tickEvent(this), 126 cachePort("test", this), 127 funcPort("functional", this), 128 retryPkt(NULL), 129// mainMem(main_mem), 130// checkMem(check_mem), 131 size(_memorySize), 132 percentReads(_percentReads), 133// percentCopies(_percentCopies), 134 percentUncacheable(_percentUncacheable), 135 progressInterval(_progressInterval), 136 nextProgressMessage(_progressInterval), 137 percentSourceUnaligned(_percentSourceUnaligned), 138 percentDestUnaligned(percentDestUnaligned), 139 maxLoads(_max_loads), 140 atomic(_atomic) 141{ 142 vector<string> cmd; 143 cmd.push_back("/bin/ls"); 144 vector<string> null_vec; 145 // thread = new SimpleThread(NULL, 0, NULL, 0, mainMem); 146 curTick = 0; 147 148 // Needs to be masked off once we know the block size. 149 traceBlockAddr = _traceAddr; 150 baseAddr1 = 0x100000; 151 baseAddr2 = 0x400000; 152 uncacheAddr = 0x800000; 153 154 // set up counters 155 noResponseCycles = 0; 156 numReads = 0; 157 tickEvent.schedule(0); 158 159 id = TESTER_ALLOCATOR++; 160 if (TESTER_ALLOCATOR > 8) 161 panic("False sharing memtester only allows up to 8 testers"); 162 163 accessRetry = false; 164} 165 166Port * 167MemTest::getPort(const std::string &if_name, int idx) 168{ 169 if (if_name == "functional") 170 return &funcPort; 171 else if (if_name == "test") 172 return &cachePort; 173 else 174 panic("No Such Port\n"); 175} 176 177void 178MemTest::init() 179{ 180 // By the time init() is called, the ports should be hooked up. 181 blockSize = cachePort.peerBlockSize(); 182 blockAddrMask = blockSize - 1; 183 traceBlockAddr = blockAddr(traceBlockAddr); 184 185 // set up intial memory contents here 186 187 cachePort.memsetBlob(baseAddr1, 1, size); 188 funcPort.memsetBlob(baseAddr1, 1, size); 189 cachePort.memsetBlob(baseAddr2, 2, size); 190 funcPort.memsetBlob(baseAddr2, 2, size); 191 cachePort.memsetBlob(uncacheAddr, 3, size); 192 funcPort.memsetBlob(uncacheAddr, 3, size); 193} 194 195static void 196printData(ostream &os, uint8_t *data, int nbytes) 197{ 198 os << hex << setfill('0'); 199 // assume little-endian: print bytes from highest address to lowest 200 for (uint8_t *dp = data + nbytes - 1; dp >= data; --dp) { 201 os << setw(2) << (unsigned)*dp; 202 } 203 os << dec; 204} 205 206void 207MemTest::completeRequest(Packet *pkt) 208{ 209 MemTestSenderState *state = 210 dynamic_cast<MemTestSenderState *>(pkt->senderState); 211 212 uint8_t *data = state->data; 213 uint8_t *pkt_data = pkt->getPtr<uint8_t>(); 214 Request *req = pkt->req; 215 216 //Remove the address from the list of outstanding 217 std::set<unsigned>::iterator removeAddr = outstandingAddrs.find(req->getPaddr()); 218 assert(removeAddr != outstandingAddrs.end()); 219 outstandingAddrs.erase(removeAddr); 220 221 switch (pkt->cmd) { 222 case Packet::ReadResp: 223 224 if (memcmp(pkt_data, data, pkt->getSize()) != 0) { 225 cerr << name() << ": on read of 0x" << hex << req->getPaddr() 226 << " (0x" << hex << blockAddr(req->getPaddr()) << ")" 227 << "@ cycle " << dec << curTick 228 << ", cache returns 0x"; 229 printData(cerr, pkt_data, pkt->getSize()); 230 cerr << ", expected 0x"; 231 printData(cerr, data, pkt->getSize()); 232 cerr << endl; 233 fatal(""); 234 } 235 236 numReads++; 237 numReadsStat++; 238 239 if (numReads == nextProgressMessage) { 240 ccprintf(cerr, "%s: completed %d read accesses @%d\n", 241 name(), numReads, curTick); 242 nextProgressMessage += progressInterval; 243 } 244 245 if (numReads >= maxLoads) 246 exitSimLoop("Maximum number of loads reached!"); 247 break; 248 249 case Packet::WriteResp: 250 numWritesStat++; 251 break; 252/* 253 case Copy: 254 //Also remove dest from outstanding list 255 removeAddr = outstandingAddrs.find(req->dest); 256 assert(removeAddr != outstandingAddrs.end()); 257 outstandingAddrs.erase(removeAddr); 258 numCopiesStat++; 259 break; 260*/ 261 default: 262 panic("invalid command"); 263 } 264 265 if (blockAddr(req->getPaddr()) == traceBlockAddr) { 266 cerr << name() << ": completed " 267 << (pkt->isWrite() ? "write" : "read") 268 << " access of " 269 << dec << pkt->getSize() << " bytes at address 0x" 270 << hex << req->getPaddr() 271 << " (0x" << hex << blockAddr(req->getPaddr()) << ")" 272 << ", value = 0x"; 273 printData(cerr, pkt_data, pkt->getSize()); 274 cerr << " @ cycle " << dec << curTick; 275 276 cerr << endl; 277 } 278 279 noResponseCycles = 0; 280 delete state; 281 delete [] data; 282 delete pkt->req; 283 delete pkt; 284} 285 286void 287MemTest::regStats() 288{ 289 using namespace Stats; 290 291 numReadsStat 292 .name(name() + ".num_reads") 293 .desc("number of read accesses completed") 294 ; 295 296 numWritesStat 297 .name(name() + ".num_writes") 298 .desc("number of write accesses completed") 299 ; 300 301 numCopiesStat 302 .name(name() + ".num_copies") 303 .desc("number of copy accesses completed") 304 ; 305} 306 307void 308MemTest::tick() 309{ 310 if (!tickEvent.scheduled()) 311 tickEvent.schedule(curTick + cycles(1)); 312 313 if (++noResponseCycles >= 500000) { 314 cerr << name() << ": deadlocked at cycle " << curTick << endl; 315 fatal(""); 316 } 317 318 if (accessRetry) { 319 return; 320 } 321 322 //make new request 323 unsigned cmd = random() % 100; 324 unsigned offset = random() % size; 325 unsigned base = random() % 2; 326 uint64_t data = random(); 327 unsigned access_size = random() % 4; 328 unsigned cacheable = random() % 100; 329 330 //If we aren't doing copies, use id as offset, and do a false sharing 331 //mem tester 332 //We can eliminate the lower bits of the offset, and then use the id 333 //to offset within the blks 334 offset &= ~63; //Not the low order bits 335 offset += id; 336 access_size = 0; 337 338 Request *req = new Request(); 339 uint32_t flags = 0; 340 Addr paddr; 341 342 if (cacheable < percentUncacheable) { 343 flags |= UNCACHEABLE; 344 paddr = uncacheAddr + offset; 345 } else { 346 paddr = ((base) ? baseAddr1 : baseAddr2) + offset; 347 } 348 //bool probe = (random() % 2 == 1) && !req->isUncacheable(); 349 bool probe = false; 350 351 paddr &= ~((1 << access_size) - 1); 352 req->setPhys(paddr, 1 << access_size, flags); 353 req->setThreadContext(id,0); 354 355 uint8_t *result = new uint8_t[8]; 356 357 if (cmd < percentReads) { 358 // read 359 360 //For now we only allow one outstanding request per addreess per tester 361 //This means we assume CPU does write forwarding to reads that alias something 362 //in the cpu store buffer. 363 if (outstandingAddrs.find(paddr) != outstandingAddrs.end()) { 364 delete result; 365 delete req; 366 return; 367 } 368 else outstandingAddrs.insert(paddr); 369 370 // ***** NOTE FOR RON: I'm not sure how to access checkMem. - Kevin 371 funcPort.readBlob(req->getPaddr(), result, req->getSize()); 372 373 if (blockAddr(paddr) == traceBlockAddr) { 374 cerr << name() 375 << ": initiating read " 376 << ((probe) ? "probe of " : "access of ") 377 << dec << req->getSize() << " bytes from addr 0x" 378 << hex << paddr 379 << " (0x" << hex << blockAddr(paddr) << ")" 380 << " at cycle " 381 << dec << curTick << endl; 382 } 383 384 Packet *pkt = new Packet(req, Packet::ReadReq, Packet::Broadcast); 385 pkt->dataDynamicArray(new uint8_t[req->getSize()]); 386 MemTestSenderState *state = new MemTestSenderState(result); 387 pkt->senderState = state; 388 389 if (probe) { 390 cachePort.sendFunctional(pkt); 391 completeRequest(pkt); 392 } else { 393// req->completionEvent = new MemCompleteEvent(req, result, this); 394 sendPkt(pkt); 395 } 396 } else { 397 // write 398 399 //For now we only allow one outstanding request per addreess per tester 400 //This means we assume CPU does write forwarding to reads that alias something 401 //in the cpu store buffer. 402 if (outstandingAddrs.find(paddr) != outstandingAddrs.end()) { 403 delete [] result; 404 delete req; 405 return; 406 } 407 408 else outstandingAddrs.insert(paddr); 409 410/* 411 if (blockAddr(req->getPaddr()) == traceBlockAddr) { 412 cerr << name() << ": initiating write " 413 << ((probe)?"probe of ":"access of ") 414 << dec << req->getSize() << " bytes (value = 0x"; 415 printData(cerr, data_pkt->getPtr(), req->getSize()); 416 cerr << ") to addr 0x" 417 << hex << req->getPaddr() 418 << " (0x" << hex << blockAddr(req->getPaddr()) << ")" 419 << " at cycle " 420 << dec << curTick << endl; 421 } 422*/ 423 Packet *pkt = new Packet(req, Packet::WriteReq, Packet::Broadcast); 424 uint8_t *pkt_data = new uint8_t[req->getSize()]; 425 pkt->dataDynamicArray(pkt_data); 426 memcpy(pkt_data, &data, req->getSize()); 427 MemTestSenderState *state = new MemTestSenderState(result); 428 pkt->senderState = state; 429 430 funcPort.writeBlob(req->getPaddr(), pkt_data, req->getSize()); 431 432 if (probe) { 433 cachePort.sendFunctional(pkt); 434 completeRequest(pkt); 435 } else { 436// req->completionEvent = new MemCompleteEvent(req, NULL, this); 437 sendPkt(pkt); 438 } 439 } 440/* else { 441 // copy 442 unsigned source_align = random() % 100; 443 unsigned dest_align = random() % 100; 444 unsigned offset2 = random() % size; 445 446 Addr source = ((base) ? baseAddr1 : baseAddr2) + offset; 447 Addr dest = ((base) ? baseAddr2 : baseAddr1) + offset2; 448 if (outstandingAddrs.find(source) != outstandingAddrs.end()) return; 449 else outstandingAddrs.insert(source); 450 if (outstandingAddrs.find(dest) != outstandingAddrs.end()) return; 451 else outstandingAddrs.insert(dest); 452 453 if (source_align >= percentSourceUnaligned) { 454 source = blockAddr(source); 455 } 456 if (dest_align >= percentDestUnaligned) { 457 dest = blockAddr(dest); 458 } 459 req->cmd = Copy; 460 req->flags &= ~UNCACHEABLE; 461 req->paddr = source; 462 req->dest = dest; 463 delete [] req->data; 464 req->data = new uint8_t[blockSize]; 465 req->size = blockSize; 466 if (source == traceBlockAddr || dest == traceBlockAddr) { 467 cerr << name() 468 << ": initiating copy of " 469 << dec << req->size << " bytes from addr 0x" 470 << hex << source 471 << " (0x" << hex << blockAddr(source) << ")" 472 << " to addr 0x" 473 << hex << dest 474 << " (0x" << hex << blockAddr(dest) << ")" 475 << " at cycle " 476 << dec << curTick << endl; 477 }* 478 cacheInterface->access(req); 479 uint8_t result[blockSize]; 480 checkMem->access(Read, source, &result, blockSize); 481 checkMem->access(Write, dest, &result, blockSize); 482 } 483*/ 484} 485 486void 487MemTest::doRetry() 488{ 489 if (cachePort.sendTiming(retryPkt)) { 490 accessRetry = false; 491 retryPkt = NULL; 492 } 493} 494 495BEGIN_DECLARE_SIM_OBJECT_PARAMS(MemTest) 496 497// SimObjectParam<BaseCache *> cache; 498// SimObjectParam<PhysicalMemory *> main_mem; 499// SimObjectParam<PhysicalMemory *> check_mem; 500 Param<unsigned> memory_size; 501 Param<unsigned> percent_reads; 502// Param<unsigned> percent_copies; 503 Param<unsigned> percent_uncacheable; 504 Param<unsigned> progress_interval; 505 Param<unsigned> percent_source_unaligned; 506 Param<unsigned> percent_dest_unaligned; 507 Param<Addr> trace_addr; 508 Param<Counter> max_loads; 509 Param<bool> atomic; 510 511END_DECLARE_SIM_OBJECT_PARAMS(MemTest) 512 513 514BEGIN_INIT_SIM_OBJECT_PARAMS(MemTest) 515 516// INIT_PARAM(cache, "L1 cache"), 517// INIT_PARAM(main_mem, "hierarchical memory"), 518// INIT_PARAM(check_mem, "check memory"), 519 INIT_PARAM(memory_size, "memory size"), 520 INIT_PARAM(percent_reads, "target read percentage"), 521// INIT_PARAM(percent_copies, "target copy percentage"), 522 INIT_PARAM(percent_uncacheable, "target uncacheable percentage"), 523 INIT_PARAM(progress_interval, "progress report interval (in accesses)"), 524 INIT_PARAM(percent_source_unaligned, 525 "percent of copy source address that are unaligned"), 526 INIT_PARAM(percent_dest_unaligned, 527 "percent of copy dest address that are unaligned"), 528 INIT_PARAM(trace_addr, "address to trace"), 529 INIT_PARAM(max_loads, "terminate when we have reached this load count"), 530 INIT_PARAM(atomic, "Is the tester testing atomic mode (or timing)") 531 532END_INIT_SIM_OBJECT_PARAMS(MemTest) 533 534 535CREATE_SIM_OBJECT(MemTest) 536{ 537 return new MemTest(getInstanceName(), /*cache->getInterface(),*/ /*main_mem,*/ 538 /*check_mem,*/ memory_size, percent_reads, /*percent_copies,*/ 539 percent_uncacheable, progress_interval, 540 percent_source_unaligned, percent_dest_unaligned, 541 trace_addr, max_loads, atomic); 542} 543 544REGISTER_SIM_OBJECT("MemTest", MemTest) 545