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

Generated on Tue Sep 21 2021 12:25:32 for gem5 by doxygen 1.8.17