memtest.cc (11435:0f1b46dde3fa) memtest.cc (11523:81332eb10367)
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 "base/random.hh"
46#include "base/statistics.hh"
47#include "cpu/testers/memtest/memtest.hh"
48#include "debug/MemTest.hh"
49#include "mem/mem_object.hh"
50#include "sim/sim_exit.hh"
51#include "sim/stats.hh"
52#include "sim/system.hh"
53
54using namespace std;
55
56unsigned int TESTER_ALLOCATOR = 0;
57
58bool
59MemTest::CpuPort::recvTimingResp(PacketPtr pkt)
60{
61 memtest.completeRequest(pkt);
62 return true;
63}
64
65void
66MemTest::CpuPort::recvReqRetry()
67{
68 memtest.recvRetry();
69}
70
71bool
72MemTest::sendPkt(PacketPtr pkt) {
73 if (atomic) {
74 port.sendAtomic(pkt);
75 completeRequest(pkt);
76 } else {
77 if (!port.sendTimingReq(pkt)) {
78 retryPkt = pkt;
79 return false;
80 }
81 }
82 return true;
83}
84
85MemTest::MemTest(const Params *p)
86 : MemObject(p),
87 tickEvent(this),
88 noRequestEvent(this),
89 noResponseEvent(this),
90 port("port", *this),
91 retryPkt(nullptr),
92 size(p->size),
93 interval(p->interval),
94 percentReads(p->percent_reads),
95 percentFunctional(p->percent_functional),
96 percentUncacheable(p->percent_uncacheable),
97 masterId(p->system->getMasterId(name())),
98 blockSize(p->system->cacheLineSize()),
99 blockAddrMask(blockSize - 1),
100 progressInterval(p->progress_interval),
101 progressCheck(p->progress_check),
102 nextProgressMessage(p->progress_interval),
103 maxLoads(p->max_loads),
104 atomic(p->system->isAtomicMode()),
105 suppressFuncWarnings(p->suppress_func_warnings)
106{
107 id = TESTER_ALLOCATOR++;
108 fatal_if(id >= blockSize, "Too many testers, only %d allowed\n",
109 blockSize - 1);
110
111 baseAddr1 = 0x100000;
112 baseAddr2 = 0x400000;
113 uncacheAddr = 0x800000;
114
115 // set up counters
116 numReads = 0;
117 numWrites = 0;
118
119 // kick things into action
120 schedule(tickEvent, curTick());
121 schedule(noRequestEvent, clockEdge(progressCheck));
122 schedule(noResponseEvent, clockEdge(progressCheck));
123}
124
125BaseMasterPort &
126MemTest::getMasterPort(const std::string &if_name, PortID idx)
127{
128 if (if_name == "port")
129 return port;
130 else
131 return MemObject::getMasterPort(if_name, idx);
132}
133
134void
135MemTest::completeRequest(PacketPtr pkt, bool functional)
136{
137 Request *req = pkt->req;
138 assert(req->getSize() == 1);
139
140 // this address is no longer outstanding
141 auto remove_addr = outstandingAddrs.find(req->getPaddr());
142 assert(remove_addr != outstandingAddrs.end());
143 outstandingAddrs.erase(remove_addr);
144
145 DPRINTF(MemTest, "Completing %s at address %x (blk %x) %s\n",
146 pkt->isWrite() ? "write" : "read",
147 req->getPaddr(), blockAlign(req->getPaddr()),
148 pkt->isError() ? "error" : "success");
149
150 const uint8_t *pkt_data = pkt->getConstPtr<uint8_t>();
151
152 if (pkt->isError()) {
153 if (!functional || !suppressFuncWarnings) {
154 warn("%s access failed at %#x\n",
155 pkt->isWrite() ? "Write" : "Read", req->getPaddr());
156 }
157 } else {
158 if (pkt->isRead()) {
159 uint8_t ref_data = referenceData[req->getPaddr()];
160 if (pkt_data[0] != ref_data) {
161 panic("%s: read of %x (blk %x) @ cycle %d "
162 "returns %x, expected %x\n", name(),
163 req->getPaddr(), blockAlign(req->getPaddr()), curTick(),
164 pkt_data[0], ref_data);
165 }
166
167 numReads++;
168 numReadsStat++;
169
170 if (numReads == (uint64_t)nextProgressMessage) {
171 ccprintf(cerr, "%s: completed %d read, %d write accesses @%d\n",
172 name(), numReads, numWrites, curTick());
173 nextProgressMessage += progressInterval;
174 }
175
176 if (maxLoads != 0 && numReads >= maxLoads)
177 exitSimLoop("maximum number of loads reached");
178 } else {
179 assert(pkt->isWrite());
180
181 // update the reference data
182 referenceData[req->getPaddr()] = pkt_data[0];
183 numWrites++;
184 numWritesStat++;
185 }
186 }
187
188 delete pkt->req;
189
190 // the packet will delete the data
191 delete pkt;
192
193 // finally shift the response timeout forward
194 reschedule(noResponseEvent, clockEdge(progressCheck), true);
195}
196
197void
198MemTest::regStats()
199{
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 "base/random.hh"
46#include "base/statistics.hh"
47#include "cpu/testers/memtest/memtest.hh"
48#include "debug/MemTest.hh"
49#include "mem/mem_object.hh"
50#include "sim/sim_exit.hh"
51#include "sim/stats.hh"
52#include "sim/system.hh"
53
54using namespace std;
55
56unsigned int TESTER_ALLOCATOR = 0;
57
58bool
59MemTest::CpuPort::recvTimingResp(PacketPtr pkt)
60{
61 memtest.completeRequest(pkt);
62 return true;
63}
64
65void
66MemTest::CpuPort::recvReqRetry()
67{
68 memtest.recvRetry();
69}
70
71bool
72MemTest::sendPkt(PacketPtr pkt) {
73 if (atomic) {
74 port.sendAtomic(pkt);
75 completeRequest(pkt);
76 } else {
77 if (!port.sendTimingReq(pkt)) {
78 retryPkt = pkt;
79 return false;
80 }
81 }
82 return true;
83}
84
85MemTest::MemTest(const Params *p)
86 : MemObject(p),
87 tickEvent(this),
88 noRequestEvent(this),
89 noResponseEvent(this),
90 port("port", *this),
91 retryPkt(nullptr),
92 size(p->size),
93 interval(p->interval),
94 percentReads(p->percent_reads),
95 percentFunctional(p->percent_functional),
96 percentUncacheable(p->percent_uncacheable),
97 masterId(p->system->getMasterId(name())),
98 blockSize(p->system->cacheLineSize()),
99 blockAddrMask(blockSize - 1),
100 progressInterval(p->progress_interval),
101 progressCheck(p->progress_check),
102 nextProgressMessage(p->progress_interval),
103 maxLoads(p->max_loads),
104 atomic(p->system->isAtomicMode()),
105 suppressFuncWarnings(p->suppress_func_warnings)
106{
107 id = TESTER_ALLOCATOR++;
108 fatal_if(id >= blockSize, "Too many testers, only %d allowed\n",
109 blockSize - 1);
110
111 baseAddr1 = 0x100000;
112 baseAddr2 = 0x400000;
113 uncacheAddr = 0x800000;
114
115 // set up counters
116 numReads = 0;
117 numWrites = 0;
118
119 // kick things into action
120 schedule(tickEvent, curTick());
121 schedule(noRequestEvent, clockEdge(progressCheck));
122 schedule(noResponseEvent, clockEdge(progressCheck));
123}
124
125BaseMasterPort &
126MemTest::getMasterPort(const std::string &if_name, PortID idx)
127{
128 if (if_name == "port")
129 return port;
130 else
131 return MemObject::getMasterPort(if_name, idx);
132}
133
134void
135MemTest::completeRequest(PacketPtr pkt, bool functional)
136{
137 Request *req = pkt->req;
138 assert(req->getSize() == 1);
139
140 // this address is no longer outstanding
141 auto remove_addr = outstandingAddrs.find(req->getPaddr());
142 assert(remove_addr != outstandingAddrs.end());
143 outstandingAddrs.erase(remove_addr);
144
145 DPRINTF(MemTest, "Completing %s at address %x (blk %x) %s\n",
146 pkt->isWrite() ? "write" : "read",
147 req->getPaddr(), blockAlign(req->getPaddr()),
148 pkt->isError() ? "error" : "success");
149
150 const uint8_t *pkt_data = pkt->getConstPtr<uint8_t>();
151
152 if (pkt->isError()) {
153 if (!functional || !suppressFuncWarnings) {
154 warn("%s access failed at %#x\n",
155 pkt->isWrite() ? "Write" : "Read", req->getPaddr());
156 }
157 } else {
158 if (pkt->isRead()) {
159 uint8_t ref_data = referenceData[req->getPaddr()];
160 if (pkt_data[0] != ref_data) {
161 panic("%s: read of %x (blk %x) @ cycle %d "
162 "returns %x, expected %x\n", name(),
163 req->getPaddr(), blockAlign(req->getPaddr()), curTick(),
164 pkt_data[0], ref_data);
165 }
166
167 numReads++;
168 numReadsStat++;
169
170 if (numReads == (uint64_t)nextProgressMessage) {
171 ccprintf(cerr, "%s: completed %d read, %d write accesses @%d\n",
172 name(), numReads, numWrites, curTick());
173 nextProgressMessage += progressInterval;
174 }
175
176 if (maxLoads != 0 && numReads >= maxLoads)
177 exitSimLoop("maximum number of loads reached");
178 } else {
179 assert(pkt->isWrite());
180
181 // update the reference data
182 referenceData[req->getPaddr()] = pkt_data[0];
183 numWrites++;
184 numWritesStat++;
185 }
186 }
187
188 delete pkt->req;
189
190 // the packet will delete the data
191 delete pkt;
192
193 // finally shift the response timeout forward
194 reschedule(noResponseEvent, clockEdge(progressCheck), true);
195}
196
197void
198MemTest::regStats()
199{
200 MemObject::regStats();
201
200 using namespace Stats;
201
202 numReadsStat
203 .name(name() + ".num_reads")
204 .desc("number of read accesses completed")
205 ;
206
207 numWritesStat
208 .name(name() + ".num_writes")
209 .desc("number of write accesses completed")
210 ;
211}
212
213void
214MemTest::tick()
215{
216 // we should never tick if we are waiting for a retry
217 assert(!retryPkt);
218
219 // create a new request
220 unsigned cmd = random_mt.random(0, 100);
221 uint8_t data = random_mt.random<uint8_t>();
222 bool uncacheable = random_mt.random(0, 100) < percentUncacheable;
223 unsigned base = random_mt.random(0, 1);
224 Request::Flags flags;
225 Addr paddr;
226
227 // generate a unique address
228 do {
229 unsigned offset = random_mt.random<unsigned>(0, size - 1);
230
231 // use the tester id as offset within the block for false sharing
232 offset = blockAlign(offset);
233 offset += id;
234
235 if (uncacheable) {
236 flags.set(Request::UNCACHEABLE);
237 paddr = uncacheAddr + offset;
238 } else {
239 paddr = ((base) ? baseAddr1 : baseAddr2) + offset;
240 }
241 } while (outstandingAddrs.find(paddr) != outstandingAddrs.end());
242
243 bool do_functional = (random_mt.random(0, 100) < percentFunctional) &&
244 !uncacheable;
245 Request *req = new Request(paddr, 1, flags, masterId);
246 req->setContext(id);
247
248 outstandingAddrs.insert(paddr);
249
250 // sanity check
251 panic_if(outstandingAddrs.size() > 100,
252 "Tester %s has more than 100 outstanding requests\n", name());
253
254 PacketPtr pkt = nullptr;
255 uint8_t *pkt_data = new uint8_t[1];
256
257 if (cmd < percentReads) {
258 // start by ensuring there is a reference value if we have not
259 // seen this address before
260 uint8_t M5_VAR_USED ref_data = 0;
261 auto ref = referenceData.find(req->getPaddr());
262 if (ref == referenceData.end()) {
263 referenceData[req->getPaddr()] = 0;
264 } else {
265 ref_data = ref->second;
266 }
267
268 DPRINTF(MemTest,
269 "Initiating %sread at addr %x (blk %x) expecting %x\n",
270 do_functional ? "functional " : "", req->getPaddr(),
271 blockAlign(req->getPaddr()), ref_data);
272
273 pkt = new Packet(req, MemCmd::ReadReq);
274 pkt->dataDynamic(pkt_data);
275 } else {
276 DPRINTF(MemTest, "Initiating %swrite at addr %x (blk %x) value %x\n",
277 do_functional ? "functional " : "", req->getPaddr(),
278 blockAlign(req->getPaddr()), data);
279
280 pkt = new Packet(req, MemCmd::WriteReq);
281 pkt->dataDynamic(pkt_data);
282 pkt_data[0] = data;
283 }
284
285 // there is no point in ticking if we are waiting for a retry
286 bool keep_ticking = true;
287 if (do_functional) {
288 pkt->setSuppressFuncError();
289 port.sendFunctional(pkt);
290 completeRequest(pkt, true);
291 } else {
292 keep_ticking = sendPkt(pkt);
293 }
294
295 if (keep_ticking) {
296 // schedule the next tick
297 schedule(tickEvent, clockEdge(interval));
298
299 // finally shift the timeout for sending of requests forwards
300 // as we have successfully sent a packet
301 reschedule(noRequestEvent, clockEdge(progressCheck), true);
302 } else {
303 DPRINTF(MemTest, "Waiting for retry\n");
304 }
305}
306
307void
308MemTest::noRequest()
309{
310 panic("%s did not send a request for %d cycles", name(), progressCheck);
311}
312
313void
314MemTest::noResponse()
315{
316 panic("%s did not see a response for %d cycles", name(), progressCheck);
317}
318
319void
320MemTest::recvRetry()
321{
322 assert(retryPkt);
323 if (port.sendTimingReq(retryPkt)) {
324 DPRINTF(MemTest, "Proceeding after successful retry\n");
325
326 retryPkt = nullptr;
327 // kick things into action again
328 schedule(tickEvent, clockEdge(interval));
329 }
330}
331
332MemTest *
333MemTestParams::create()
334{
335 return new MemTest(this);
336}
202 using namespace Stats;
203
204 numReadsStat
205 .name(name() + ".num_reads")
206 .desc("number of read accesses completed")
207 ;
208
209 numWritesStat
210 .name(name() + ".num_writes")
211 .desc("number of write accesses completed")
212 ;
213}
214
215void
216MemTest::tick()
217{
218 // we should never tick if we are waiting for a retry
219 assert(!retryPkt);
220
221 // create a new request
222 unsigned cmd = random_mt.random(0, 100);
223 uint8_t data = random_mt.random<uint8_t>();
224 bool uncacheable = random_mt.random(0, 100) < percentUncacheable;
225 unsigned base = random_mt.random(0, 1);
226 Request::Flags flags;
227 Addr paddr;
228
229 // generate a unique address
230 do {
231 unsigned offset = random_mt.random<unsigned>(0, size - 1);
232
233 // use the tester id as offset within the block for false sharing
234 offset = blockAlign(offset);
235 offset += id;
236
237 if (uncacheable) {
238 flags.set(Request::UNCACHEABLE);
239 paddr = uncacheAddr + offset;
240 } else {
241 paddr = ((base) ? baseAddr1 : baseAddr2) + offset;
242 }
243 } while (outstandingAddrs.find(paddr) != outstandingAddrs.end());
244
245 bool do_functional = (random_mt.random(0, 100) < percentFunctional) &&
246 !uncacheable;
247 Request *req = new Request(paddr, 1, flags, masterId);
248 req->setContext(id);
249
250 outstandingAddrs.insert(paddr);
251
252 // sanity check
253 panic_if(outstandingAddrs.size() > 100,
254 "Tester %s has more than 100 outstanding requests\n", name());
255
256 PacketPtr pkt = nullptr;
257 uint8_t *pkt_data = new uint8_t[1];
258
259 if (cmd < percentReads) {
260 // start by ensuring there is a reference value if we have not
261 // seen this address before
262 uint8_t M5_VAR_USED ref_data = 0;
263 auto ref = referenceData.find(req->getPaddr());
264 if (ref == referenceData.end()) {
265 referenceData[req->getPaddr()] = 0;
266 } else {
267 ref_data = ref->second;
268 }
269
270 DPRINTF(MemTest,
271 "Initiating %sread at addr %x (blk %x) expecting %x\n",
272 do_functional ? "functional " : "", req->getPaddr(),
273 blockAlign(req->getPaddr()), ref_data);
274
275 pkt = new Packet(req, MemCmd::ReadReq);
276 pkt->dataDynamic(pkt_data);
277 } else {
278 DPRINTF(MemTest, "Initiating %swrite at addr %x (blk %x) value %x\n",
279 do_functional ? "functional " : "", req->getPaddr(),
280 blockAlign(req->getPaddr()), data);
281
282 pkt = new Packet(req, MemCmd::WriteReq);
283 pkt->dataDynamic(pkt_data);
284 pkt_data[0] = data;
285 }
286
287 // there is no point in ticking if we are waiting for a retry
288 bool keep_ticking = true;
289 if (do_functional) {
290 pkt->setSuppressFuncError();
291 port.sendFunctional(pkt);
292 completeRequest(pkt, true);
293 } else {
294 keep_ticking = sendPkt(pkt);
295 }
296
297 if (keep_ticking) {
298 // schedule the next tick
299 schedule(tickEvent, clockEdge(interval));
300
301 // finally shift the timeout for sending of requests forwards
302 // as we have successfully sent a packet
303 reschedule(noRequestEvent, clockEdge(progressCheck), true);
304 } else {
305 DPRINTF(MemTest, "Waiting for retry\n");
306 }
307}
308
309void
310MemTest::noRequest()
311{
312 panic("%s did not send a request for %d cycles", name(), progressCheck);
313}
314
315void
316MemTest::noResponse()
317{
318 panic("%s did not see a response for %d cycles", name(), progressCheck);
319}
320
321void
322MemTest::recvRetry()
323{
324 assert(retryPkt);
325 if (port.sendTimingReq(retryPkt)) {
326 DPRINTF(MemTest, "Proceeding after successful retry\n");
327
328 retryPkt = nullptr;
329 // kick things into action again
330 schedule(tickEvent, clockEdge(interval));
331 }
332}
333
334MemTest *
335MemTestParams::create()
336{
337 return new MemTest(this);
338}