gem5  v22.1.0.0
tester_thread.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2017-2021 Advanced Micro Devices, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  * this list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  * this list of conditions and the following disclaimer in the documentation
13  * and/or other materials provided with the distribution.
14  *
15  * 3. Neither the name of the copyright holder nor the names of its
16  * contributors may be used to endorse or promote products derived from this
17  * software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
33 
34 #include <fstream>
35 
36 #include "debug/ProtocolTest.hh"
37 
38 namespace gem5
39 {
40 
42  : ClockedObject(p),
43  threadEvent(this, "TesterThread tick"),
44  deadlockCheckEvent(this),
45  threadId(p.thread_id),
46  numLanes(p.num_lanes),
47  tester(nullptr), addrManager(nullptr), port(nullptr),
48  scalarPort(nullptr), sqcPort(nullptr), curEpisode(nullptr),
49  curAction(nullptr), pendingLdStCount(0), pendingFenceCount(0),
50  pendingAtomicCount(0), lastActiveCycle(Cycles(0)),
51  deadlockThreshold(p.deadlock_threshold)
52 {
53 }
54 
56 {
57  for (auto ep : episodeHistory) {
58  assert(ep != nullptr);
59  delete ep;
60  }
61 }
62 
63 void
65 {
66  // this thread is waken up by one of the following events
67  // - hitCallback is called
68  // - a new episode is created
69 
70  // check if this is the first episode in this thread
71  if (curEpisode == nullptr) {
73  assert(curEpisode);
74  }
75 
76  if (isNextActionReady()) {
77  // isNextActionReady should check if the action list is empty
78  assert(curAction != nullptr);
79 
80  // issue the next action
82  } else {
83  // check for completion of the current episode
84  // completion = no outstanding requests + not having more actions
85  if (!curEpisode->hasMoreActions() &&
86  pendingLdStCount == 0 &&
87  pendingFenceCount == 0 &&
88  pendingAtomicCount == 0) {
89 
91 
92  // check if it's time to stop the tester
93  if (tester->checkExit()) {
94  // no more event is scheduled for this thread
95  return;
96  }
97 
98  // issue the next episode
100  assert(curEpisode);
101 
102  // now we get a new episode
103  // let's wake up the thread in the next cycle
104  if (!threadEvent.scheduled()) {
105  scheduleWakeup();
106  }
107  }
108  }
109 }
110 
111 void
113 {
114  assert(!threadEvent.scheduled());
116 }
117 
118 void
120 {
121  // after this first schedule, the deadlock event is scheduled by itself
122  assert(!deadlockCheckEvent.scheduled());
124 }
125 
126 void
129  ProtocolTester::GMTokenPort *_tokenPort,
130  ProtocolTester::SeqPort *_scalarPort,
131  ProtocolTester::SeqPort *_sqcPort)
132 {
133  tester = _tester;
134  port = _port;
135  tokenPort = _tokenPort;
136  scalarPort = _scalarPort;
137  sqcPort = _sqcPort;
138 
139  assert(tester && port);
141  assert(addrManager);
142 }
143 
144 void
146 {
147  int num_reg_loads = random() % tester->getEpisodeLength();
148  int num_reg_stores = tester->getEpisodeLength() - num_reg_loads;
149 
150  // create a new episode
151  curEpisode = new Episode(tester, this, num_reg_loads, num_reg_stores);
152  episodeHistory.push_back(curEpisode);
153 }
154 
155 int
157 {
158  if (!tokenPort) {
159  return 0;
160  }
161 
162  int tokens_needed = 0;
164 
165  switch(curAction->getType()) {
167  tokens_needed = numLanes;
168  break;
171  for (int lane = 0; lane < numLanes; ++lane) {
172  Location loc = curAction->getLocation(lane);
173 
174  if (loc != AddressManager::INVALID_LOCATION && loc >= 0) {
175  tokens_needed++;
176  }
177  }
178  break;
179  default:
180  tokens_needed = 0;
181  }
182 
183  return tokens_needed;
184 }
185 
186 bool
188 {
189  if (!curEpisode->hasMoreActions()) {
190  return false;
191  } else {
193 
194  // Only GPU wavefront threads have a token port. For all other types
195  // of threads evaluate to true.
196  bool haveTokens = true;
197 
198  switch(curAction->getType()) {
200  haveTokens = tokenPort ?
202 
203  // an atomic action must wait for all previous requests
204  // to complete
205  if (pendingLdStCount == 0 &&
206  pendingFenceCount == 0 &&
207  pendingAtomicCount == 0 &&
208  haveTokens) {
209  return true;
210  }
211 
212  return false;
214  // we should not see any outstanding ld_st or fence here
215  assert(pendingLdStCount == 0 &&
216  pendingFenceCount == 0);
217 
218  // an acquire action must wait for all previous atomic
219  // requests to complete
220  if (pendingAtomicCount == 0) {
221  return true;
222  }
223 
224  return false;
226  // we should not see any outstanding atomic or fence here
227  assert(pendingAtomicCount == 0 &&
228  pendingFenceCount == 0);
229 
230  // a release action must wait for all previous ld/st
231  // requests to complete
232  if (pendingLdStCount == 0) {
233  return true;
234  }
235 
236  return false;
239  // we should not see any outstanding atomic here
240  assert(pendingAtomicCount == 0);
241 
242  // can't issue if there is a pending fence
243  if (pendingFenceCount > 0) {
244  return false;
245  }
246 
247  // a Load or Store is ready if it doesn't overlap
248  // with any outstanding request
249  for (int lane = 0; lane < numLanes; ++lane) {
250  Location loc = curAction->getLocation(lane);
251 
252  if (loc != AddressManager::INVALID_LOCATION && loc >= 0) {
254 
255  if (outstandingLoads.find(addr) !=
256  outstandingLoads.end()) {
257  return false;
258  }
259 
260  if (outstandingStores.find(addr) !=
261  outstandingStores.end()) {
262  return false;
263  }
264 
265  if (outstandingAtomics.find(addr) !=
266  outstandingAtomics.end()) {
267  // this is not an atomic action, so the address
268  // should not be in outstandingAtomics list
269  assert(false);
270  }
271  }
272  }
273 
274  haveTokens = tokenPort ?
276  if (!haveTokens) {
277  return false;
278  }
279 
280  return true;
281  default:
282  panic("The tester got an invalid action\n");
283  }
284  }
285 }
286 
287 void
289 {
290  switch(curAction->getType()) {
292  if (tokenPort) {
294  }
295  issueAtomicOps();
296  break;
298  issueAcquireOp();
299  break;
301  issueReleaseOp();
302  break;
304  if (tokenPort) {
306  }
307  issueLoadOps();
308  break;
310  if (tokenPort) {
312  }
313  issueStoreOps();
314  break;
315  default:
316  panic("The tester got an invalid action\n");
317  }
318 
319  // the current action has been issued, pop it from the action list
322 
323  // we may be able to schedule the next action
324  // just wake up this thread in the next cycle
325  if (!threadEvent.scheduled()) {
326  scheduleWakeup();
327  }
328 }
329 
330 void
332  int lane, Location loc, Value stored_val)
333 {
334  OutstandingReqTable::iterator it = req_table.find(address);
335  OutstandingReq req(lane, loc, stored_val, curCycle());
336 
337  if (it == req_table.end()) {
338  // insert a new list of requests for this address
339  req_table.insert(std::pair<Addr, OutstandingReqList>(address,
340  OutstandingReqList(1, req)));
341  } else {
342  // add a new request
343  (it->second).push_back(req);
344  }
345 }
346 
349 {
350  OutstandingReqTable::iterator it = req_table.find(addr);
351 
352  // there must be exactly one list of requests for this address in the table
353  assert(it != req_table.end());
354 
355  // get the request list
356  OutstandingReqList& req_list = it->second;
357  assert(!req_list.empty());
358 
359  // save a request
360  OutstandingReq ret_req = req_list.back();
361 
362  // remove the request from the list
363  req_list.pop_back();
364 
365  // if the list is now empty, remove it from req_table
366  if (req_list.empty()) {
367  req_table.erase(it);
368  }
369 
370  return ret_req;
371 }
372 
373 void
375 {
376  if (!addrManager->validateAtomicResp(loc, ret_val)) {
377  std::stringstream ss;
379 
380  // basic info
381  ss << threadName << ": Atomic Op returned unexpected value\n"
382  << "\tEpisode " << curEpisode->getEpisodeId() << "\n"
383  << "\tLane ID " << lane << "\n"
384  << "\tAddress " << ruby::printAddress(addr) << "\n"
385  << "\tAtomic Op's return value " << ret_val << "\n";
386 
387  // print out basic info
388  warn("%s\n", ss.str());
389 
390  // TODO add more detailed info
391 
392  // dump all error info and exit the simulation
394  }
395 }
396 
397 void
399 {
400  if (ret_val != addrManager->getLoggedValue(loc)) {
401  std::stringstream ss;
403 
404  // basic info
405  ss << threadName << ": Loaded value is not consistent with "
406  << "the last stored value\n"
407  << "\tTesterThread " << threadId << "\n"
408  << "\tEpisode " << curEpisode->getEpisodeId() << "\n"
409  << "\tLane ID " << lane << "\n"
410  << "\tAddress " << ruby::printAddress(addr) << "\n"
411  << "\tLoaded value " << ret_val << "\n"
412  << "\tLast writer " << addrManager->printLastWriter(loc) << "\n";
413 
414  // print out basic info
415  warn("%s\n", ss.str());
416 
417  // TODO add more detailed info
418 
419  // dump all error info and exit the simulation
421  }
422 }
423 
424 bool
425 TesterThread::checkDRF(Location atomic_loc, Location loc, bool isStore) const
426 {
427  if (curEpisode && curEpisode->isEpsActive()) {
428  // check against the current episode this thread is executing
429  return curEpisode->checkDRF(atomic_loc, loc, isStore, numLanes);
430  }
431 
432  return true;
433 }
434 
435 void
437 {
439  // deadlock detected
440  std::stringstream ss;
441 
442  ss << threadName << ": Deadlock detected\n"
443  << "\tLast active cycle: " << lastActiveCycle << "\n"
444  << "\tCurrent cycle: " << curCycle() << "\n"
445  << "\tDeadlock threshold: " << deadlockThreshold << "\n";
446 
447  // print out basic info
448  warn("%s\n", ss.str());
449 
450  // dump all error info and exit the simulation
452  } else if (!tester->checkExit()) {
453  // schedule a future deadlock check event
454  assert(!deadlockCheckEvent.scheduled());
457  }
458 }
459 
460 void
462  std::stringstream& ss) const
463 {
464  Cycles cur_cycle = curCycle();
465 
466  for (const auto& m : table) {
467  for (const auto& req : m.second) {
468  ss << "\t\t\tAddr " << ruby::printAddress(m.first)
469  << ": delta (curCycle - issueCycle) = "
470  << (cur_cycle - req.issueCycle) << std::endl;
471  }
472  }
473 }
474 
475 void
476 TesterThread::printAllOutstandingReqs(std::stringstream& ss) const
477 {
478  // dump all outstanding requests of this thread
479  ss << "\t\tOutstanding Loads:\n";
481  ss << "\t\tOutstanding Stores:\n";
483  ss << "\t\tOutstanding Atomics:\n";
485  ss << "\t\tNumber of outstanding acquires & releases: "
486  << pendingFenceCount << std::endl;
487 }
488 
489 } // namespace gem5
Addr getAddress(Location loc)
Value getLoggedValue(Location loc) const
std::string printLastWriter(Location loc) const
static const int INVALID_LOCATION
bool validateAtomicResp(Location loc, Value ret_val)
The ClockedObject class extends the SimObject with a clock and accessor functions to relate ticks to ...
Cycles curCycle() const
Determine the current cycle, corresponding to a tick aligned to a clock edge.
Tick nextCycle() const
Based on the clock of the object, determine the start tick of the first cycle that is at least one cy...
Tick clockPeriod() const
Cycles is a wrapper class for representing cycle counts, i.e.
Definition: types.hh:79
Location getLocation(int lane) const
Definition: episode.cc:289
Type getType() const
Definition: episode.hh:66
int getEpisodeId() const
Definition: episode.hh:85
void completeEpisode()
Definition: episode.cc:200
bool checkDRF(Location atomic_loc, Location loc, bool isStore, int max_lane) const
Definition: episode.cc:240
bool hasMoreActions() const
Definition: episode.hh:91
bool isEpsActive() const
Definition: episode.hh:95
void popAction()
Definition: episode.cc:86
const Action * peekCurAction() const
Definition: episode.cc:77
AddressManager * getAddressManager() const
int getEpisodeLength() const
void dumpErrorLog(std::stringstream &ss)
TesterThread(const Params &p)
AddressManager * addrManager
AddressManager::Value Value
void attachTesterThreadToPorts(ProtocolTester *_tester, ProtocolTester::SeqPort *_port, ProtocolTester::GMTokenPort *_tokenPort=nullptr, ProtocolTester::SeqPort *_sqcPort=nullptr, ProtocolTester::SeqPort *_scalarPort=nullptr)
virtual void issueReleaseOp()=0
OutstandingReqTable outstandingAtomics
AddressManager::Location Location
void scheduleDeadlockCheckEvent()
void validateAtomicResp(Location loc, int lane, Value ret_val)
ProtocolTester * tester
ProtocolTester::SeqPort * port
bool checkDRF(Location atomic_loc, Location loc, bool isStore) const
void printAllOutstandingReqs(std::stringstream &ss) const
DeadlockCheckEvent deadlockCheckEvent
virtual void issueAcquireOp()=0
ProtocolTester::SeqPort * scalarPort
OutstandingReqTable outstandingStores
std::string threadName
void addOutstandingReqs(OutstandingReqTable &req_table, Addr addr, int lane, Location loc, Value stored_val=AddressManager::INVALID_VALUE)
virtual void issueStoreOps()=0
OutstandingReqTable outstandingLoads
ProtocolTester::GMTokenPort * tokenPort
virtual ~TesterThread()
EpisodeHistory episodeHistory
OutstandingReq popOutstandingReq(OutstandingReqTable &req_table, Addr address)
const Episode::Action * curAction
virtual void issueAtomicOps()=0
ProtocolTester::SeqPort * sqcPort
void printOutstandingReqs(const OutstandingReqTable &table, std::stringstream &ss) const
TesterThreadParams Params
virtual void issueLoadOps()=0
std::unordered_map< Addr, OutstandingReqList > OutstandingReqTable
TesterThreadEvent threadEvent
std::vector< OutstandingReq > OutstandingReqList
void validateLoadResp(Location loc, int lane, Value ret_val)
void acquireTokens(int num_tokens)
Acquire tokens by decrementing the number of available tokens across the port.
Definition: token_port.cc:64
bool haveTokens(int num_tokens)
Query if there are at least num_tokens tokens available to acquire.
Definition: token_port.cc:56
STL pair class.
Definition: stl.hh:58
STL vector class.
Definition: stl.hh:37
bool scheduled() const
Determine if the current event is scheduled.
Definition: eventq.hh:465
void schedule(Event &event, Tick when)
Definition: eventq.hh:1019
#define panic(...)
This implements a cprintf based panic() function.
Definition: logging.hh:178
#define warn(...)
Definition: logging.hh:246
Bitfield< 1 > ep
Bitfield< 54 > p
Definition: pagetable.hh:70
Bitfield< 3 > addr
Definition: types.hh:84
std::string printAddress(Addr addr)
Definition: Address.cc:80
Reference material can be found at the JEDEC website: UFS standard http://www.jedec....
Tick curTick()
The universal simulation clock.
Definition: cur_tick.hh:46
uint64_t Addr
Address type This will probably be moved somewhere else in the near future.
Definition: types.hh:147
std::stringstream ss
Definition: trace.test.cc:45

Generated on Wed Dec 21 2022 10:22:32 for gem5 by doxygen 1.9.1