mem-cache: Fix setting prefetch bit
[gem5.git] / src / mem / mem_checker.cc
1 /*
2 * Copyright (c) 2014 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 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions are
16 * met: redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer;
18 * redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution;
21 * neither the name of the copyright holders nor the names of its
22 * contributors may be used to endorse or promote products derived from
23 * this software without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 */
37
38 #include "mem/mem_checker.hh"
39
40 #include <cassert>
41
42 void
43 MemChecker::WriteCluster::startWrite(MemChecker::Serial serial, Tick _start,
44 uint8_t data)
45 {
46 assert(!isComplete());
47
48 if (start == TICK_FUTURE) {
49 // Initialize a fresh write cluster
50 start = _start;
51 }
52 chatty_assert(start <= _start, "WriteClusters must filled in order!");
53
54 ++numIncomplete;
55
56 if (complete != TICK_FUTURE) {
57 // Reopen a closed write cluster
58 assert(_start < complete); // should open a new write cluster, instead;
59 // also somewhat fishy wrt causality / ordering of calls vs time
60 // progression TODO: Check me!
61 complete = TICK_FUTURE;
62 }
63
64 // Create new transaction, and denote completion time to be in the future.
65 writes.insert(std::make_pair(serial,
66 MemChecker::Transaction(serial, _start, TICK_FUTURE, data)));
67 }
68
69 void
70 MemChecker::WriteCluster::completeWrite(MemChecker::Serial serial, Tick _complete)
71 {
72 auto it = writes.find(serial);
73
74 if (it == writes.end()) {
75 warn("Could not locate write transaction: serial = %d, complete = %d\n",
76 serial, _complete);
77 return;
78 }
79
80 // Record completion time of the write
81 assert(it->second.complete == TICK_FUTURE);
82 it->second.complete = _complete;
83
84 // Update max completion time for the cluster
85 if (completeMax < _complete) {
86 completeMax = _complete;
87 }
88
89 if (--numIncomplete == 0) {
90 // All writes have completed, this cluster is now complete and will be
91 // assigned the max of completion tick values among all writes.
92 //
93 // Note that we cannot simply keep updating complete, because that would
94 // count the cluster as closed already. Instead, we keep TICK_FUTURE
95 // until all writes have completed.
96 complete = completeMax;
97 }
98 }
99
100 void
101 MemChecker::WriteCluster::abortWrite(MemChecker::Serial serial)
102 {
103 if (!writes.erase(serial)) {
104 warn("Could not locate write transaction: serial = %d\n", serial);
105 return;
106 }
107
108 if (--numIncomplete == 0 && !writes.empty()) {
109 // This write cluster is now complete, and we can assign the current
110 // completeMax value.
111 complete = completeMax;
112 }
113
114 // Note: this WriteCluster is in pristine state if this was the only
115 // write present; the cluster will get reused through
116 // getIncompleteWriteCluster().
117 }
118
119 void
120 MemChecker::ByteTracker::startRead(MemChecker::Serial serial, Tick start)
121 {
122 outstandingReads.insert(std::make_pair(serial,
123 MemChecker::Transaction(serial, start, TICK_FUTURE)));
124 }
125
126 bool
127 MemChecker::ByteTracker::inExpectedData(Tick start, Tick complete, uint8_t data)
128 {
129 _lastExpectedData.clear();
130
131 bool wc_overlap = true;
132
133 // Find the last value read from the location
134 const Transaction& last_obs =
135 *lastCompletedTransaction(&readObservations, start);
136 bool last_obs_valid = (last_obs.complete != TICK_INITIAL);
137
138 // Scan backwards through the write clusters to find the closest younger
139 // preceding & overlapping writes.
140 for (auto cluster = writeClusters.rbegin();
141 cluster != writeClusters.rend() && wc_overlap; ++cluster) {
142 for (const auto& addr_write : cluster->writes) {
143 const Transaction& write = addr_write.second;
144
145 if (write.complete < last_obs.start) {
146 // If this write transaction completed before the last
147 // observation, we ignore it as the last_observation has the
148 // correct value
149 continue;
150 }
151
152 if (write.data == data) {
153 // Found a match, end search.
154 return true;
155 }
156
157 // Record possible, but non-matching data for debugging
158 _lastExpectedData.push_back(write.data);
159
160 if (write.complete > start) {
161 // This write overlapped with the transaction we want to check
162 // -> continue checking the overlapping write cluster
163 continue;
164 }
165
166 // This write cluster has writes that have completed before the
167 // checked transaction. There is no need to check an earlier
168 // write-cluster -> set the exit condition for the outer loop
169 wc_overlap = false;
170
171 if (last_obs.complete < write.start) {
172 // We found a write which started after the last observed read,
173 // therefore we can not longer consider the value seen by the
174 // last observation as a valid expected value.
175 //
176 // Once all writes have been iterated through, we can check if
177 // the last observation is still valid to compare against.
178 last_obs_valid = false;
179 }
180 }
181 }
182
183 // We have not found any matching write, so far; check other sources of
184 // confirmation
185 if (last_obs_valid) {
186 // The last observation is not outdated according to the writes we have
187 // seen so far.
188 assert(last_obs.complete <= start);
189 if (last_obs.data == data) {
190 // Matched data from last observation -> all good
191 return true;
192 }
193 // Record non-matching, but possible value
194 _lastExpectedData.push_back(last_obs.data);
195 } else {
196 // We have not seen any valid observation, and the only writes
197 // observed are overlapping, so anything (in particular the
198 // initialisation value) goes
199 // NOTE: We can overlap with multiple write clusters, here
200 if (!writeClusters.empty() && wc_overlap) {
201 // ensure that all write clusters really overlap this read
202 assert(writeClusters.begin()->start < complete &&
203 writeClusters.rbegin()->complete > start);
204 return true;
205 }
206 }
207
208 if (_lastExpectedData.empty()) {
209 assert(last_obs.complete == TICK_INITIAL);
210 // We have not found any possible (non-matching data). Can happen in
211 // initial system state
212 DPRINTF(MemChecker, "no last observation nor write! start = %d, "\
213 "complete = %d, data = %#x\n", start, complete, data);
214 return true;
215 }
216 return false;
217 }
218
219 bool
220 MemChecker::ByteTracker::completeRead(MemChecker::Serial serial,
221 Tick complete, uint8_t data)
222 {
223 auto it = outstandingReads.find(serial);
224
225 if (it == outstandingReads.end()) {
226 // Can happen if concurrent with reset_address_range
227 warn("Could not locate read transaction: serial = %d, complete = %d\n",
228 serial, complete);
229 return true;
230 }
231
232 Tick start = it->second.start;
233 outstandingReads.erase(it);
234
235 // Verify data
236 const bool result = inExpectedData(start, complete, data);
237
238 readObservations.emplace_back(serial, start, complete, data);
239 pruneTransactions();
240
241 return result;
242 }
243
244 MemChecker::WriteCluster*
245 MemChecker::ByteTracker::getIncompleteWriteCluster()
246 {
247 if (writeClusters.empty() || writeClusters.back().isComplete()) {
248 writeClusters.emplace_back();
249 }
250
251 return &writeClusters.back();
252 }
253
254 void
255 MemChecker::ByteTracker::startWrite(MemChecker::Serial serial, Tick start,
256 uint8_t data)
257 {
258 getIncompleteWriteCluster()->startWrite(serial, start, data);
259 }
260
261 void
262 MemChecker::ByteTracker::completeWrite(MemChecker::Serial serial, Tick complete)
263 {
264 getIncompleteWriteCluster()->completeWrite(serial, complete);
265 pruneTransactions();
266 }
267
268 void
269 MemChecker::ByteTracker::abortWrite(MemChecker::Serial serial)
270 {
271 getIncompleteWriteCluster()->abortWrite(serial);
272 }
273
274 void
275 MemChecker::ByteTracker::pruneTransactions()
276 {
277 // Obtain tick of first outstanding read. If there are no outstanding
278 // reads, we use curTick(), i.e. we will remove all readObservation except
279 // the most recent one.
280 const Tick before = outstandingReads.empty() ? curTick() :
281 outstandingReads.begin()->second.start;
282
283 // Pruning of readObservations
284 readObservations.erase(readObservations.begin(),
285 lastCompletedTransaction(&readObservations, before));
286
287 // Pruning of writeClusters
288 if (!writeClusters.empty()) {
289 writeClusters.erase(writeClusters.begin(),
290 lastCompletedTransaction(&writeClusters, before));
291 }
292 }
293
294 bool
295 MemChecker::completeRead(MemChecker::Serial serial, Tick complete,
296 Addr addr, size_t size, uint8_t *data)
297 {
298 bool result = true;
299
300 DPRINTF(MemChecker,
301 "completing read: serial = %d, complete = %d, "
302 "addr = %#llx, size = %d\n", serial, complete, addr, size);
303
304 for (size_t i = 0; i < size; ++i) {
305 ByteTracker *tracker = getByteTracker(addr + i);
306
307 if (!tracker->completeRead(serial, complete, data[i])) {
308 // Generate error message, and aggregate all failures for the bytes
309 // considered in this transaction in one message.
310 if (result) {
311 result = false;
312 errorMessage = "";
313 } else {
314 errorMessage += "\n";
315 }
316
317 errorMessage += csprintf(" Read transaction for address %#llx "
318 "failed: received %#x, expected ",
319 (unsigned long long)(addr + i), data[i]);
320
321 for (size_t j = 0; j < tracker->lastExpectedData().size(); ++j) {
322 errorMessage +=
323 csprintf("%#x%s",
324 tracker->lastExpectedData()[j],
325 (j == tracker->lastExpectedData().size() - 1)
326 ? "" : "|");
327 }
328 }
329 }
330
331 if (!result) {
332 DPRINTF(MemChecker, "read of %#llx @ cycle %d failed:\n%s\n", addr,
333 complete, errorMessage);
334 }
335
336 return result;
337 }
338
339 void
340 MemChecker::reset(Addr addr, size_t size)
341 {
342 for (size_t i = 0; i < size; ++i) {
343 byte_trackers.erase(addr + i);
344 }
345 }