memtest.cc (11793:ef606668d247) memtest.cc (11800:54436a1784dc)
1/*
2 * Copyright (c) 2015 ARM Limited
3 * All rights reserved
4 *
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder. You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
13 *
14 * Copyright (c) 2002-2005 The Regents of The University of Michigan
15 * All rights reserved.
16 *
17 * Redistribution and use in source and binary forms, with or without
18 * modification, are permitted provided that the following conditions are
19 * met: redistributions of source code must retain the above copyright
20 * notice, this list of conditions and the following disclaimer;
21 * redistributions in binary form must reproduce the above copyright
22 * notice, this list of conditions and the following disclaimer in the
23 * documentation and/or other materials provided with the distribution;
24 * neither the name of the copyright holders nor the names of its
25 * contributors may be used to endorse or promote products derived from
26 * this software without specific prior written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 *
40 * Authors: Erik Hallnor
41 * Steve Reinhardt
42 * Andreas Hansson
43 */
44
45#include "cpu/testers/memtest/memtest.hh"
46
47#include "base/random.hh"
48#include "base/statistics.hh"
1/*
2 * Copyright (c) 2015 ARM Limited
3 * All rights reserved
4 *
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder. You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
13 *
14 * Copyright (c) 2002-2005 The Regents of The University of Michigan
15 * All rights reserved.
16 *
17 * Redistribution and use in source and binary forms, with or without
18 * modification, are permitted provided that the following conditions are
19 * met: redistributions of source code must retain the above copyright
20 * notice, this list of conditions and the following disclaimer;
21 * redistributions in binary form must reproduce the above copyright
22 * notice, this list of conditions and the following disclaimer in the
23 * documentation and/or other materials provided with the distribution;
24 * neither the name of the copyright holders nor the names of its
25 * contributors may be used to endorse or promote products derived from
26 * this software without specific prior written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 *
40 * Authors: Erik Hallnor
41 * Steve Reinhardt
42 * Andreas Hansson
43 */
44
45#include "cpu/testers/memtest/memtest.hh"
46
47#include "base/random.hh"
48#include "base/statistics.hh"
49#include "base/trace.hh"
49#include "debug/MemTest.hh"
50#include "mem/mem_object.hh"
51#include "sim/sim_exit.hh"
52#include "sim/stats.hh"
53#include "sim/system.hh"
54
55using namespace std;
56
57unsigned int TESTER_ALLOCATOR = 0;
58
59bool
60MemTest::CpuPort::recvTimingResp(PacketPtr pkt)
61{
62 memtest.completeRequest(pkt);
63 return true;
64}
65
66void
67MemTest::CpuPort::recvReqRetry()
68{
69 memtest.recvRetry();
70}
71
72bool
73MemTest::sendPkt(PacketPtr pkt) {
74 if (atomic) {
75 port.sendAtomic(pkt);
76 completeRequest(pkt);
77 } else {
78 if (!port.sendTimingReq(pkt)) {
79 retryPkt = pkt;
80 return false;
81 }
82 }
83 return true;
84}
85
86MemTest::MemTest(const Params *p)
87 : MemObject(p),
88 tickEvent(this),
89 noRequestEvent(this),
90 noResponseEvent(this),
91 port("port", *this),
92 retryPkt(nullptr),
93 size(p->size),
94 interval(p->interval),
95 percentReads(p->percent_reads),
96 percentFunctional(p->percent_functional),
97 percentUncacheable(p->percent_uncacheable),
98 masterId(p->system->getMasterId(name())),
99 blockSize(p->system->cacheLineSize()),
100 blockAddrMask(blockSize - 1),
101 progressInterval(p->progress_interval),
102 progressCheck(p->progress_check),
103 nextProgressMessage(p->progress_interval),
104 maxLoads(p->max_loads),
105 atomic(p->system->isAtomicMode()),
106 suppressFuncWarnings(p->suppress_func_warnings)
107{
108 id = TESTER_ALLOCATOR++;
109 fatal_if(id >= blockSize, "Too many testers, only %d allowed\n",
110 blockSize - 1);
111
112 baseAddr1 = 0x100000;
113 baseAddr2 = 0x400000;
114 uncacheAddr = 0x800000;
115
116 // set up counters
117 numReads = 0;
118 numWrites = 0;
119
120 // kick things into action
121 schedule(tickEvent, curTick());
122 schedule(noRequestEvent, clockEdge(progressCheck));
123 schedule(noResponseEvent, clockEdge(progressCheck));
124}
125
126BaseMasterPort &
127MemTest::getMasterPort(const std::string &if_name, PortID idx)
128{
129 if (if_name == "port")
130 return port;
131 else
132 return MemObject::getMasterPort(if_name, idx);
133}
134
135void
136MemTest::completeRequest(PacketPtr pkt, bool functional)
137{
138 Request *req = pkt->req;
139 assert(req->getSize() == 1);
140
141 // this address is no longer outstanding
142 auto remove_addr = outstandingAddrs.find(req->getPaddr());
143 assert(remove_addr != outstandingAddrs.end());
144 outstandingAddrs.erase(remove_addr);
145
146 DPRINTF(MemTest, "Completing %s at address %x (blk %x) %s\n",
147 pkt->isWrite() ? "write" : "read",
148 req->getPaddr(), blockAlign(req->getPaddr()),
149 pkt->isError() ? "error" : "success");
150
151 const uint8_t *pkt_data = pkt->getConstPtr<uint8_t>();
152
153 if (pkt->isError()) {
154 if (!functional || !suppressFuncWarnings) {
155 warn("%s access failed at %#x\n",
156 pkt->isWrite() ? "Write" : "Read", req->getPaddr());
157 }
158 } else {
159 if (pkt->isRead()) {
160 uint8_t ref_data = referenceData[req->getPaddr()];
161 if (pkt_data[0] != ref_data) {
162 panic("%s: read of %x (blk %x) @ cycle %d "
163 "returns %x, expected %x\n", name(),
164 req->getPaddr(), blockAlign(req->getPaddr()), curTick(),
165 pkt_data[0], ref_data);
166 }
167
168 numReads++;
169 numReadsStat++;
170
171 if (numReads == (uint64_t)nextProgressMessage) {
172 ccprintf(cerr, "%s: completed %d read, %d write accesses @%d\n",
173 name(), numReads, numWrites, curTick());
174 nextProgressMessage += progressInterval;
175 }
176
177 if (maxLoads != 0 && numReads >= maxLoads)
178 exitSimLoop("maximum number of loads reached");
179 } else {
180 assert(pkt->isWrite());
181
182 // update the reference data
183 referenceData[req->getPaddr()] = pkt_data[0];
184 numWrites++;
185 numWritesStat++;
186 }
187 }
188
189 delete pkt->req;
190
191 // the packet will delete the data
192 delete pkt;
193
194 // finally shift the response timeout forward
195 reschedule(noResponseEvent, clockEdge(progressCheck), true);
196}
197
198void
199MemTest::regStats()
200{
201 MemObject::regStats();
202
203 using namespace Stats;
204
205 numReadsStat
206 .name(name() + ".num_reads")
207 .desc("number of read accesses completed")
208 ;
209
210 numWritesStat
211 .name(name() + ".num_writes")
212 .desc("number of write accesses completed")
213 ;
214}
215
216void
217MemTest::tick()
218{
219 // we should never tick if we are waiting for a retry
220 assert(!retryPkt);
221
222 // create a new request
223 unsigned cmd = random_mt.random(0, 100);
224 uint8_t data = random_mt.random<uint8_t>();
225 bool uncacheable = random_mt.random(0, 100) < percentUncacheable;
226 unsigned base = random_mt.random(0, 1);
227 Request::Flags flags;
228 Addr paddr;
229
230 // generate a unique address
231 do {
232 unsigned offset = random_mt.random<unsigned>(0, size - 1);
233
234 // use the tester id as offset within the block for false sharing
235 offset = blockAlign(offset);
236 offset += id;
237
238 if (uncacheable) {
239 flags.set(Request::UNCACHEABLE);
240 paddr = uncacheAddr + offset;
241 } else {
242 paddr = ((base) ? baseAddr1 : baseAddr2) + offset;
243 }
244 } while (outstandingAddrs.find(paddr) != outstandingAddrs.end());
245
246 bool do_functional = (random_mt.random(0, 100) < percentFunctional) &&
247 !uncacheable;
248 Request *req = new Request(paddr, 1, flags, masterId);
249 req->setContext(id);
250
251 outstandingAddrs.insert(paddr);
252
253 // sanity check
254 panic_if(outstandingAddrs.size() > 100,
255 "Tester %s has more than 100 outstanding requests\n", name());
256
257 PacketPtr pkt = nullptr;
258 uint8_t *pkt_data = new uint8_t[1];
259
260 if (cmd < percentReads) {
261 // start by ensuring there is a reference value if we have not
262 // seen this address before
263 uint8_t M5_VAR_USED ref_data = 0;
264 auto ref = referenceData.find(req->getPaddr());
265 if (ref == referenceData.end()) {
266 referenceData[req->getPaddr()] = 0;
267 } else {
268 ref_data = ref->second;
269 }
270
271 DPRINTF(MemTest,
272 "Initiating %sread at addr %x (blk %x) expecting %x\n",
273 do_functional ? "functional " : "", req->getPaddr(),
274 blockAlign(req->getPaddr()), ref_data);
275
276 pkt = new Packet(req, MemCmd::ReadReq);
277 pkt->dataDynamic(pkt_data);
278 } else {
279 DPRINTF(MemTest, "Initiating %swrite at addr %x (blk %x) value %x\n",
280 do_functional ? "functional " : "", req->getPaddr(),
281 blockAlign(req->getPaddr()), data);
282
283 pkt = new Packet(req, MemCmd::WriteReq);
284 pkt->dataDynamic(pkt_data);
285 pkt_data[0] = data;
286 }
287
288 // there is no point in ticking if we are waiting for a retry
289 bool keep_ticking = true;
290 if (do_functional) {
291 pkt->setSuppressFuncError();
292 port.sendFunctional(pkt);
293 completeRequest(pkt, true);
294 } else {
295 keep_ticking = sendPkt(pkt);
296 }
297
298 if (keep_ticking) {
299 // schedule the next tick
300 schedule(tickEvent, clockEdge(interval));
301
302 // finally shift the timeout for sending of requests forwards
303 // as we have successfully sent a packet
304 reschedule(noRequestEvent, clockEdge(progressCheck), true);
305 } else {
306 DPRINTF(MemTest, "Waiting for retry\n");
307 }
308}
309
310void
311MemTest::noRequest()
312{
313 panic("%s did not send a request for %d cycles", name(), progressCheck);
314}
315
316void
317MemTest::noResponse()
318{
319 panic("%s did not see a response for %d cycles", name(), progressCheck);
320}
321
322void
323MemTest::recvRetry()
324{
325 assert(retryPkt);
326 if (port.sendTimingReq(retryPkt)) {
327 DPRINTF(MemTest, "Proceeding after successful retry\n");
328
329 retryPkt = nullptr;
330 // kick things into action again
331 schedule(tickEvent, clockEdge(interval));
332 }
333}
334
335MemTest *
336MemTestParams::create()
337{
338 return new MemTest(this);
339}
50#include "debug/MemTest.hh"
51#include "mem/mem_object.hh"
52#include "sim/sim_exit.hh"
53#include "sim/stats.hh"
54#include "sim/system.hh"
55
56using namespace std;
57
58unsigned int TESTER_ALLOCATOR = 0;
59
60bool
61MemTest::CpuPort::recvTimingResp(PacketPtr pkt)
62{
63 memtest.completeRequest(pkt);
64 return true;
65}
66
67void
68MemTest::CpuPort::recvReqRetry()
69{
70 memtest.recvRetry();
71}
72
73bool
74MemTest::sendPkt(PacketPtr pkt) {
75 if (atomic) {
76 port.sendAtomic(pkt);
77 completeRequest(pkt);
78 } else {
79 if (!port.sendTimingReq(pkt)) {
80 retryPkt = pkt;
81 return false;
82 }
83 }
84 return true;
85}
86
87MemTest::MemTest(const Params *p)
88 : MemObject(p),
89 tickEvent(this),
90 noRequestEvent(this),
91 noResponseEvent(this),
92 port("port", *this),
93 retryPkt(nullptr),
94 size(p->size),
95 interval(p->interval),
96 percentReads(p->percent_reads),
97 percentFunctional(p->percent_functional),
98 percentUncacheable(p->percent_uncacheable),
99 masterId(p->system->getMasterId(name())),
100 blockSize(p->system->cacheLineSize()),
101 blockAddrMask(blockSize - 1),
102 progressInterval(p->progress_interval),
103 progressCheck(p->progress_check),
104 nextProgressMessage(p->progress_interval),
105 maxLoads(p->max_loads),
106 atomic(p->system->isAtomicMode()),
107 suppressFuncWarnings(p->suppress_func_warnings)
108{
109 id = TESTER_ALLOCATOR++;
110 fatal_if(id >= blockSize, "Too many testers, only %d allowed\n",
111 blockSize - 1);
112
113 baseAddr1 = 0x100000;
114 baseAddr2 = 0x400000;
115 uncacheAddr = 0x800000;
116
117 // set up counters
118 numReads = 0;
119 numWrites = 0;
120
121 // kick things into action
122 schedule(tickEvent, curTick());
123 schedule(noRequestEvent, clockEdge(progressCheck));
124 schedule(noResponseEvent, clockEdge(progressCheck));
125}
126
127BaseMasterPort &
128MemTest::getMasterPort(const std::string &if_name, PortID idx)
129{
130 if (if_name == "port")
131 return port;
132 else
133 return MemObject::getMasterPort(if_name, idx);
134}
135
136void
137MemTest::completeRequest(PacketPtr pkt, bool functional)
138{
139 Request *req = pkt->req;
140 assert(req->getSize() == 1);
141
142 // this address is no longer outstanding
143 auto remove_addr = outstandingAddrs.find(req->getPaddr());
144 assert(remove_addr != outstandingAddrs.end());
145 outstandingAddrs.erase(remove_addr);
146
147 DPRINTF(MemTest, "Completing %s at address %x (blk %x) %s\n",
148 pkt->isWrite() ? "write" : "read",
149 req->getPaddr(), blockAlign(req->getPaddr()),
150 pkt->isError() ? "error" : "success");
151
152 const uint8_t *pkt_data = pkt->getConstPtr<uint8_t>();
153
154 if (pkt->isError()) {
155 if (!functional || !suppressFuncWarnings) {
156 warn("%s access failed at %#x\n",
157 pkt->isWrite() ? "Write" : "Read", req->getPaddr());
158 }
159 } else {
160 if (pkt->isRead()) {
161 uint8_t ref_data = referenceData[req->getPaddr()];
162 if (pkt_data[0] != ref_data) {
163 panic("%s: read of %x (blk %x) @ cycle %d "
164 "returns %x, expected %x\n", name(),
165 req->getPaddr(), blockAlign(req->getPaddr()), curTick(),
166 pkt_data[0], ref_data);
167 }
168
169 numReads++;
170 numReadsStat++;
171
172 if (numReads == (uint64_t)nextProgressMessage) {
173 ccprintf(cerr, "%s: completed %d read, %d write accesses @%d\n",
174 name(), numReads, numWrites, curTick());
175 nextProgressMessage += progressInterval;
176 }
177
178 if (maxLoads != 0 && numReads >= maxLoads)
179 exitSimLoop("maximum number of loads reached");
180 } else {
181 assert(pkt->isWrite());
182
183 // update the reference data
184 referenceData[req->getPaddr()] = pkt_data[0];
185 numWrites++;
186 numWritesStat++;
187 }
188 }
189
190 delete pkt->req;
191
192 // the packet will delete the data
193 delete pkt;
194
195 // finally shift the response timeout forward
196 reschedule(noResponseEvent, clockEdge(progressCheck), true);
197}
198
199void
200MemTest::regStats()
201{
202 MemObject::regStats();
203
204 using namespace Stats;
205
206 numReadsStat
207 .name(name() + ".num_reads")
208 .desc("number of read accesses completed")
209 ;
210
211 numWritesStat
212 .name(name() + ".num_writes")
213 .desc("number of write accesses completed")
214 ;
215}
216
217void
218MemTest::tick()
219{
220 // we should never tick if we are waiting for a retry
221 assert(!retryPkt);
222
223 // create a new request
224 unsigned cmd = random_mt.random(0, 100);
225 uint8_t data = random_mt.random<uint8_t>();
226 bool uncacheable = random_mt.random(0, 100) < percentUncacheable;
227 unsigned base = random_mt.random(0, 1);
228 Request::Flags flags;
229 Addr paddr;
230
231 // generate a unique address
232 do {
233 unsigned offset = random_mt.random<unsigned>(0, size - 1);
234
235 // use the tester id as offset within the block for false sharing
236 offset = blockAlign(offset);
237 offset += id;
238
239 if (uncacheable) {
240 flags.set(Request::UNCACHEABLE);
241 paddr = uncacheAddr + offset;
242 } else {
243 paddr = ((base) ? baseAddr1 : baseAddr2) + offset;
244 }
245 } while (outstandingAddrs.find(paddr) != outstandingAddrs.end());
246
247 bool do_functional = (random_mt.random(0, 100) < percentFunctional) &&
248 !uncacheable;
249 Request *req = new Request(paddr, 1, flags, masterId);
250 req->setContext(id);
251
252 outstandingAddrs.insert(paddr);
253
254 // sanity check
255 panic_if(outstandingAddrs.size() > 100,
256 "Tester %s has more than 100 outstanding requests\n", name());
257
258 PacketPtr pkt = nullptr;
259 uint8_t *pkt_data = new uint8_t[1];
260
261 if (cmd < percentReads) {
262 // start by ensuring there is a reference value if we have not
263 // seen this address before
264 uint8_t M5_VAR_USED ref_data = 0;
265 auto ref = referenceData.find(req->getPaddr());
266 if (ref == referenceData.end()) {
267 referenceData[req->getPaddr()] = 0;
268 } else {
269 ref_data = ref->second;
270 }
271
272 DPRINTF(MemTest,
273 "Initiating %sread at addr %x (blk %x) expecting %x\n",
274 do_functional ? "functional " : "", req->getPaddr(),
275 blockAlign(req->getPaddr()), ref_data);
276
277 pkt = new Packet(req, MemCmd::ReadReq);
278 pkt->dataDynamic(pkt_data);
279 } else {
280 DPRINTF(MemTest, "Initiating %swrite at addr %x (blk %x) value %x\n",
281 do_functional ? "functional " : "", req->getPaddr(),
282 blockAlign(req->getPaddr()), data);
283
284 pkt = new Packet(req, MemCmd::WriteReq);
285 pkt->dataDynamic(pkt_data);
286 pkt_data[0] = data;
287 }
288
289 // there is no point in ticking if we are waiting for a retry
290 bool keep_ticking = true;
291 if (do_functional) {
292 pkt->setSuppressFuncError();
293 port.sendFunctional(pkt);
294 completeRequest(pkt, true);
295 } else {
296 keep_ticking = sendPkt(pkt);
297 }
298
299 if (keep_ticking) {
300 // schedule the next tick
301 schedule(tickEvent, clockEdge(interval));
302
303 // finally shift the timeout for sending of requests forwards
304 // as we have successfully sent a packet
305 reschedule(noRequestEvent, clockEdge(progressCheck), true);
306 } else {
307 DPRINTF(MemTest, "Waiting for retry\n");
308 }
309}
310
311void
312MemTest::noRequest()
313{
314 panic("%s did not send a request for %d cycles", name(), progressCheck);
315}
316
317void
318MemTest::noResponse()
319{
320 panic("%s did not see a response for %d cycles", name(), progressCheck);
321}
322
323void
324MemTest::recvRetry()
325{
326 assert(retryPkt);
327 if (port.sendTimingReq(retryPkt)) {
328 DPRINTF(MemTest, "Proceeding after successful retry\n");
329
330 retryPkt = nullptr;
331 // kick things into action again
332 schedule(tickEvent, clockEdge(interval));
333 }
334}
335
336MemTest *
337MemTestParams::create()
338{
339 return new MemTest(this);
340}