gem5
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
mem_checker.cc
Go to the documentation of this file.
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  * Authors: Rune Holm
38  * Marco Elver
39  */
40 
41 #include "mem/mem_checker.hh"
42 
43 #include <cassert>
44 
45 void
47  uint8_t data)
48 {
49  assert(!isComplete());
50 
51  if (start == TICK_FUTURE) {
52  // Initialize a fresh write cluster
53  start = _start;
54  }
55  chatty_assert(start <= _start, "WriteClusters must filled in order!");
56 
57  ++numIncomplete;
58 
59  if (complete != TICK_FUTURE) {
60  // Reopen a closed write cluster
61  assert(_start < complete); // should open a new write cluster, instead;
62  // also somewhat fishy wrt causality / ordering of calls vs time
63  // progression TODO: Check me!
65  }
66 
67  // Create new transaction, and denote completion time to be in the future.
68  writes.insert(std::make_pair(serial,
69  MemChecker::Transaction(serial, _start, TICK_FUTURE, data)));
70 }
71 
72 void
74 {
75  auto it = writes.find(serial);
76 
77  if (it == writes.end()) {
78  warn("Could not locate write transaction: serial = %d, complete = %d\n",
79  serial, _complete);
80  return;
81  }
82 
83  // Record completion time of the write
84  assert(it->second.complete == TICK_FUTURE);
85  it->second.complete = _complete;
86 
87  // Update max completion time for the cluster
88  if (completeMax < _complete) {
89  completeMax = _complete;
90  }
91 
92  if (--numIncomplete == 0) {
93  // All writes have completed, this cluster is now complete and will be
94  // assigned the max of completion tick values among all writes.
95  //
96  // Note that we cannot simply keep updating complete, because that would
97  // count the cluster as closed already. Instead, we keep TICK_FUTURE
98  // until all writes have completed.
99  complete = completeMax;
100  }
101 }
102 
103 void
105 {
106  if (!writes.erase(serial)) {
107  warn("Could not locate write transaction: serial = %d\n", serial);
108  return;
109  }
110 
111  if (--numIncomplete == 0 && !writes.empty()) {
112  // This write cluster is now complete, and we can assign the current
113  // completeMax value.
114  complete = completeMax;
115  }
116 
117  // Note: this WriteCluster is in pristine state if this was the only
118  // write present; the cluster will get reused through
119  // getIncompleteWriteCluster().
120 }
121 
122 void
124 {
125  outstandingReads.insert(std::make_pair(serial,
126  MemChecker::Transaction(serial, start, TICK_FUTURE)));
127 }
128 
129 bool
131 {
132  _lastExpectedData.clear();
133 
134  bool wc_overlap = true;
135 
136  // Find the last value read from the location
137  const Transaction& last_obs =
138  *lastCompletedTransaction(&readObservations, start);
139  bool last_obs_valid = (last_obs.complete != TICK_INITIAL);
140 
141  // Scan backwards through the write clusters to find the closest younger
142  // preceding & overlapping writes.
143  for (auto cluster = writeClusters.rbegin();
144  cluster != writeClusters.rend() && wc_overlap; ++cluster) {
145  for (const auto& addr_write : cluster->writes) {
146  const Transaction& write = addr_write.second;
147 
148  if (write.complete < last_obs.start) {
149  // If this write transaction completed before the last
150  // observation, we ignore it as the last_observation has the
151  // correct value
152  continue;
153  }
154 
155  if (write.data == data) {
156  // Found a match, end search.
157  return true;
158  }
159 
160  // Record possible, but non-matching data for debugging
161  _lastExpectedData.push_back(write.data);
162 
163  if (write.complete > start) {
164  // This write overlapped with the transaction we want to check
165  // -> continue checking the overlapping write cluster
166  continue;
167  }
168 
169  // This write cluster has writes that have completed before the
170  // checked transaction. There is no need to check an earlier
171  // write-cluster -> set the exit condition for the outer loop
172  wc_overlap = false;
173 
174  if (last_obs.complete < write.start) {
175  // We found a write which started after the last observed read,
176  // therefore we can not longer consider the value seen by the
177  // last observation as a valid expected value.
178  //
179  // Once all writes have been iterated through, we can check if
180  // the last observation is still valid to compare against.
181  last_obs_valid = false;
182  }
183  }
184  }
185 
186  // We have not found any matching write, so far; check other sources of
187  // confirmation
188  if (last_obs_valid) {
189  // The last observation is not outdated according to the writes we have
190  // seen so far.
191  assert(last_obs.complete <= start);
192  if (last_obs.data == data) {
193  // Matched data from last observation -> all good
194  return true;
195  }
196  // Record non-matching, but possible value
197  _lastExpectedData.push_back(last_obs.data);
198  } else {
199  // We have not seen any valid observation, and the only writes
200  // observed are overlapping, so anything (in particular the
201  // initialisation value) goes
202  // NOTE: We can overlap with multiple write clusters, here
203  if (!writeClusters.empty() && wc_overlap) {
204  // ensure that all write clusters really overlap this read
205  assert(writeClusters.begin()->start < complete &&
206  writeClusters.rbegin()->complete > start);
207  return true;
208  }
209  }
210 
211  if (_lastExpectedData.empty()) {
212  assert(last_obs.complete == TICK_INITIAL);
213  // We have not found any possible (non-matching data). Can happen in
214  // initial system state
215  DPRINTF(MemChecker, "no last observation nor write! start = %d, "\
216  "complete = %d, data = %#x\n", start, complete, data);
217  return true;
218  }
219  return false;
220 }
221 
222 bool
224  Tick complete, uint8_t data)
225 {
226  auto it = outstandingReads.find(serial);
227 
228  if (it == outstandingReads.end()) {
229  // Can happen if concurrent with reset_address_range
230  warn("Could not locate read transaction: serial = %d, complete = %d\n",
231  serial, complete);
232  return true;
233  }
234 
235  Tick start = it->second.start;
236  outstandingReads.erase(it);
237 
238  // Verify data
239  const bool result = inExpectedData(start, complete, data);
240 
241  readObservations.emplace_back(serial, start, complete, data);
242  pruneTransactions();
243 
244  return result;
245 }
246 
249 {
250  if (writeClusters.empty() || writeClusters.back().isComplete()) {
251  writeClusters.emplace_back();
252  }
253 
254  return &writeClusters.back();
255 }
256 
257 void
259  uint8_t data)
260 {
261  getIncompleteWriteCluster()->startWrite(serial, start, data);
262 }
263 
264 void
266 {
267  getIncompleteWriteCluster()->completeWrite(serial, complete);
268  pruneTransactions();
269 }
270 
271 void
273 {
274  getIncompleteWriteCluster()->abortWrite(serial);
275 }
276 
277 void
279 {
280  // Obtain tick of first outstanding read. If there are no outstanding
281  // reads, we use curTick(), i.e. we will remove all readObservation except
282  // the most recent one.
283  const Tick before = outstandingReads.empty() ? curTick() :
284  outstandingReads.begin()->second.start;
285 
286  // Pruning of readObservations
287  readObservations.erase(readObservations.begin(),
288  lastCompletedTransaction(&readObservations, before));
289 
290  // Pruning of writeClusters
291  if (!writeClusters.empty()) {
292  writeClusters.erase(writeClusters.begin(),
293  lastCompletedTransaction(&writeClusters, before));
294  }
295 }
296 
297 bool
299  Addr addr, size_t size, uint8_t *data)
300 {
301  bool result = true;
302 
304  "completing read: serial = %d, complete = %d, "
305  "addr = %#llx, size = %d\n", serial, complete, addr, size);
306 
307  for (size_t i = 0; i < size; ++i) {
308  ByteTracker *tracker = getByteTracker(addr + i);
309 
310  if (!tracker->completeRead(serial, complete, data[i])) {
311  // Generate error message, and aggregate all failures for the bytes
312  // considered in this transaction in one message.
313  if (result) {
314  result = false;
315  errorMessage = "";
316  } else {
317  errorMessage += "\n";
318  }
319 
320  errorMessage += csprintf(" Read transaction for address %#llx "
321  "failed: received %#x, expected ",
322  (unsigned long long)(addr + i), data[i]);
323 
324  for (size_t j = 0; j < tracker->lastExpectedData().size(); ++j) {
325  errorMessage +=
326  csprintf("%#x%s",
327  tracker->lastExpectedData()[j],
328  (j == tracker->lastExpectedData().size() - 1)
329  ? "" : "|");
330  }
331  }
332  }
333 
334  if (!result) {
335  DPRINTF(MemChecker, "read of %#llx @ cycle %d failed:\n%s\n", addr,
336  complete, errorMessage);
337  }
338 
339  return result;
340 }
341 
342 void
344 {
345  for (size_t i = 0; i < size; ++i) {
346  byte_trackers.erase(addr + i);
347  }
348 }
349 
350 MemChecker*
351 MemCheckerParams::create()
352 {
353  return new MemChecker(this);
354 }
#define chatty_assert(cond,...)
The chatty assert macro will function like a normal assert, but will allow the specification of addit...
Definition: misc.hh:259
#define DPRINTF(x,...)
Definition: trace.hh:212
void abortWrite(Serial serial)
Aborts a write transaction.
Definition: mem_checker.cc:104
Bitfield< 7 > i
Definition: miscregs.hh:1378
Tick complete
Completion tick.
Definition: mem_checker.hh:120
bool completeRead(Serial serial, Tick complete, Addr addr, size_t size, uint8_t *data)
Completes a previously started read transaction.
Definition: mem_checker.cc:298
ByteTracker * getByteTracker(Addr addr)
Returns the instance of ByteTracker for the requested location.
Definition: mem_checker.hh:480
ip6_addr_t addr
Definition: inet.hh:335
The Transaction class captures the lifetimes of read and write operations, and the values they consum...
Definition: mem_checker.hh:105
std::unordered_map< Serial, Transaction > writes
Map of Serial –> Transaction of all writes in cluster; contains all, in-flight or already completed...
Definition: mem_checker.hh:188
void completeWrite(Serial serial, Tick _complete)
Completes a write transaction.
Definition: mem_checker.cc:73
uint64_t Serial
The Serial type is used to be able to uniquely identify a transaction as it passes through the system...
Definition: mem_checker.hh:80
const char data[]
Definition: circlebuf.cc:43
MemChecker(const MemCheckerParams *p)
Definition: mem_checker.hh:375
#define warn(...)
Definition: misc.hh:219
MemChecker.
Definition: mem_checker.hh:72
Tick curTick()
The current simulated tick.
Definition: core.hh:47
std::string csprintf(const char *format, const Args &...args)
Definition: cprintf.hh:161
std::unordered_map< Addr, ByteTracker > byte_trackers
Maintain a map of address –> byte-tracker.
Definition: mem_checker.hh:513
uint64_t Tick
Tick count type.
Definition: types.hh:63
The ByteTracker keeps track of transactions for the same byte – all outstanding reads, the completed reads (and what they observed) and write clusters (see WriteCluster).
Definition: mem_checker.hh:203
void reset()
Resets the entire checker.
Definition: mem_checker.hh:452
std::string errorMessage
Detailed error message of the last violation in completeRead.
Definition: mem_checker.hh:488
void completeWrite(Serial serial, Tick complete)
Completes a write transaction.
Definition: mem_checker.cc:265
WriteCluster * getIncompleteWriteCluster()
Convenience function to return the most recent incomplete write cluster.
Definition: mem_checker.cc:248
uint64_t Addr
Address type This will probably be moved somewhere else in the near future.
Definition: types.hh:142
const std::vector< uint8_t > & lastExpectedData() const
This function returns the expected data that inExpectedData iterated through in the last call...
Definition: mem_checker.hh:299
Bitfield< 24 > j
Definition: miscregs.hh:1369
void startWrite(Serial serial, Tick start, uint8_t data)
Starts a write transaction.
Definition: mem_checker.cc:258
bool inExpectedData(Tick start, Tick complete, uint8_t data)
Given a start and end time (of any read transaction), this function iterates through all data that su...
Definition: mem_checker.cc:130
static const Tick TICK_INITIAL
The initial tick the system starts with.
Definition: mem_checker.hh:89
int size()
Definition: pagetable.hh:146
The WriteCluster class captures sets of writes where all writes are overlapping with at least one oth...
Definition: mem_checker.hh:141
Tick start
Start of earliest write in cluster.
Definition: mem_checker.hh:181
bool completeRead(Serial serial, Tick complete, uint8_t data)
Completes a read transaction that is still outstanding.
Definition: mem_checker.cc:223
void abortWrite(Serial serial)
Aborts a write transaction.
Definition: mem_checker.cc:272
uint8_t data
Depending on the memory operation, the data value either represents: for writes, the value written up...
Definition: mem_checker.hh:127
void startWrite(Serial serial, Tick _start, uint8_t data)
Starts a write transaction.
Definition: mem_checker.cc:46
void pruneTransactions()
Prunes no longer needed transactions.
Definition: mem_checker.cc:278
Tick start
Start tick.
Definition: mem_checker.hh:119
static const Tick TICK_FUTURE
The maximum value that curTick() could ever return.
Definition: mem_checker.hh:94
Tick complete
Completion of last write in cluster.
Definition: mem_checker.hh:182
void startRead(Serial serial, Tick start)
Starts a read transaction.
Definition: mem_checker.cc:123
bool isComplete() const
Definition: mem_checker.hh:178

Generated on Fri Jun 9 2017 13:03:49 for gem5 by doxygen 1.8.6