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