memtest.cc revision 3348
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/memtest/memtest.hh" 42//#include "cpu/simple_thread.hh" 43//#include "mem/cache/base_cache.hh" 44#include "mem/mem_object.hh" 45#include "mem/port.hh" 46#include "mem/packet.hh" 47//#include "mem/physical.hh" 48#include "mem/request.hh" 49#include "sim/builder.hh" 50#include "sim/sim_events.hh" 51#include "sim/stats.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) && !(flags & UNCACHEABLE); 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 pkt->makeAtomicResponse(); 392 completeRequest(pkt); 393 } else { 394// req->completionEvent = new MemCompleteEvent(req, result, this); 395 sendPkt(pkt); 396 } 397 } else { 398 // write 399 400 //For now we only allow one outstanding request per addreess per tester 401 //This means we assume CPU does write forwarding to reads that alias something 402 //in the cpu store buffer. 403 if (outstandingAddrs.find(paddr) != outstandingAddrs.end()) { 404 delete [] result; 405 delete req; 406 return; 407 } 408 409 else outstandingAddrs.insert(paddr); 410 411/* 412 if (blockAddr(req->getPaddr()) == traceBlockAddr) { 413 cerr << name() << ": initiating write " 414 << ((probe)?"probe of ":"access of ") 415 << dec << req->getSize() << " bytes (value = 0x"; 416 printData(cerr, data_pkt->getPtr(), req->getSize()); 417 cerr << ") to addr 0x" 418 << hex << req->getPaddr() 419 << " (0x" << hex << blockAddr(req->getPaddr()) << ")" 420 << " at cycle " 421 << dec << curTick << endl; 422 } 423*/ 424 Packet *pkt = new Packet(req, Packet::WriteReq, Packet::Broadcast); 425 uint8_t *pkt_data = new uint8_t[req->getSize()]; 426 pkt->dataDynamicArray(pkt_data); 427 memcpy(pkt_data, &data, req->getSize()); 428 MemTestSenderState *state = new MemTestSenderState(result); 429 pkt->senderState = state; 430 431 funcPort.writeBlob(req->getPaddr(), pkt_data, req->getSize()); 432 433 if (probe) { 434 cachePort.sendFunctional(pkt); 435 pkt->makeAtomicResponse(); 436 completeRequest(pkt); 437 } else { 438// req->completionEvent = new MemCompleteEvent(req, NULL, this); 439 sendPkt(pkt); 440 } 441 } 442/* else { 443 // copy 444 unsigned source_align = random() % 100; 445 unsigned dest_align = random() % 100; 446 unsigned offset2 = random() % size; 447 448 Addr source = ((base) ? baseAddr1 : baseAddr2) + offset; 449 Addr dest = ((base) ? baseAddr2 : baseAddr1) + offset2; 450 if (outstandingAddrs.find(source) != outstandingAddrs.end()) return; 451 else outstandingAddrs.insert(source); 452 if (outstandingAddrs.find(dest) != outstandingAddrs.end()) return; 453 else outstandingAddrs.insert(dest); 454 455 if (source_align >= percentSourceUnaligned) { 456 source = blockAddr(source); 457 } 458 if (dest_align >= percentDestUnaligned) { 459 dest = blockAddr(dest); 460 } 461 req->cmd = Copy; 462 req->flags &= ~UNCACHEABLE; 463 req->paddr = source; 464 req->dest = dest; 465 delete [] req->data; 466 req->data = new uint8_t[blockSize]; 467 req->size = blockSize; 468 if (source == traceBlockAddr || dest == traceBlockAddr) { 469 cerr << name() 470 << ": initiating copy of " 471 << dec << req->size << " bytes from addr 0x" 472 << hex << source 473 << " (0x" << hex << blockAddr(source) << ")" 474 << " to addr 0x" 475 << hex << dest 476 << " (0x" << hex << blockAddr(dest) << ")" 477 << " at cycle " 478 << dec << curTick << endl; 479 }* 480 cacheInterface->access(req); 481 uint8_t result[blockSize]; 482 checkMem->access(Read, source, &result, blockSize); 483 checkMem->access(Write, dest, &result, blockSize); 484 } 485*/ 486} 487 488void 489MemTest::doRetry() 490{ 491 if (cachePort.sendTiming(retryPkt)) { 492 accessRetry = false; 493 retryPkt = NULL; 494 } 495} 496 497BEGIN_DECLARE_SIM_OBJECT_PARAMS(MemTest) 498 499// SimObjectParam<BaseCache *> cache; 500// SimObjectParam<PhysicalMemory *> main_mem; 501// SimObjectParam<PhysicalMemory *> check_mem; 502 Param<unsigned> memory_size; 503 Param<unsigned> percent_reads; 504// Param<unsigned> percent_copies; 505 Param<unsigned> percent_uncacheable; 506 Param<unsigned> progress_interval; 507 Param<unsigned> percent_source_unaligned; 508 Param<unsigned> percent_dest_unaligned; 509 Param<Addr> trace_addr; 510 Param<Counter> max_loads; 511 Param<bool> atomic; 512 513END_DECLARE_SIM_OBJECT_PARAMS(MemTest) 514 515 516BEGIN_INIT_SIM_OBJECT_PARAMS(MemTest) 517 518// INIT_PARAM(cache, "L1 cache"), 519// INIT_PARAM(main_mem, "hierarchical memory"), 520// INIT_PARAM(check_mem, "check memory"), 521 INIT_PARAM(memory_size, "memory size"), 522 INIT_PARAM(percent_reads, "target read percentage"), 523// INIT_PARAM(percent_copies, "target copy percentage"), 524 INIT_PARAM(percent_uncacheable, "target uncacheable percentage"), 525 INIT_PARAM(progress_interval, "progress report interval (in accesses)"), 526 INIT_PARAM(percent_source_unaligned, 527 "percent of copy source address that are unaligned"), 528 INIT_PARAM(percent_dest_unaligned, 529 "percent of copy dest address that are unaligned"), 530 INIT_PARAM(trace_addr, "address to trace"), 531 INIT_PARAM(max_loads, "terminate when we have reached this load count"), 532 INIT_PARAM(atomic, "Is the tester testing atomic mode (or timing)") 533 534END_INIT_SIM_OBJECT_PARAMS(MemTest) 535 536 537CREATE_SIM_OBJECT(MemTest) 538{ 539 return new MemTest(getInstanceName(), /*cache->getInterface(),*/ /*main_mem,*/ 540 /*check_mem,*/ memory_size, percent_reads, /*percent_copies,*/ 541 percent_uncacheable, progress_interval, 542 percent_source_unaligned, percent_dest_unaligned, 543 trace_addr, max_loads, atomic); 544} 545 546REGISTER_SIM_OBJECT("MemTest", MemTest) 547