2 * Copyright (c) 2002-2005 The Regents of The University of Michigan
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.
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.
28 * Authors: Erik Hallnor
32 // FIX ME: make trackBlkAddr use blocksize from actual cache, not hard coded
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"
49 using namespace TheISA
;
51 int TESTER_ALLOCATOR
=0;
53 MemTest::MemTest(const string
&name
,
54 MemInterface
*_cache_interface
,
55 FunctionalMemory
*main_mem
,
56 FunctionalMemory
*check_mem
,
58 unsigned _percentReads
,
59 unsigned _percentCopies
,
60 unsigned _percentUncacheable
,
61 unsigned _progressInterval
,
62 unsigned _percentSourceUnaligned
,
63 unsigned _percentDestUnaligned
,
68 cacheInterface(_cache_interface
),
72 percentReads(_percentReads
),
73 percentCopies(_percentCopies
),
74 percentUncacheable(_percentUncacheable
),
75 progressInterval(_progressInterval
),
76 nextProgressMessage(_progressInterval
),
77 percentSourceUnaligned(_percentSourceUnaligned
),
78 percentDestUnaligned(percentDestUnaligned
),
82 cmd
.push_back("/bin/ls");
83 vector
<string
> null_vec
;
84 cpuXC
= new CPUExecContext(NULL
, 0, mainMem
, 0);
86 blockSize
= cacheInterface
->getBlockSize();
87 blockAddrMask
= blockSize
- 1;
88 traceBlockAddr
= blockAddr(_traceAddr
);
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
);
100 baseAddr2
= 0x400000;
101 uncacheAddr
= 0x800000;
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
);
116 noResponseCycles
= 0;
118 tickEvent
.schedule(0);
120 id
= TESTER_ALLOCATOR
++;
124 printData(ostream
&os
, uint8_t *data
, int nbytes
)
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
;
135 MemTest::completeRequest(MemReqPtr
&req
, uint8_t *data
)
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
);
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
);
159 if (numReads
== nextProgressMessage
) {
160 ccprintf(cerr
, "%s: completed %d read accesses @%d\n",
161 name(), numReads
, curTick
);
162 nextProgressMessage
+= progressInterval
;
165 if (numReads
>= maxLoads
)
166 SimExit(curTick
, "Maximum number of loads reached!");
174 //Also remove dest from outstanding list
175 removeAddr
= outstandingAddrs
.find(req
->dest
);
176 assert(removeAddr
!= outstandingAddrs
.end());
177 outstandingAddrs
.erase(removeAddr
);
182 panic("invalid command");
185 if (blockAddr(req
->paddr
) == traceBlockAddr
) {
186 cerr
<< name() << ": completed "
187 << (req
->cmd
.isWrite() ? "write" : "read")
189 << dec
<< req
->size
<< " bytes at address 0x"
191 << " (0x" << hex
<< blockAddr(req
->paddr
) << ")"
193 printData(cerr
, req
->data
, req
->size
);
194 cerr
<< " @ cycle " << dec
<< curTick
;
199 noResponseCycles
= 0;
207 using namespace Stats
;
211 .name(name() + ".num_reads")
212 .desc("number of read accesses completed")
216 .name(name() + ".num_writes")
217 .desc("number of write accesses completed")
221 .name(name() + ".num_copies")
222 .desc("number of copy accesses completed")
229 if (!tickEvent
.scheduled())
230 tickEvent
.schedule(curTick
+ cycles(1));
232 if (++noResponseCycles
>= 500000) {
233 cerr
<< name() << ": deadlocked at cycle " << curTick
<< endl
;
237 if (cacheInterface
->isBlocked()) {
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;
249 //If we aren't doing copies, use id as offset, and do a false sharing
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
259 MemReqPtr req
= new MemReq();
261 if (cacheable
< percentUncacheable
) {
262 req
->flags
|= UNCACHEABLE
;
263 req
->paddr
= uncacheAddr
+ offset
;
265 req
->paddr
= ((base
) ? baseAddr1
: baseAddr2
) + offset
;
267 // bool probe = (random() % 2 == 1) && !req->isUncacheable();
270 req
->size
= 1 << access_size
;
271 req
->data
= new uint8_t[req
->size
];
272 req
->paddr
&= ~(req
->size
- 1);
274 req
->xc
= cpuXC
->getProxy();
276 if (cmd
< percentReads
) {
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
);
286 uint8_t *result
= new uint8_t[8];
287 checkMem
->access(Read
, req
->paddr
, result
, req
->size
);
288 if (blockAddr(req
->paddr
) == traceBlockAddr
) {
290 << ": initiating read "
291 << ((probe
) ? "probe of " : "access of ")
292 << dec
<< req
->size
<< " bytes from addr 0x"
294 << " (0x" << hex
<< blockAddr(req
->paddr
) << ")"
296 << dec
<< curTick
<< endl
;
299 cacheInterface
->probeAndUpdate(req
);
300 completeRequest(req
, result
);
302 req
->completionEvent
= new MemCompleteEvent(req
, result
, this);
303 cacheInterface
->access(req
);
305 } else if (cmd
< (100 - percentCopies
)){
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
);
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"
324 << " (0x" << hex
<< blockAddr(req
->paddr
) << ")"
326 << dec
<< curTick
<< endl
;
329 cacheInterface
->probeAndUpdate(req
);
330 completeRequest(req
, NULL
);
332 req
->completionEvent
= new MemCompleteEvent(req
, NULL
, this);
333 cacheInterface
->access(req
);
337 unsigned source_align
= random() % 100;
338 unsigned dest_align
= random() % 100;
339 unsigned offset2
= random() % size
;
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
);
348 if (source_align
>= percentSourceUnaligned
) {
349 source
= blockAddr(source
);
351 if (dest_align
>= percentDestUnaligned
) {
352 dest
= blockAddr(dest
);
355 req
->flags
&= ~UNCACHEABLE
;
359 req
->data
= new uint8_t[blockSize
];
360 req
->size
= blockSize
;
361 if (source
== traceBlockAddr
|| dest
== traceBlockAddr
) {
363 << ": initiating copy of "
364 << dec
<< req
->size
<< " bytes from addr 0x"
366 << " (0x" << hex
<< blockAddr(source
) << ")"
369 << " (0x" << hex
<< blockAddr(dest
) << ")"
371 << dec
<< curTick
<< endl
;
373 cacheInterface
->access(req
);
374 uint8_t result
[blockSize
];
375 checkMem
->access(Read
, source
, &result
, blockSize
);
376 checkMem
->access(Write
, dest
, &result
, blockSize
);
382 MemCompleteEvent::process()
384 tester
->completeRequest(req
, data
);
390 MemCompleteEvent::description()
392 return "memory access completion";
396 BEGIN_DECLARE_SIM_OBJECT_PARAMS(MemTest
)
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
;
411 END_DECLARE_SIM_OBJECT_PARAMS(MemTest
)
414 BEGIN_INIT_SIM_OBJECT_PARAMS(MemTest
)
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")
431 END_INIT_SIM_OBJECT_PARAMS(MemTest
)
434 CREATE_SIM_OBJECT(MemTest
)
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
);
443 REGISTER_SIM_OBJECT("MemTest", MemTest
)