Merge ktlim@zamp:/z/ktlim2/clean/m5-o3
[gem5.git] / src / cpu / memtest / memtest.cc
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/cpu_exec_context.hh"
42 #include "cpu/memtest/memtest.hh"
43 #include "mem/cache/base_cache.hh"
44 #include "sim/builder.hh"
45 #include "sim/sim_events.hh"
46 #include "sim/stats.hh"
47
48 using namespace std;
49 using namespace TheISA;
50
51 int TESTER_ALLOCATOR=0;
52
53 MemTest::MemTest(const string &name,
54 MemInterface *_cache_interface,
55 FunctionalMemory *main_mem,
56 FunctionalMemory *check_mem,
57 unsigned _memorySize,
58 unsigned _percentReads,
59 unsigned _percentCopies,
60 unsigned _percentUncacheable,
61 unsigned _progressInterval,
62 unsigned _percentSourceUnaligned,
63 unsigned _percentDestUnaligned,
64 Addr _traceAddr,
65 Counter _max_loads)
66 : SimObject(name),
67 tickEvent(this),
68 cacheInterface(_cache_interface),
69 mainMem(main_mem),
70 checkMem(check_mem),
71 size(_memorySize),
72 percentReads(_percentReads),
73 percentCopies(_percentCopies),
74 percentUncacheable(_percentUncacheable),
75 progressInterval(_progressInterval),
76 nextProgressMessage(_progressInterval),
77 percentSourceUnaligned(_percentSourceUnaligned),
78 percentDestUnaligned(percentDestUnaligned),
79 maxLoads(_max_loads)
80 {
81 vector<string> cmd;
82 cmd.push_back("/bin/ls");
83 vector<string> null_vec;
84 cpuXC = new CPUExecContext(NULL, 0, mainMem, 0);
85
86 blockSize = cacheInterface->getBlockSize();
87 blockAddrMask = blockSize - 1;
88 traceBlockAddr = blockAddr(_traceAddr);
89
90 //setup data storage with interesting values
91 uint8_t *data1 = new uint8_t[size];
92 uint8_t *data2 = new uint8_t[size];
93 uint8_t *data3 = new uint8_t[size];
94 memset(data1, 1, size);
95 memset(data2, 2, size);
96 memset(data3, 3, size);
97 curTick = 0;
98
99 baseAddr1 = 0x100000;
100 baseAddr2 = 0x400000;
101 uncacheAddr = 0x800000;
102
103 // set up intial memory contents here
104 mainMem->prot_write(baseAddr1, data1, size);
105 checkMem->prot_write(baseAddr1, data1, size);
106 mainMem->prot_write(baseAddr2, data2, size);
107 checkMem->prot_write(baseAddr2, data2, size);
108 mainMem->prot_write(uncacheAddr, data3, size);
109 checkMem->prot_write(uncacheAddr, data3, size);
110
111 delete [] data1;
112 delete [] data2;
113 delete [] data3;
114
115 // set up counters
116 noResponseCycles = 0;
117 numReads = 0;
118 tickEvent.schedule(0);
119
120 id = TESTER_ALLOCATOR++;
121 }
122
123 static void
124 printData(ostream &os, uint8_t *data, int nbytes)
125 {
126 os << hex << setfill('0');
127 // assume little-endian: print bytes from highest address to lowest
128 for (uint8_t *dp = data + nbytes - 1; dp >= data; --dp) {
129 os << setw(2) << (unsigned)*dp;
130 }
131 os << dec;
132 }
133
134 void
135 MemTest::completeRequest(MemReqPtr &req, uint8_t *data)
136 {
137 //Remove the address from the list of outstanding
138 std::set<unsigned>::iterator removeAddr = outstandingAddrs.find(req->paddr);
139 assert(removeAddr != outstandingAddrs.end());
140 outstandingAddrs.erase(removeAddr);
141
142 switch (req->cmd) {
143 case Read:
144 if (memcmp(req->data, data, req->size) != 0) {
145 cerr << name() << ": on read of 0x" << hex << req->paddr
146 << " (0x" << hex << blockAddr(req->paddr) << ")"
147 << "@ cycle " << dec << curTick
148 << ", cache returns 0x";
149 printData(cerr, req->data, req->size);
150 cerr << ", expected 0x";
151 printData(cerr, data, req->size);
152 cerr << endl;
153 fatal("");
154 }
155
156 numReads++;
157 numReadsStat++;
158
159 if (numReads == nextProgressMessage) {
160 ccprintf(cerr, "%s: completed %d read accesses @%d\n",
161 name(), numReads, curTick);
162 nextProgressMessage += progressInterval;
163 }
164
165 if (numReads >= maxLoads)
166 SimExit(curTick, "Maximum number of loads reached!");
167 break;
168
169 case Write:
170 numWritesStat++;
171 break;
172
173 case Copy:
174 //Also remove dest from outstanding list
175 removeAddr = outstandingAddrs.find(req->dest);
176 assert(removeAddr != outstandingAddrs.end());
177 outstandingAddrs.erase(removeAddr);
178 numCopiesStat++;
179 break;
180
181 default:
182 panic("invalid command");
183 }
184
185 if (blockAddr(req->paddr) == traceBlockAddr) {
186 cerr << name() << ": completed "
187 << (req->cmd.isWrite() ? "write" : "read")
188 << " access of "
189 << dec << req->size << " bytes at address 0x"
190 << hex << req->paddr
191 << " (0x" << hex << blockAddr(req->paddr) << ")"
192 << ", value = 0x";
193 printData(cerr, req->data, req->size);
194 cerr << " @ cycle " << dec << curTick;
195
196 cerr << endl;
197 }
198
199 noResponseCycles = 0;
200 delete [] data;
201 }
202
203
204 void
205 MemTest::regStats()
206 {
207 using namespace Stats;
208
209
210 numReadsStat
211 .name(name() + ".num_reads")
212 .desc("number of read accesses completed")
213 ;
214
215 numWritesStat
216 .name(name() + ".num_writes")
217 .desc("number of write accesses completed")
218 ;
219
220 numCopiesStat
221 .name(name() + ".num_copies")
222 .desc("number of copy accesses completed")
223 ;
224 }
225
226 void
227 MemTest::tick()
228 {
229 if (!tickEvent.scheduled())
230 tickEvent.schedule(curTick + cycles(1));
231
232 if (++noResponseCycles >= 500000) {
233 cerr << name() << ": deadlocked at cycle " << curTick << endl;
234 fatal("");
235 }
236
237 if (cacheInterface->isBlocked()) {
238 return;
239 }
240
241 //make new request
242 unsigned cmd = random() % 100;
243 unsigned offset = random() % size;
244 unsigned base = random() % 2;
245 uint64_t data = random();
246 unsigned access_size = random() % 4;
247 unsigned cacheable = random() % 100;
248
249 //If we aren't doing copies, use id as offset, and do a false sharing
250 //mem tester
251 if (percentCopies == 0) {
252 //We can eliminate the lower bits of the offset, and then use the id
253 //to offset within the blks
254 offset &= ~63; //Not the low order bits
255 offset += id;
256 access_size = 0;
257 }
258
259 MemReqPtr req = new MemReq();
260
261 if (cacheable < percentUncacheable) {
262 req->flags |= UNCACHEABLE;
263 req->paddr = uncacheAddr + offset;
264 } else {
265 req->paddr = ((base) ? baseAddr1 : baseAddr2) + offset;
266 }
267 // bool probe = (random() % 2 == 1) && !req->isUncacheable();
268 bool probe = false;
269
270 req->size = 1 << access_size;
271 req->data = new uint8_t[req->size];
272 req->paddr &= ~(req->size - 1);
273 req->time = curTick;
274 req->xc = cpuXC->getProxy();
275
276 if (cmd < percentReads) {
277 // read
278
279 //For now we only allow one outstanding request per addreess per tester
280 //This means we assume CPU does write forwarding to reads that alias something
281 //in the cpu store buffer.
282 if (outstandingAddrs.find(req->paddr) != outstandingAddrs.end()) return;
283 else outstandingAddrs.insert(req->paddr);
284
285 req->cmd = Read;
286 uint8_t *result = new uint8_t[8];
287 checkMem->access(Read, req->paddr, result, req->size);
288 if (blockAddr(req->paddr) == traceBlockAddr) {
289 cerr << name()
290 << ": initiating read "
291 << ((probe) ? "probe of " : "access of ")
292 << dec << req->size << " bytes from addr 0x"
293 << hex << req->paddr
294 << " (0x" << hex << blockAddr(req->paddr) << ")"
295 << " at cycle "
296 << dec << curTick << endl;
297 }
298 if (probe) {
299 cacheInterface->probeAndUpdate(req);
300 completeRequest(req, result);
301 } else {
302 req->completionEvent = new MemCompleteEvent(req, result, this);
303 cacheInterface->access(req);
304 }
305 } else if (cmd < (100 - percentCopies)){
306 // write
307
308 //For now we only allow one outstanding request per addreess per tester
309 //This means we assume CPU does write forwarding to reads that alias something
310 //in the cpu store buffer.
311 if (outstandingAddrs.find(req->paddr) != outstandingAddrs.end()) return;
312 else outstandingAddrs.insert(req->paddr);
313
314 req->cmd = Write;
315 memcpy(req->data, &data, req->size);
316 checkMem->access(Write, req->paddr, req->data, req->size);
317 if (blockAddr(req->paddr) == traceBlockAddr) {
318 cerr << name() << ": initiating write "
319 << ((probe)?"probe of ":"access of ")
320 << dec << req->size << " bytes (value = 0x";
321 printData(cerr, req->data, req->size);
322 cerr << ") to addr 0x"
323 << hex << req->paddr
324 << " (0x" << hex << blockAddr(req->paddr) << ")"
325 << " at cycle "
326 << dec << curTick << endl;
327 }
328 if (probe) {
329 cacheInterface->probeAndUpdate(req);
330 completeRequest(req, NULL);
331 } else {
332 req->completionEvent = new MemCompleteEvent(req, NULL, this);
333 cacheInterface->access(req);
334 }
335 } else {
336 // copy
337 unsigned source_align = random() % 100;
338 unsigned dest_align = random() % 100;
339 unsigned offset2 = random() % size;
340
341 Addr source = ((base) ? baseAddr1 : baseAddr2) + offset;
342 Addr dest = ((base) ? baseAddr2 : baseAddr1) + offset2;
343 if (outstandingAddrs.find(source) != outstandingAddrs.end()) return;
344 else outstandingAddrs.insert(source);
345 if (outstandingAddrs.find(dest) != outstandingAddrs.end()) return;
346 else outstandingAddrs.insert(dest);
347
348 if (source_align >= percentSourceUnaligned) {
349 source = blockAddr(source);
350 }
351 if (dest_align >= percentDestUnaligned) {
352 dest = blockAddr(dest);
353 }
354 req->cmd = Copy;
355 req->flags &= ~UNCACHEABLE;
356 req->paddr = source;
357 req->dest = dest;
358 delete [] req->data;
359 req->data = new uint8_t[blockSize];
360 req->size = blockSize;
361 if (source == traceBlockAddr || dest == traceBlockAddr) {
362 cerr << name()
363 << ": initiating copy of "
364 << dec << req->size << " bytes from addr 0x"
365 << hex << source
366 << " (0x" << hex << blockAddr(source) << ")"
367 << " to addr 0x"
368 << hex << dest
369 << " (0x" << hex << blockAddr(dest) << ")"
370 << " at cycle "
371 << dec << curTick << endl;
372 }
373 cacheInterface->access(req);
374 uint8_t result[blockSize];
375 checkMem->access(Read, source, &result, blockSize);
376 checkMem->access(Write, dest, &result, blockSize);
377 }
378 }
379
380
381 void
382 MemCompleteEvent::process()
383 {
384 tester->completeRequest(req, data);
385 delete this;
386 }
387
388
389 const char *
390 MemCompleteEvent::description()
391 {
392 return "memory access completion";
393 }
394
395
396 BEGIN_DECLARE_SIM_OBJECT_PARAMS(MemTest)
397
398 SimObjectParam<BaseCache *> cache;
399 SimObjectParam<FunctionalMemory *> main_mem;
400 SimObjectParam<FunctionalMemory *> check_mem;
401 Param<unsigned> memory_size;
402 Param<unsigned> percent_reads;
403 Param<unsigned> percent_copies;
404 Param<unsigned> percent_uncacheable;
405 Param<unsigned> progress_interval;
406 Param<unsigned> percent_source_unaligned;
407 Param<unsigned> percent_dest_unaligned;
408 Param<Addr> trace_addr;
409 Param<Counter> max_loads;
410
411 END_DECLARE_SIM_OBJECT_PARAMS(MemTest)
412
413
414 BEGIN_INIT_SIM_OBJECT_PARAMS(MemTest)
415
416 INIT_PARAM(cache, "L1 cache"),
417 INIT_PARAM(main_mem, "hierarchical memory"),
418 INIT_PARAM(check_mem, "check memory"),
419 INIT_PARAM(memory_size, "memory size"),
420 INIT_PARAM(percent_reads, "target read percentage"),
421 INIT_PARAM(percent_copies, "target copy percentage"),
422 INIT_PARAM(percent_uncacheable, "target uncacheable percentage"),
423 INIT_PARAM(progress_interval, "progress report interval (in accesses)"),
424 INIT_PARAM(percent_source_unaligned,
425 "percent of copy source address that are unaligned"),
426 INIT_PARAM(percent_dest_unaligned,
427 "percent of copy dest address that are unaligned"),
428 INIT_PARAM(trace_addr, "address to trace"),
429 INIT_PARAM(max_loads, "terminate when we have reached this load count")
430
431 END_INIT_SIM_OBJECT_PARAMS(MemTest)
432
433
434 CREATE_SIM_OBJECT(MemTest)
435 {
436 return new MemTest(getInstanceName(), cache->getInterface(), main_mem,
437 check_mem, memory_size, percent_reads, percent_copies,
438 percent_uncacheable, progress_interval,
439 percent_source_unaligned, percent_dest_unaligned,
440 trace_addr, max_loads);
441 }
442
443 REGISTER_SIM_OBJECT("MemTest", MemTest)