memtest.cc revision 1762
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
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 "sim/builder.hh"
43#include "sim/sim_events.hh"
44#include "sim/stats.hh"
45
46using namespace std;
47
48int TESTER_ALLOCATOR=0;
49
50MemTest::MemTest(const string &name,
51                 MemInterface *_cache_interface,
52                 FunctionalMemory *main_mem,
53                 FunctionalMemory *check_mem,
54                 unsigned _memorySize,
55                 unsigned _percentReads,
56                 unsigned _percentCopies,
57                 unsigned _percentUncacheable,
58                 unsigned _progressInterval,
59                 unsigned _percentSourceUnaligned,
60                 unsigned _percentDestUnaligned,
61                 Addr _traceAddr,
62                 Counter _max_loads)
63    : SimObject(name),
64      tickEvent(this),
65      cacheInterface(_cache_interface),
66      mainMem(main_mem),
67      checkMem(check_mem),
68      size(_memorySize),
69      percentReads(_percentReads),
70      percentCopies(_percentCopies),
71      percentUncacheable(_percentUncacheable),
72      progressInterval(_progressInterval),
73      nextProgressMessage(_progressInterval),
74      percentSourceUnaligned(_percentSourceUnaligned),
75      percentDestUnaligned(percentDestUnaligned),
76      maxLoads(_max_loads)
77{
78    vector<string> cmd;
79    cmd.push_back("/bin/ls");
80    vector<string> null_vec;
81    xc = new ExecContext(NULL, 0, mainMem, 0);
82
83    blockSize = cacheInterface->getBlockSize();
84    blockAddrMask = blockSize - 1;
85    traceBlockAddr = blockAddr(_traceAddr);
86
87    //setup data storage with interesting values
88    uint8_t *data1 = new uint8_t[size];
89    uint8_t *data2 = new uint8_t[size];
90    uint8_t *data3 = new uint8_t[size];
91    memset(data1, 1, size);
92    memset(data2, 2, size);
93    memset(data3, 3, size);
94    curTick = 0;
95
96    baseAddr1 = 0x100000;
97    baseAddr2 = 0x400000;
98    uncacheAddr = 0x800000;
99
100    // set up intial memory contents here
101    mainMem->prot_write(baseAddr1, data1, size);
102    checkMem->prot_write(baseAddr1, data1, size);
103    mainMem->prot_write(baseAddr2, data2, size);
104    checkMem->prot_write(baseAddr2, data2, size);
105    mainMem->prot_write(uncacheAddr, data3, size);
106    checkMem->prot_write(uncacheAddr, data3, size);
107
108    delete [] data1;
109    delete [] data2;
110    delete [] data3;
111
112    // set up counters
113    noResponseCycles = 0;
114    numReads = 0;
115    tickEvent.schedule(0);
116
117    id = TESTER_ALLOCATOR++;
118}
119
120static void
121printData(ostream &os, uint8_t *data, int nbytes)
122{
123    os << hex << setfill('0');
124    // assume little-endian: print bytes from highest address to lowest
125    for (uint8_t *dp = data + nbytes - 1; dp >= data; --dp) {
126        os << setw(2) << (unsigned)*dp;
127    }
128    os << dec;
129}
130
131void
132MemTest::completeRequest(MemReqPtr &req, uint8_t *data)
133{
134    //Remove the address from the list of outstanding
135    std::set<unsigned>::iterator removeAddr = outstandingAddrs.find(req->paddr);
136    assert(removeAddr != outstandingAddrs.end());
137    outstandingAddrs.erase(removeAddr);
138
139    switch (req->cmd) {
140      case Read:
141        if (memcmp(req->data, data, req->size) != 0) {
142            cerr << name() << ": on read of 0x" << hex << req->paddr
143                 << " (0x" << hex << blockAddr(req->paddr) << ")"
144                 << "@ cycle " << dec << curTick
145                 << ", cache returns 0x";
146            printData(cerr, req->data, req->size);
147            cerr << ", expected 0x";
148            printData(cerr, data, req->size);
149            cerr << endl;
150            fatal("");
151        }
152
153        numReads++;
154        numReadsStat++;
155
156        if (numReads == nextProgressMessage) {
157            ccprintf(cerr, "%s: completed %d read accesses @%d\n",
158                     name(), numReads, curTick);
159            nextProgressMessage += progressInterval;
160        }
161
162        if (numReads >= maxLoads)
163            SimExit(curTick, "Maximum number of loads reached!");
164        break;
165
166      case Write:
167        numWritesStat++;
168        break;
169
170      case Copy:
171        //Also remove dest from outstanding list
172        removeAddr = outstandingAddrs.find(req->dest);
173        assert(removeAddr != outstandingAddrs.end());
174        outstandingAddrs.erase(removeAddr);
175        numCopiesStat++;
176        break;
177
178      default:
179        panic("invalid command");
180    }
181
182    if (blockAddr(req->paddr) == traceBlockAddr) {
183        cerr << name() << ": completed "
184             << (req->cmd.isWrite() ? "write" : "read")
185             << " access of "
186             << dec << req->size << " bytes at address 0x"
187             << hex << req->paddr
188             << " (0x" << hex << blockAddr(req->paddr) << ")"
189             << ", value = 0x";
190        printData(cerr, req->data, req->size);
191        cerr << " @ cycle " << dec << curTick;
192
193        cerr << endl;
194    }
195
196    noResponseCycles = 0;
197    delete [] data;
198}
199
200
201void
202MemTest::regStats()
203{
204    using namespace Stats;
205
206
207    numReadsStat
208        .name(name() + ".num_reads")
209        .desc("number of read accesses completed")
210        ;
211
212    numWritesStat
213        .name(name() + ".num_writes")
214        .desc("number of write accesses completed")
215        ;
216
217    numCopiesStat
218        .name(name() + ".num_copies")
219        .desc("number of copy accesses completed")
220        ;
221}
222
223void
224MemTest::tick()
225{
226    if (!tickEvent.scheduled())
227        tickEvent.schedule(curTick + cycles(1));
228
229    if (++noResponseCycles >= 500000) {
230        cerr << name() << ": deadlocked at cycle " << curTick << endl;
231        fatal("");
232    }
233
234    if (cacheInterface->isBlocked()) {
235        return;
236    }
237
238    //make new request
239    unsigned cmd = rand() % 100;
240    unsigned offset1 = random() % size;
241    unsigned offset2 = random() % size;
242    unsigned base = random() % 2;
243    uint64_t data = random();
244    unsigned access_size = random() % 4;
245    unsigned cacheable = rand() % 100;
246    unsigned source_align = rand() % 100;
247    unsigned dest_align = rand() % 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        offset1 &= ~63; //Not the low order bits
255        offset1 += 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 + offset1;
264    } else {
265        req->paddr = ((base) ? baseAddr1 : baseAddr2) + offset1;
266    }
267    bool probe = (rand() % 2 == 1) && !req->isUncacheable();
268    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 = xc;
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        Addr source = ((base) ? baseAddr1 : baseAddr2) + offset1;
338        Addr dest = ((base) ? baseAddr2 : baseAddr1) + offset2;
339        if (outstandingAddrs.find(source) != outstandingAddrs.end()) return;
340        else outstandingAddrs.insert(source);
341        if (outstandingAddrs.find(dest) != outstandingAddrs.end()) return;
342        else outstandingAddrs.insert(dest);
343
344        if (source_align >= percentSourceUnaligned) {
345            source = blockAddr(source);
346        }
347        if (dest_align >= percentDestUnaligned) {
348            dest = blockAddr(dest);
349        }
350        req->cmd = Copy;
351        req->flags &= ~UNCACHEABLE;
352        req->paddr = source;
353        req->dest = dest;
354        delete [] req->data;
355        req->data = new uint8_t[blockSize];
356        req->size = blockSize;
357        if (source == traceBlockAddr || dest == traceBlockAddr) {
358            cerr << name()
359                 << ": initiating copy of "
360                 << dec << req->size << " bytes from addr 0x"
361                 << hex << source
362                 << " (0x" << hex << blockAddr(source) << ")"
363                 << " to addr 0x"
364                 << hex << dest
365                 << " (0x" << hex << blockAddr(dest) << ")"
366                 << " at cycle "
367                 << dec << curTick << endl;
368        }
369        cacheInterface->access(req);
370        uint8_t result[blockSize];
371        checkMem->access(Read, source, &result, blockSize);
372        checkMem->access(Write, dest, &result, blockSize);
373    }
374}
375
376
377void
378MemCompleteEvent::process()
379{
380    tester->completeRequest(req, data);
381    delete this;
382}
383
384
385const char *
386MemCompleteEvent::description()
387{
388    return "memory access completion";
389}
390
391
392BEGIN_DECLARE_SIM_OBJECT_PARAMS(MemTest)
393
394    SimObjectParam<BaseCache *> cache;
395    SimObjectParam<FunctionalMemory *> main_mem;
396    SimObjectParam<FunctionalMemory *> check_mem;
397    Param<unsigned> memory_size;
398    Param<unsigned> percent_reads;
399    Param<unsigned> percent_copies;
400    Param<unsigned> percent_uncacheable;
401    Param<unsigned> progress_interval;
402    Param<unsigned> percent_source_unaligned;
403    Param<unsigned> percent_dest_unaligned;
404    Param<Addr> trace_addr;
405    Param<Counter> max_loads;
406
407END_DECLARE_SIM_OBJECT_PARAMS(MemTest)
408
409
410BEGIN_INIT_SIM_OBJECT_PARAMS(MemTest)
411
412    INIT_PARAM(cache, "L1 cache"),
413    INIT_PARAM(main_mem, "hierarchical memory"),
414    INIT_PARAM(check_mem, "check memory"),
415    INIT_PARAM(memory_size, "memory size"),
416    INIT_PARAM(percent_reads, "target read percentage"),
417    INIT_PARAM(percent_copies, "target copy percentage"),
418    INIT_PARAM(percent_uncacheable, "target uncacheable percentage"),
419    INIT_PARAM(progress_interval, "progress report interval (in accesses)"),
420    INIT_PARAM(percent_source_unaligned,
421               "percent of copy source address that are unaligned"),
422    INIT_PARAM(percent_dest_unaligned,
423               "percent of copy dest address that are unaligned"),
424    INIT_PARAM(trace_addr, "address to trace"),
425    INIT_PARAM(max_loads, "terminate when we have reached this load count")
426
427END_INIT_SIM_OBJECT_PARAMS(MemTest)
428
429
430CREATE_SIM_OBJECT(MemTest)
431{
432    return new MemTest(getInstanceName(), cache->getInterface(), main_mem,
433                       check_mem, memory_size, percent_reads, percent_copies,
434                       percent_uncacheable, progress_interval,
435                       percent_source_unaligned, percent_dest_unaligned,
436                       trace_addr, max_loads);
437}
438
439REGISTER_SIM_OBJECT("MemTest", MemTest)
440