memtest.cc (10653:e3fc6bc7f97e) | memtest.cc (10688:22452667fd5c) |
---|---|
1/* | 1/* |
2 * Copyright (c) 2015 ARM Limited 3 * All rights reserved 4 * 5 * The license below extends only to copyright in the software and shall 6 * not be construed as granting a license to any other intellectual 7 * property including but not limited to intellectual property relating 8 * to a hardware implementation of the functionality of the software 9 * licensed hereunder. You may use the software subject to the license 10 * terms below provided that you ensure that this notice is replicated 11 * unmodified and in its entirety in all distributions of the software, 12 * modified or unmodified, in source code or in binary form. 13 * |
|
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 --- 12 unchanged lines hidden (view full) --- 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 | 14 * Copyright (c) 2002-2005 The Regents of The University of Michigan 15 * All rights reserved. 16 * 17 * Redistribution and use in source and binary forms, with or without 18 * modification, are permitted provided that the following conditions are 19 * met: redistributions of source code must retain the above copyright 20 * notice, this list of conditions and the following disclaimer; 21 * redistributions in binary form must reproduce the above copyright --- 12 unchanged lines hidden (view full) --- 34 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 35 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 36 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 37 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 38 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 39 * 40 * Authors: Erik Hallnor 41 * Steve Reinhardt |
42 * Andreas Hansson |
|
30 */ 31 | 43 */ 44 |
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/random.hh" 41#include "base/statistics.hh" 42#include "cpu/testers/memtest/memtest.hh" 43#include "debug/MemTest.hh" 44#include "mem/mem_object.hh" | 45#include "base/random.hh" 46#include "base/statistics.hh" 47#include "cpu/testers/memtest/memtest.hh" 48#include "debug/MemTest.hh" 49#include "mem/mem_object.hh" |
45#include "mem/packet.hh" 46#include "mem/port.hh" 47#include "mem/request.hh" 48#include "sim/sim_events.hh" | 50#include "sim/sim_exit.hh" |
49#include "sim/stats.hh" 50#include "sim/system.hh" 51 52using namespace std; 53 | 51#include "sim/stats.hh" 52#include "sim/system.hh" 53 54using namespace std; 55 |
54int TESTER_ALLOCATOR=0; | 56unsigned int TESTER_ALLOCATOR = 0; |
55 56bool 57MemTest::CpuPort::recvTimingResp(PacketPtr pkt) 58{ | 57 58bool 59MemTest::CpuPort::recvTimingResp(PacketPtr pkt) 60{ |
59 memtest->completeRequest(pkt); | 61 memtest.completeRequest(pkt); |
60 return true; 61} 62 63void 64MemTest::CpuPort::recvRetry() 65{ | 62 return true; 63} 64 65void 66MemTest::CpuPort::recvRetry() 67{ |
66 memtest->doRetry(); | 68 memtest.recvRetry(); |
67} 68 | 69} 70 |
69void | 71bool |
70MemTest::sendPkt(PacketPtr pkt) { 71 if (atomic) { | 72MemTest::sendPkt(PacketPtr pkt) { 73 if (atomic) { |
72 cachePort.sendAtomic(pkt); | 74 port.sendAtomic(pkt); |
73 completeRequest(pkt); | 75 completeRequest(pkt); |
74 } 75 else if (!cachePort.sendTimingReq(pkt)) { 76 DPRINTF(MemTest, "accessRetry setting to true\n"); 77 78 // 79 // dma requests should never be retried 80 // 81 if (issueDmas) { 82 panic("Nacked DMA requests are not supported\n"); 83 } 84 accessRetry = true; 85 retryPkt = pkt; | |
86 } else { | 76 } else { |
87 if (issueDmas) { 88 dmaOutstanding = true; | 77 if (!port.sendTimingReq(pkt)) { 78 retryPkt = pkt; 79 return false; |
89 } 90 } | 80 } 81 } |
91 | 82 return true; |
92} 93 94MemTest::MemTest(const Params *p) 95 : MemObject(p), 96 tickEvent(this), | 83} 84 85MemTest::MemTest(const Params *p) 86 : MemObject(p), 87 tickEvent(this), |
97 cachePort("test", this), 98 funcPort("functional", this), 99 funcProxy(funcPort, p->sys->cacheLineSize()), 100 retryPkt(NULL), 101// mainMem(main_mem), 102// checkMem(check_mem), 103 size(p->memory_size), | 88 noRequestEvent(this), 89 noResponseEvent(this), 90 port("port", *this), 91 retryPkt(nullptr), 92 size(p->size), 93 interval(p->interval), |
104 percentReads(p->percent_reads), 105 percentFunctional(p->percent_functional), 106 percentUncacheable(p->percent_uncacheable), | 94 percentReads(p->percent_reads), 95 percentFunctional(p->percent_functional), 96 percentUncacheable(p->percent_uncacheable), |
107 issueDmas(p->issue_dmas), 108 masterId(p->sys->getMasterId(name())), 109 blockSize(p->sys->cacheLineSize()), | 97 masterId(p->system->getMasterId(name())), 98 blockSize(p->system->cacheLineSize()), 99 blockAddrMask(blockSize - 1), |
110 progressInterval(p->progress_interval), | 100 progressInterval(p->progress_interval), |
101 progressCheck(p->progress_check), |
|
111 nextProgressMessage(p->progress_interval), | 102 nextProgressMessage(p->progress_interval), |
112 percentSourceUnaligned(p->percent_source_unaligned), 113 percentDestUnaligned(p->percent_dest_unaligned), | |
114 maxLoads(p->max_loads), | 103 maxLoads(p->max_loads), |
115 atomic(p->atomic), 116 suppress_func_warnings(p->suppress_func_warnings) | 104 atomic(p->system->isAtomicMode()), 105 suppressFuncWarnings(p->suppress_func_warnings) |
117{ 118 id = TESTER_ALLOCATOR++; | 106{ 107 id = TESTER_ALLOCATOR++; |
108 fatal_if(id >= blockSize, "Too many testers, only %d allowed\n", 109 blockSize - 1); |
|
119 | 110 |
120 // Needs to be masked off once we know the block size. 121 traceBlockAddr = p->trace_addr; | |
122 baseAddr1 = 0x100000; 123 baseAddr2 = 0x400000; 124 uncacheAddr = 0x800000; 125 | 111 baseAddr1 = 0x100000; 112 baseAddr2 = 0x400000; 113 uncacheAddr = 0x800000; 114 |
126 blockAddrMask = blockSize - 1; 127 traceBlockAddr = blockAddr(traceBlockAddr); 128 | |
129 // set up counters | 115 // set up counters |
130 noResponseCycles = 0; | |
131 numReads = 0; 132 numWrites = 0; | 116 numReads = 0; 117 numWrites = 0; |
133 schedule(tickEvent, 0); | |
134 | 118 |
135 accessRetry = false; 136 dmaOutstanding = false; | 119 // kick things into action 120 schedule(tickEvent, curTick()); 121 schedule(noRequestEvent, clockEdge(progressCheck)); 122 schedule(noResponseEvent, clockEdge(progressCheck)); |
137} 138 139BaseMasterPort & 140MemTest::getMasterPort(const std::string &if_name, PortID idx) 141{ | 123} 124 125BaseMasterPort & 126MemTest::getMasterPort(const std::string &if_name, PortID idx) 127{ |
142 if (if_name == "functional") 143 return funcPort; 144 else if (if_name == "test") 145 return cachePort; | 128 if (if_name == "port") 129 return port; |
146 else 147 return MemObject::getMasterPort(if_name, idx); 148} 149 150void | 130 else 131 return MemObject::getMasterPort(if_name, idx); 132} 133 134void |
151MemTest::init() | 135MemTest::completeRequest(PacketPtr pkt, bool functional) |
152{ | 136{ |
153 // initial memory contents for both physical memory and functional 154 // memory should be 0; no need to initialize them. 155} 156 157 158void 159MemTest::completeRequest(PacketPtr pkt) 160{ | |
161 Request *req = pkt->req; | 137 Request *req = pkt->req; |
138 assert(req->getSize() == 1); |
|
162 | 139 |
163 if (issueDmas) { 164 dmaOutstanding = false; 165 } | 140 // this address is no longer outstanding 141 auto remove_addr = outstandingAddrs.find(req->getPaddr()); 142 assert(remove_addr != outstandingAddrs.end()); 143 outstandingAddrs.erase(remove_addr); |
166 | 144 |
167 DPRINTF(MemTest, "completing %s at address %x (blk %x) %s\n", | 145 DPRINTF(MemTest, "Completing %s at address %x (blk %x) %s\n", |
168 pkt->isWrite() ? "write" : "read", | 146 pkt->isWrite() ? "write" : "read", |
169 req->getPaddr(), blockAddr(req->getPaddr()), | 147 req->getPaddr(), blockAlign(req->getPaddr()), |
170 pkt->isError() ? "error" : "success"); 171 | 148 pkt->isError() ? "error" : "success"); 149 |
172 MemTestSenderState *state = 173 safe_cast<MemTestSenderState *>(pkt->senderState); | 150 const uint8_t *pkt_data = pkt->getConstPtr<uint8_t>(); |
174 | 151 |
175 uint8_t *data = state->data; 176 // @todo: This should really be a const pointer 177 uint8_t *pkt_data = pkt->getPtr<uint8_t>(); 178 179 //Remove the address from the list of outstanding 180 std::set<unsigned>::iterator removeAddr = 181 outstandingAddrs.find(req->getPaddr()); 182 assert(removeAddr != outstandingAddrs.end()); 183 outstandingAddrs.erase(removeAddr); 184 | |
185 if (pkt->isError()) { | 152 if (pkt->isError()) { |
186 if (!suppress_func_warnings) { 187 warn("Functional %s access failed at %#x\n", 188 pkt->isWrite() ? "write" : "read", req->getPaddr()); | 153 if (!functional || !suppressFuncWarnings) { 154 warn("%s access failed at %#x\n", 155 pkt->isWrite() ? "Write" : "Read", req->getPaddr()); |
189 } 190 } else { 191 if (pkt->isRead()) { | 156 } 157 } else { 158 if (pkt->isRead()) { |
192 if (memcmp(pkt_data, data, pkt->getSize()) != 0) { | 159 uint8_t ref_data = referenceData[req->getPaddr()]; 160 if (pkt_data[0] != ref_data) { |
193 panic("%s: read of %x (blk %x) @ cycle %d " 194 "returns %x, expected %x\n", name(), | 161 panic("%s: read of %x (blk %x) @ cycle %d " 162 "returns %x, expected %x\n", name(), |
195 req->getPaddr(), blockAddr(req->getPaddr()), curTick(), 196 *pkt_data, *data); | 163 req->getPaddr(), blockAlign(req->getPaddr()), curTick(), 164 pkt_data[0], ref_data); |
197 } 198 199 numReads++; 200 numReadsStat++; 201 202 if (numReads == (uint64_t)nextProgressMessage) { 203 ccprintf(cerr, "%s: completed %d read, %d write accesses @%d\n", 204 name(), numReads, numWrites, curTick()); 205 nextProgressMessage += progressInterval; 206 } 207 208 if (maxLoads != 0 && numReads >= maxLoads) 209 exitSimLoop("maximum number of loads reached"); 210 } else { 211 assert(pkt->isWrite()); | 165 } 166 167 numReads++; 168 numReadsStat++; 169 170 if (numReads == (uint64_t)nextProgressMessage) { 171 ccprintf(cerr, "%s: completed %d read, %d write accesses @%d\n", 172 name(), numReads, numWrites, curTick()); 173 nextProgressMessage += progressInterval; 174 } 175 176 if (maxLoads != 0 && numReads >= maxLoads) 177 exitSimLoop("maximum number of loads reached"); 178 } else { 179 assert(pkt->isWrite()); |
212 funcProxy.writeBlob(req->getPaddr(), pkt_data, req->getSize()); | 180 181 // update the reference data 182 referenceData[req->getPaddr()] = pkt_data[0]; |
213 numWrites++; 214 numWritesStat++; 215 } 216 } 217 | 183 numWrites++; 184 numWritesStat++; 185 } 186 } 187 |
218 noResponseCycles = 0; 219 delete state; 220 delete [] data; | |
221 delete pkt->req; | 188 delete pkt->req; |
189 190 // the packet will delete the data |
|
222 delete pkt; | 191 delete pkt; |
192 193 // finally shift the response timeout forward 194 reschedule(noResponseEvent, clockEdge(progressCheck), true); |
|
223} 224 225void 226MemTest::regStats() 227{ 228 using namespace Stats; 229 230 numReadsStat 231 .name(name() + ".num_reads") 232 .desc("number of read accesses completed") 233 ; 234 235 numWritesStat 236 .name(name() + ".num_writes") 237 .desc("number of write accesses completed") 238 ; | 195} 196 197void 198MemTest::regStats() 199{ 200 using namespace Stats; 201 202 numReadsStat 203 .name(name() + ".num_reads") 204 .desc("number of read accesses completed") 205 ; 206 207 numWritesStat 208 .name(name() + ".num_writes") 209 .desc("number of write accesses completed") 210 ; |
239 240 numCopiesStat 241 .name(name() + ".num_copies") 242 .desc("number of copy accesses completed") 243 ; | |
244} 245 246void 247MemTest::tick() 248{ | 211} 212 213void 214MemTest::tick() 215{ |
249 if (!tickEvent.scheduled()) 250 schedule(tickEvent, clockEdge(Cycles(1))); | 216 // we should never tick if we are waiting for a retry 217 assert(!retryPkt); |
251 | 218 |
252 if (++noResponseCycles >= 500000) { 253 if (issueDmas) { 254 cerr << "DMA tester "; 255 } 256 cerr << name() << ": deadlocked at cycle " << curTick() << endl; 257 fatal(""); 258 } 259 260 if (accessRetry || (issueDmas && dmaOutstanding)) { 261 DPRINTF(MemTest, "MemTester waiting on accessRetry or DMA response\n"); 262 return; 263 } 264 265 //make new request | 219 // create a new request |
266 unsigned cmd = random_mt.random(0, 100); | 220 unsigned cmd = random_mt.random(0, 100); |
267 unsigned offset = random_mt.random<unsigned>(0, size - 1); 268 unsigned base = random_mt.random(0, 1); 269 uint64_t data = random_mt.random<uint64_t>(); 270 unsigned access_size = random_mt.random(0, 3); | 221 uint8_t data = random_mt.random<uint8_t>(); |
271 bool uncacheable = random_mt.random(0, 100) < percentUncacheable; | 222 bool uncacheable = random_mt.random(0, 100) < percentUncacheable; |
272 273 unsigned dma_access_size = random_mt.random(0, 3); 274 275 //If we aren't doing copies, use id as offset, and do a false sharing 276 //mem tester 277 //We can eliminate the lower bits of the offset, and then use the id 278 //to offset within the blks 279 offset = blockAddr(offset); 280 offset += id; 281 access_size = 0; 282 dma_access_size = 0; 283 | 223 unsigned base = random_mt.random(0, 1); |
284 Request::Flags flags; 285 Addr paddr; 286 | 224 Request::Flags flags; 225 Addr paddr; 226 |
287 if (uncacheable) { 288 flags.set(Request::UNCACHEABLE); 289 paddr = uncacheAddr + offset; 290 } else { 291 paddr = ((base) ? baseAddr1 : baseAddr2) + offset; 292 } | 227 // generate a unique address 228 do { 229 unsigned offset = random_mt.random<unsigned>(0, size - 1); |
293 | 230 |
294 // For now we only allow one outstanding request per address 295 // per tester This means we assume CPU does write forwarding 296 // to reads that alias something in the cpu store buffer. 297 if (outstandingAddrs.find(paddr) != outstandingAddrs.end()) { 298 return; 299 } | 231 // use the tester id as offset within the block for false sharing 232 offset = blockAlign(offset); 233 offset += id; |
300 | 234 |
235 if (uncacheable) { 236 flags.set(Request::UNCACHEABLE); 237 paddr = uncacheAddr + offset; 238 } else { 239 paddr = ((base) ? baseAddr1 : baseAddr2) + offset; 240 } 241 } while (outstandingAddrs.find(paddr) != outstandingAddrs.end()); 242 |
|
301 bool do_functional = (random_mt.random(0, 100) < percentFunctional) && 302 !uncacheable; | 243 bool do_functional = (random_mt.random(0, 100) < percentFunctional) && 244 !uncacheable; |
303 Request *req = nullptr; 304 uint8_t *result = new uint8_t[8]; | 245 Request *req = new Request(paddr, 1, flags, masterId); 246 req->setThreadContext(id, 0); |
305 | 247 |
306 if (issueDmas) { 307 paddr &= ~((1 << dma_access_size) - 1); 308 req = new Request(paddr, 1 << dma_access_size, flags, masterId); 309 req->setThreadContext(id,0); 310 } else { 311 paddr &= ~((1 << access_size) - 1); 312 req = new Request(paddr, 1 << access_size, flags, masterId); 313 req->setThreadContext(id,0); 314 } 315 assert(req->getSize() == 1); | 248 outstandingAddrs.insert(paddr); |
316 | 249 |
317 if (cmd < percentReads) { 318 // read 319 outstandingAddrs.insert(paddr); | 250 // sanity check 251 panic_if(outstandingAddrs.size() > 100, 252 "Tester %s has more than 100 outstanding requests\n", name()); |
320 | 253 |
321 // ***** NOTE FOR RON: I'm not sure how to access checkMem. - Kevin 322 funcProxy.readBlob(req->getPaddr(), result, req->getSize()); | 254 PacketPtr pkt = nullptr; 255 uint8_t *pkt_data = new uint8_t[1]; |
323 | 256 |
324 DPRINTF(MemTest, 325 "id %d initiating %sread at addr %x (blk %x) expecting %x\n", 326 id, do_functional ? "functional " : "", req->getPaddr(), 327 blockAddr(req->getPaddr()), *result); 328 329 PacketPtr pkt = new Packet(req, MemCmd::ReadReq); 330 pkt->dataDynamic(new uint8_t[req->getSize()]); 331 MemTestSenderState *state = new MemTestSenderState(result); 332 pkt->senderState = state; 333 334 if (do_functional) { 335 assert(pkt->needsResponse()); 336 pkt->setSuppressFuncError(); 337 cachePort.sendFunctional(pkt); 338 completeRequest(pkt); | 257 if (cmd < percentReads) { 258 // start by ensuring there is a reference value if we have not 259 // seen this address before 260 uint8_t M5_VAR_USED ref_data = 0; 261 auto ref = referenceData.find(req->getPaddr()); 262 if (ref == referenceData.end()) { 263 referenceData[req->getPaddr()] = 0; |
339 } else { | 264 } else { |
340 sendPkt(pkt); | 265 ref_data = ref->second; |
341 } | 266 } |
342 } else { 343 // write 344 outstandingAddrs.insert(paddr); | |
345 | 267 |
346 DPRINTF(MemTest, "initiating %swrite at addr %x (blk %x) value %x\n", | 268 DPRINTF(MemTest, 269 "Initiating %sread at addr %x (blk %x) expecting %x\n", |
347 do_functional ? "functional " : "", req->getPaddr(), | 270 do_functional ? "functional " : "", req->getPaddr(), |
348 blockAddr(req->getPaddr()), data & 0xff); | 271 blockAlign(req->getPaddr()), ref_data); |
349 | 272 |
350 PacketPtr pkt = new Packet(req, MemCmd::WriteReq); 351 uint8_t *pkt_data = new uint8_t[req->getSize()]; | 273 pkt = new Packet(req, MemCmd::ReadReq); |
352 pkt->dataDynamic(pkt_data); | 274 pkt->dataDynamic(pkt_data); |
353 memcpy(pkt_data, &data, req->getSize()); 354 MemTestSenderState *state = new MemTestSenderState(result); 355 pkt->senderState = state; | 275 } else { 276 DPRINTF(MemTest, "Initiating %swrite at addr %x (blk %x) value %x\n", 277 do_functional ? "functional " : "", req->getPaddr(), 278 blockAlign(req->getPaddr()), data); |
356 | 279 |
357 if (do_functional) { 358 pkt->setSuppressFuncError(); 359 cachePort.sendFunctional(pkt); 360 completeRequest(pkt); 361 } else { 362 sendPkt(pkt); 363 } | 280 pkt = new Packet(req, MemCmd::WriteReq); 281 pkt->dataDynamic(pkt_data); 282 pkt_data[0] = data; |
364 } | 283 } |
284 285 // there is no point in ticking if we are waiting for a retry 286 bool keep_ticking = true; 287 if (do_functional) { 288 pkt->setSuppressFuncError(); 289 port.sendFunctional(pkt); 290 completeRequest(pkt, true); 291 } else { 292 keep_ticking = sendPkt(pkt); 293 } 294 295 if (keep_ticking) { 296 // schedule the next tick 297 schedule(tickEvent, clockEdge(interval)); 298 299 // finally shift the timeout for sending of requests forwards 300 // as we have successfully sent a packet 301 reschedule(noRequestEvent, clockEdge(progressCheck), true); 302 } else { 303 DPRINTF(MemTest, "Waiting for retry\n"); 304 } |
|
365} 366 367void | 305} 306 307void |
368MemTest::doRetry() | 308MemTest::noRequest() |
369{ | 309{ |
370 if (cachePort.sendTimingReq(retryPkt)) { 371 DPRINTF(MemTest, "accessRetry setting to false\n"); 372 accessRetry = false; 373 retryPkt = NULL; 374 } | 310 panic("%s did not send a request for %d cycles", name(), progressCheck); |
375} 376 | 311} 312 |
377 | |
378void | 313void |
379MemTest::printAddr(Addr a) | 314MemTest::noResponse() |
380{ | 315{ |
381 cachePort.printAddr(a); | 316 panic("%s did not see a response for %d cycles", name(), progressCheck); |
382} 383 | 317} 318 |
319void 320MemTest::recvRetry() 321{ 322 assert(retryPkt); 323 if (port.sendTimingReq(retryPkt)) { 324 DPRINTF(MemTest, "Proceeding after successful retry\n"); |
|
384 | 325 |
326 retryPkt = nullptr; 327 // kick things into action again 328 schedule(tickEvent, clockEdge(interval)); 329 } 330} 331 |
|
385MemTest * 386MemTestParams::create() 387{ 388 return new MemTest(this); 389} | 332MemTest * 333MemTestParams::create() 334{ 335 return new MemTest(this); 336} |