memtest.cc revision 145
1/*
2 * Copyright (c) 2003 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 <string>
32#include <sstream>
33#include <iomanip>
34#include <vector>
35
36#include "cpu/memtest/memtest.hh"
37#include "base/misc.hh"
38#include "sim/sim_events.hh"
39#include "mem/functional_mem/main_memory.hh"
40#include "mem/cache/base_cache.hh"
41
42#include "base/statistics.hh"
43#include "sim/sim_stats.hh"
44
45using namespace std;
46
47MemTest::MemTest(const string &name,
48                 MemInterface *_cache_interface,
49                 FunctionalMemory *main_mem,
50                 FunctionalMemory *check_mem,
51                 unsigned _memorySize,
52                 unsigned _percentReads,
53                 unsigned _percentUncacheable,
54                 unsigned _progressInterval,
55                 Addr _traceAddr,
56                 Counter max_loads_any_thread,
57                 Counter max_loads_all_threads)
58    : BaseCPU(name, 1, 0, 0, max_loads_any_thread, max_loads_all_threads),
59      tickEvent(this),
60      cacheInterface(_cache_interface),
61      mainMem(main_mem),
62      checkMem(check_mem),
63      size(_memorySize),
64      percentReads(_percentReads),
65      percentUncacheable(_percentUncacheable),
66      progressInterval(_progressInterval),
67      nextProgressMessage(_progressInterval)
68{
69    vector<string> cmd;
70    cmd.push_back("/bin/ls");
71    vector<string> null_vec;
72    xc = new ExecContext(this ,0,mainMem,0);
73
74    blockSize = cacheInterface->getBlockSize();
75    blockAddrMask = blockSize - 1;
76    traceBlockAddr = blockAddr(_traceAddr);
77
78    //setup data storage with interesting values
79    uint8_t *data1 = new uint8_t[size];
80    uint8_t *data2 = new uint8_t[size];
81    uint8_t *data3 = new uint8_t[size];
82    memset(data1, 1, size);
83    memset(data2, 2, size);
84    memset(data3, 3, size);
85    curTick = 0;
86
87    baseAddr1 = 0x100000;
88    baseAddr2 = 0x400000;
89    uncacheAddr = 0x800000;
90
91    // set up intial memory contents here
92    mainMem->prot_write(baseAddr1, data1, size);
93    checkMem->prot_write(baseAddr1, data1, size);
94    mainMem->prot_write(baseAddr2, data2, size);
95    checkMem->prot_write(baseAddr2, data2, size);
96    mainMem->prot_write(uncacheAddr, data3, size);
97    checkMem->prot_write(uncacheAddr, data3, size);
98
99    delete [] data1;
100    delete [] data2;
101    delete [] data3;
102
103    // set up counters
104    noResponseCycles = 0;
105    numReads = 0;
106    numWrites = 0;
107    tickEvent.schedule(0);
108}
109
110static void
111printData(ostream &os, uint8_t *data, int nbytes)
112{
113    os << hex << setfill('0');
114    // assume little-endian: print bytes from highest address to lowest
115    for (uint8_t *dp = data + nbytes - 1; dp >= data; --dp) {
116        os << setw(2) << (unsigned)*dp;
117    }
118    os << dec;
119}
120
121void
122MemTest::completeRequest(MemReqPtr req, uint8_t *data)
123{
124    switch (req->cmd) {
125      case Read:
126        if (memcmp(req->data, data, req->size) != 0) {
127            cerr << name() << ": on read of 0x" << hex << req->paddr
128                 << " @ cycle " << dec << curTick
129                 << ", cache returns 0x";
130            printData(cerr, req->data, req->size);
131            cerr << ", expected 0x";
132            printData(cerr, data, req->size);
133            cerr << endl;
134            fatal("");
135        }
136
137        numReads++;
138
139        if (numReads.value() == nextProgressMessage) {
140            cerr << name() << ": completed " << numReads.value()
141                 << " read accesses @ " << curTick << endl;
142            nextProgressMessage += progressInterval;
143        }
144
145        comLoadEventQueue[0]->serviceEvents(numReads.value());
146        break;
147
148      case Write:
149        numWrites++;
150        break;
151
152
153      default:
154        panic("invalid command");
155    }
156
157    if (blockAddr(req->paddr) == traceBlockAddr) {
158        cerr << name() << ": completed "
159             << (req->cmd.isWrite() ? "write" : "read") << " access of "
160             << req->size << " bytes at address 0x"
161             << hex << req->paddr << ", value = 0x";
162        printData(cerr, req->data, req->size);
163        cerr << " @ cycle " << dec << curTick;
164
165        cerr << endl;
166    }
167
168    noResponseCycles = 0;
169    delete [] data;
170}
171
172
173void
174MemTest::regStats()
175{
176    using namespace Statistics;
177
178    numReads
179        .name(name() + ".num_reads")
180        .desc("number of read accesses completed")
181        ;
182
183    numWrites
184        .name(name() + ".num_writes")
185        .desc("number of write accesses completed")
186        ;
187
188    numCopies
189        .name(name() + ".num_copies")
190        .desc("number of copy accesses completed")
191        ;
192}
193
194void
195MemTest::tick()
196{
197    if (!tickEvent.scheduled())
198        tickEvent.schedule(curTick + 1);
199
200    if (++noResponseCycles >= 5000) {
201        cerr << name() << ": deadlocked at cycle " << curTick << endl;
202        fatal("");
203    }
204
205    if (cacheInterface->isBlocked()) {
206        return;
207    }
208
209    //make new request
210    unsigned cmd = rand() % 100;
211    unsigned offset1 = random() % size;
212    unsigned base = random() % 2;
213    uint64_t data = random();
214    unsigned access_size = random() % 4;
215    unsigned cacheable = rand() % 100;
216    unsigned probe = rand() % 2;
217
218    MemReqPtr req = new MemReq();
219
220    if (cacheable < percentUncacheable) {
221        req->flags |= UNCACHEABLE;
222        req->paddr = uncacheAddr + offset1;
223    } else {
224        req->paddr = ((base) ? baseAddr1 : baseAddr2) + offset1;
225    }
226
227    req->size = 1 << access_size;
228    req->data = new uint8_t[req->size];
229    req->paddr &= ~(req->size - 1);
230    req->time = curTick;
231    req->xc = xc;
232
233    if (cmd < percentReads) {
234        // read
235        req->cmd = Read;
236        uint8_t *result = new uint8_t[8];
237        checkMem->access(Read, req->paddr, result, req->size);
238        if (blockAddr(req->paddr) == traceBlockAddr) {
239            cerr << name() << ": initiating read "
240                 << ((probe)?"probe of ":"access of ")
241                 << req->size << " bytes from addr 0x"
242                 << hex << req->paddr << " at cycle "
243                 << dec << curTick << endl;
244        }
245        if (probe) {
246            cacheInterface->probeAndUpdate(req);
247            completeRequest(req, result);
248        } else {
249            req->completionEvent = new MemCompleteEvent(req, result, this);
250            cacheInterface->access(req);
251        }
252    } else {
253        // write
254        req->cmd = Write;
255        memcpy(req->data, &data, req->size);
256        checkMem->access(Write, req->paddr, req->data, req->size);
257        if (blockAddr(req->paddr) == traceBlockAddr) {
258            cerr << name() << ": initiating write "
259                 << ((probe)?"probe of ":"access of ")
260                 << req->size << " bytes (value = 0x";
261            printData(cerr, req->data, req->size);
262            cerr << ") to addr 0x"
263                 << hex << req->paddr << " at cycle "
264                 << dec << curTick << endl;
265        }
266        if (probe) {
267            cacheInterface->probeAndUpdate(req);
268            completeRequest(req, NULL);
269        } else {
270            req->completionEvent = new MemCompleteEvent(req, NULL, this);
271            cacheInterface->access(req);
272        }
273    }
274}
275
276
277void
278MemCompleteEvent::process()
279{
280    tester->completeRequest(req, data);
281    delete this;
282}
283
284
285const char *
286MemCompleteEvent::description()
287{
288    return "memory access completion";
289}
290
291
292BEGIN_DECLARE_SIM_OBJECT_PARAMS(MemTest)
293
294    SimObjectParam<BaseCache *> cache;
295    SimObjectParam<FunctionalMemory *> main_mem;
296    SimObjectParam<FunctionalMemory *> check_mem;
297    Param<unsigned> memory_size;
298    Param<unsigned> percent_reads;
299    Param<unsigned> percent_uncacheable;
300    Param<unsigned> progress_interval;
301    Param<Addr> trace_addr;
302    Param<Counter> max_loads_any_thread;
303    Param<Counter> max_loads_all_threads;
304
305END_DECLARE_SIM_OBJECT_PARAMS(MemTest)
306
307
308BEGIN_INIT_SIM_OBJECT_PARAMS(MemTest)
309
310    INIT_PARAM(cache, "L1 cache"),
311    INIT_PARAM(main_mem, "hierarchical memory"),
312    INIT_PARAM(check_mem, "check memory"),
313    INIT_PARAM_DFLT(memory_size, "memory size", 65536),
314    INIT_PARAM_DFLT(percent_reads, "target read percentage", 65),
315    INIT_PARAM_DFLT(percent_uncacheable, "target uncacheable percentage", 10),
316    INIT_PARAM_DFLT(progress_interval,
317                    "progress report interval (in accesses)", 1000000),
318    INIT_PARAM_DFLT(trace_addr, "address to trace", 0),
319    INIT_PARAM_DFLT(max_loads_any_thread,
320                    "terminate when any thread reaches this load count",
321                    0),
322    INIT_PARAM_DFLT(max_loads_all_threads,
323                    "terminate when all threads have reached this load count",
324                    0)
325
326END_INIT_SIM_OBJECT_PARAMS(MemTest)
327
328
329CREATE_SIM_OBJECT(MemTest)
330{
331    return new MemTest(getInstanceName(), cache->getInterface(), main_mem,
332                       check_mem, memory_size, percent_reads,
333                       percent_uncacheable, progress_interval,
334                       trace_addr, max_loads_any_thread,
335                       max_loads_all_threads);
336}
337
338REGISTER_SIM_OBJECT("MemTest", MemTest)
339