Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / sam-t2 / sam / cpus / vonk / ss / api / memsync / src / TsoChecker.cc
CommitLineData
920dae64
AT
1// ========== Copyright Header Begin ==========================================
2//
3// OpenSPARC T2 Processor File: TsoChecker.cc
4// Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
5// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES.
6//
7// The above named program is free software; you can redistribute it and/or
8// modify it under the terms of the GNU General Public
9// License version 2 as published by the Free Software Foundation.
10//
11// The above named program is distributed in the hope that it will be
12// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14// General Public License for more details.
15//
16// You should have received a copy of the GNU General Public
17// License along with this work; if not, write to the Free Software
18// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
19//
20// ========== Copyright Header End ============================================
21/************************************************************************
22**
23** Copyright (C) 2002, Sun Microsystems, Inc.
24**
25** Sun considers its source code as an unpublished, proprietary
26** trade secret and it is available only under strict license provisions.
27** This copyright notice is placed here only to protect Sun in the event
28** the source is deemed a published work. Disassembly, decompilation,
29** or other means of reducing the object code to human readable form
30** is prohibited by the license agreement under which this code is
31** provided to the user or company in possession of this copy."
32**
33*************************************************************************/
34#include "TsoChecker.h"
35#include <sstream>
36#include <time.h>
37
38using namespace std;
39using namespace Tso;
40
41////////////////////////////////////////////////
42
43Tso::TsoChecker::TsoChecker() :
44 listSize_(0),
45 nodeList_(NULL)
46{
47 fprintf(stdout, "# msync TsoChecker enabled. To disable, use -sas_run_args=-DNO_TSO_CHECKER\n");
48}
49
50////////////////////////////////////////////////
51
52TsoChecker::TsoChecker( const TsoChecker & orig )
53{
54 maccHist_ = orig.maccHist_;
55}
56
57////////////////////////////////////////////////
58
59TsoChecker::~TsoChecker()
60{
61
62}
63
64////////////////////////////////////////////////
65
66const TsoChecker &
67TsoChecker::operator=( const TsoChecker & rhs )
68{
69 return *this;
70}
71
72////////////////////////////////////////////////
73
74bool
75TsoChecker::operator==( const TsoChecker & rhs ) const
76{
77 return false;
78}
79
80////////////////////////////////////////////////
81
82string
83TsoChecker::toString() const
84{
85 ostringstream os;
86
87
88 return os.str();
89}
90
91void
92TsoChecker::init(int nstrands)
93{
94 listSize_ = nstrands;
95 nodeList_ = new list<TsoNode*>[nstrands];
96 tmpList_ = new list<TsoNode*>[nstrands];
97 lastPoIseq_ = new uint64_t[nstrands];
98
99 for (int i = 0; i < nstrands; i++) {
100 lastPoIseq_[i] = 0ull;
101 }
102}
103
104TsoNode*
105TsoChecker::findNode(TsoCheckerCmd& cmd)
106{
107 //MSYNC_DEBUG(8, "TsoChecker::findNode cmd=%s", cmd.toString().c_str());//DBX
108
109 list<TsoNode*>::iterator ni;
110 uint64_t mask;
111
112 if (tmpList_[cmd.getThrdId()].empty()) {
113 //MSYNC_DEBUG(8, "TsoChecker::findNode tmpList_[%d] empty", cmd.getThrdId());//DBX
114 return (NULL);
115 }
116
117 mask = ((cmd.getItype() == ITYPE_BLOCK_LOAD) || (cmd.getItype() == ITYPE_BLOCK_STORE)) ?
118 BLK_OP_ADDR_MASK : ((cmd.getItype() == ITYPE_QUAD_LOAD) ? QUAD_OP_ADDR_MASK : FULL_ADDR_MASK);
119
120 //MSYNC_DEBUG(8, "TsoChecker::findNode mask=%#x", mask);//DBX
121
122 ni = tmpList_[cmd.getThrdId()].end();
123 do {
124 ni--;
125 //MSYNC_DEBUG(8, "TsoChecker::findNode ni=%s", (*ni)->toString().c_str());//DBX
126 if ((*ni)->getIseq() == cmd.getIseq() &&
127 (*ni)->getItype() == cmd.getItype() &&
128 ((*ni)->getAddr() & mask) == (cmd.getAddr()& mask)) {
129 //MSYNC_DEBUG(8, "TsoChecker::findNode match=%s", (*ni)->toString().c_str());//DBX
130 return (*ni);
131 }
132 } while (ni != tmpList_[cmd.getThrdId()].begin());
133
134// if (nodeList_[cmd.getThrdId()].empty()) {
135// return (NULL);
136// }
137// ni = nodeList_[cmd.getThrdId()].end();
138// do {
139// ni--;
140// if ((*ni)->getIseq() == cmd.getIseq() &&
141// (*ni)->getItype() == cmd.getItype() &&
142// ((*ni)->getAddr() & mask) == (cmd.getAddr() & mask)) {
143// MS_ASSERT(false, "Should not find node in the nodeList_");
144// return (*ni);
145// }
146// } while (ni != nodeList_[cmd.getThrdId()].begin());
147
148 return (NULL);
149}
150
151TsoNode*
152TsoChecker::findPrev(enum TSO_NODE_TYPE ntype, uint32_t tid)
153{
154 list<TsoNode*>::iterator ni;
155
156 if (nodeList_[tid].empty()) {
157 return (NULL);
158 }
159
160 ni = nodeList_[tid].end();
161 do {
162 ni--;
163 switch (ntype) {
164 case TSO_LOAD:
165 if ((*ni)->isTsoLoad()) {
166 return (*ni);
167 }
168 break;
169 case TSO_STORE:
170 if ((*ni)->isTsoStore()) {
171 return (*ni);
172 }
173 break;
174 default:
175 break;
176 }
177 } while (ni != nodeList_[tid].begin());
178
179 return (NULL);
180}
181
182void
183TsoChecker::formDataDependenceEdges(TsoNode *np)
184{
185 if (np->isTSO()) {
186 if (np->isStoreCommit()) {
187 maccHist_.storeCommit(np);
188 } else if (np->isLoadData()) {
189 if (np->getDsrc() != DSRC_STB) {
190 maccHist_.loadAccess(np);
191 }
192 }
193 }
194}
195
196void
197TsoChecker::formProgramOrderEdges(uint32_t tid)
198{
199 list<TsoNode*>::iterator ni;
200 TsoNode *np;
201 TsoEdge edge;
202
203 for (ni = tmpList_[tid].begin(); ni != tmpList_[tid].end(); ni++) {
204
205 MSYNC_DEBUG(8, "tmpList %s", (*ni)->toString().c_str());
206
207 if ((*ni)->getIseq() != (lastPoIseq_[tid] + 1)) {
208 //MSYNC_DEBUG(8, "TsoChecker iseq not in contiguous sequence, ni=%d, last=%d", (*ni)->getIseq(), (lastPoIseq_[tid] + 1));//DBX
209 break; // not in contiguous sequence
210 } else {
211 lastPoIseq_[tid]++;
212 //MSYNC_DEBUG(8, "TsoChecker lastPoIseq_[%d]=%d", tid, lastPoIseq_[tid]);//DBX
213 }
214
215 if ((*ni)->isTsoLoad()) {
216 if (np = findPrev(TSO_LOAD, tid)) {
217 edge.addEdge (ET_PROG, np, (*ni));
218 np->setLastLoad(false);
219 (*ni)->setLastLoad(true);
220 } else { // This can only possible if this is the 1st tsoLoad, not necessary iseq=1
221 (*ni)->setLastLoad(true);
222 }
223 }
224 if ((*ni)->isTsoStore()) {
225 if (np = findPrev(TSO_LOAD, tid)) {
226 edge.addEdge (ET_PROG, np, (*ni));
227 }
228 if (np = findPrev(TSO_STORE, tid)) {
229 edge.addEdge (ET_PROG, np, (*ni));
230 np->setLastStore(false);
231 (*ni)->setLastStore(true);
232 } else {
233 (*ni)->setLastStore(true);
234 }
235 }
236 if ((*ni)->isTSO()) { // atomic is both TsoLoad and TsoStore, but contains only one node
237 nodeList_[tid].push_back(*ni);
238 }
239 }
240 if (ni != tmpList_[tid].end()) {
241 tmpList_[tid].erase(tmpList_[tid].begin(), ni);
242 }
243}
244
245void
246TsoChecker::input(TsoCheckerCmd& cmd)
247{
248 /* create Node */
249 TsoNode* node; // = new TsoNode(cmd);
250 TsoNode* node1;
251 list<TsoNode*>::iterator ni;
252 TsoEdge edge;
253 bool doProgramOrder;
254 bool newNode;
255
256 uint32_t tid = cmd.getThrdId();
257
258 doProgramOrder = false;
259 newNode = false;
260
261 setTime(cmd.getTime());
262
263 if (node = findNode(cmd)) {
264 /********************************************************************************
265 * This path indicates the instruction corresponds to the node consists of
266 * multiple TSO nodes. The algorithm requires some content of the node been
267 * updated according to the new cmd, and the old content of node been pushed
268 * to the nodes_ list. Doing so, one instruction can appear as one node in
269 * the TSO digraph.
270 ********************************************************************************/
271 switch (cmd.getItype()) {
272 node1 = new TsoNode(*node);
273 case ITYPE_ATOMIC:
274 // this one must be the store part of the atomic, change content to SC
275 if (cmd.getCmd() != MEM_STORE_COMMIT)
276 {
277 MS_ERROR("Atomic does not follow load-store order. tid=%d PA=%llx", tid, cmd.getAddr());
278 return;
279 }
280 node->setCommit(cmd.getMacc());
281 node->setGobs(cmd.getGobs());
282 node->setData(cmd.getData());
283 node->setCmd(cmd.getCmd());
284 node->setSizeV(cmd.getSizeV());
285 node->setOld(0);
286 node->nodePush(node1);
287 node->incNcnt();
288 break;
289 case ITYPE_QUAD_LOAD:
290 node->setCommit(cmd.getMacc());
291 node->setData(cmd.getData());
292 node->setAddr(cmd.getAddr());
293 node->setSizeV(cmd.getSizeV());
294 node->nodePush(node1);
295 node->incNcnt();
296 break;
297 default:
298 MS_ERROR("Encountered instruction type that should have only one TSO node. tid=%d PA=%llx", tid, cmd.getAddr());
299 return;
300 }
301 } else { // not atomic or atomic but new
302 node = new TsoNode(cmd);
303 if (cmd.isAtomic()) {
304 MSYNC_DEBUG(8, "atomic-node=%s", node->toString().c_str());
305 if (cmd.getCmd() != MEM_LOAD_DATA)
306 {
307 MS_ERROR("Atomic node creation should go with LoadData. tid=%d PA=%llx", tid, cmd.getAddr());
308 return;
309 }
310 }
311 newNode = true;
312 }
313
314// if ((cmd.isAtomic() && cmd.isStoreCommit()) ||
315// !cmd.isAtomic()) {
316 if (node->allEntriesArrive()) {
317 doProgramOrder = (node->getIseq() == (lastPoIseq_[tid] + 1)) ? true : false;
318 }
319
320 MSYNC_DEBUG(6, "node=%s", node->toString().c_str());
321
322 /* Note that the node's entry type is not valid for atomic instruction
323 whose LoadData and StoreCommit share the same node. Therefore, the
324 entry type is always LoadData. That is why the MemoryAccessHistory
325 action uses cmd.isStoreCommit() and cmd.isLoadData() for conditional
326 check. An atomic instruction that has both load and store parts should
327 perform both maccHist_.storeCommit() and maccHist_.loadAccess().
328
329 Since an atomic node can be processed in both if-else parts, the
330 program order edge may be formed to times. The edge.addEdge()
331 will detect this, and force only one edge is formed.
332 */
333
334 /* note that the iseq coming to this function could be out of order */
335
336
337 /* Form data dependence edges */
338 formDataDependenceEdges(node);
339
340 if (newNode) {
341 tmpListInsert (tid, node);
342 MSYNC_DEBUG(6, "tmpList size=%d %s",
343 tmpList_[tid].size(), node->toString().c_str());
344 }
345
346 /* Form program order edges */
347 if (doProgramOrder) {
348 if (tmpList_[tid].empty())
349 {
350 MS_ERROR("tmpList cannot be empty. tid=%d PA=%llx", tid, cmd.getAddr());
351 return;
352 }
353 formProgramOrderEdges(tid);
354 }
355
356 /* Cycle check and prun nodes */
357
358 bool removed;
359 do {
360 removed = false;
361 for (ni = nodeList_[tid].begin(); ni != nodeList_[tid].end(); ni++) {
362 MSYNC_DEBUG(6, "Check %s", (*ni)->toString().c_str());
363 if (canRemove(*ni)) {
364 if (checkCycle(*ni, *ni)) {
365 MSYNC_DEBUG(0, "Tso Violation: %s", (*ni)->toString().c_str());
366 printNodeList(ERROR);
367 MemorySyncMessage::error("Tso Violation");
368 } else { // no TSO violation
369 removeNode(*ni);
370 nodeList_[tid].erase(ni);
371 removed = true;
372 // delete(*ni); // need to make sure the node does not appear in other list
373 break;
374 }
375 }
376 }
377 } while (!nodeList_[tid].empty() && removed);
378
379}
380
381void
382TsoChecker::input(uint32_t tid, uint64_t iseq, enum INSTR_TYPE itype, enum MEM_CMD cmd,
383 uint64_t addr, uint64_t data, uint8_t sizeV, enum DATA_SRC dsrc,
384 uint64_t macc, uint64_t gobs, uint64_t time)
385{
386 TsoCheckerCmd tsocmd;
387
388 tsocmd.setIseq(iseq);
389 tsocmd.setMacc(macc);
390 tsocmd.setGobs(gobs);
391 tsocmd.setThrdId(tid);
392 tsocmd.setItype(itype);
393 tsocmd.setCmd(cmd);
394 tsocmd.setAddr(addr);
395 tsocmd.setData(data);
396 tsocmd.setDsrc(dsrc);
397 tsocmd.setSizeV(sizeV);
398 tsocmd.setTime(time);
399
400 MSYNC_DEBUG(6, "tsocmd=%s", tsocmd.toString().c_str());
401
402 input(tsocmd);
403}
404
405string
406TsoChecker::printOutEdges(TsoNode* node) {
407
408 ostringstream os;
409 list<TsoEdge*>::iterator ei;
410
411 // os << "nodePtr=" << node << endl;
412 for (ei = node->outBegin(); ei != node->outEnd(); ei++) {
413 os << " " << (char*) node->getNodeName() << " -> " << (char*) (*ei)->getDst()->getNodeName();
414 os << " [label=" << '"' << edgeType[(*ei)->getType()] << '"' << "]" << ";" << endl;
415 }
416
417 return os.str();
418}
419
420void
421TsoChecker::printNodeList()
422{
423 printNodeList(NORMAL);
424}
425
426void
427TsoChecker::printNodeList(int flag)
428{
429 list<TsoNode*>::iterator ni;
430 TsoNode* nodePtr;
431 ofstream out;
432
433 if (flag == ERROR) {
434 out.open("TsoViolationDiGraph");
435 } else {
436 out.open("TsoDiGraph");
437 }
438 if (!out) {
439 cout << "Cannot Open File" << endl;
440 }
441
442 // cerr << "TsoChecker is printing TsoDiGraph" << endl;
443
444 out << "digraph G {" << endl;
445 out << " center=true;" << endl;
446 out << " size=" << '"' << "7.5, 10" << '"' << ";" << endl;
447 //out << "DBX: listSize=" << listSize_ << endl;//DBX
448 int i;
449 //for (i = 0; i < MAX_STRANDS; i++) {
450 for (i = 0; i < listSize_; i++) {
451 //out << "DBX: nodeList_[" << i << "]=" << &(nodeList_[i]) << endl;//DBX
452 out << "# nodelist " << i << " size=" << nodeList_[i].size() << endl;
453 }
454 //for (i = 0; i < MAX_STRANDS; i++) {
455 for (i = 0; i < listSize_; i++) {
456 if (nodeList_[i].size() > 0) {
457 out << " subgraph cluster" << i << " {" << endl;
458 out << " label = " << '"' << "thread " << i << '"' << ";" << endl;
459 for (ni = nodeList_[i].begin(); ni != nodeList_[i].end(); ni++) {
460 out << " " << (char*) (*ni)->getNodeName();
461 out << " [label=" << '"' << (*ni)->getIseq() << "," << mmitype[(*ni)->getItype()] << '"' << "];" << endl;
462 MSYNC_DEBUG(6, "NodeList %s", (*ni)->toString().c_str());
463 }
464 out << " }" << endl;
465 }
466 }
467
468 //for (i = 0; i < MAX_STRANDS; i++) {
469 for (i = 0; i < listSize_; i++) {
470 if (nodeList_[i].size() > 0) {
471 for (ni = nodeList_[i].begin(); ni != nodeList_[i].end(); ni++) {
472 out << printOutEdges (*ni);
473 }
474 }
475 }
476
477 out << "}" << endl;
478
479}
480
481bool
482TsoChecker::checkCycle(TsoNode *np, TsoNode *start_np)
483{
484 list<TsoEdge*>::iterator ei;
485 TsoEdge *ep;
486 TsoNode *np1;
487
488 if (np->noInEdges()) return false;
489
490 for (ei = np->inBegin(); ei != np->inEnd(); ei++) {
491 ep = *ei;
492 np1 = ep->getSrc();
493 if (np1 == start_np) {
494 MSYNC_DEBUG(0, "Tso Violation: %s", np1->toString().c_str());
495 return true;
496 } else {
497 if (checkCycle(np1, start_np)) {
498 MSYNC_DEBUG(0, "Tso Violation: %s", np1->toString().c_str());
499 return true;
500 }
501 }
502 }
503 return false;
504}
505
506bool
507TsoChecker::canRemove(TsoNode* np)
508{
509// return false;
510// if (np->getIseq() >= 20) return false;
511
512 bool storeOk = false;
513 bool loadOk = false;
514
515 if (np->isStore() && !np->isLastStore() &&
516 (np->isAllOld() && (np->getRetire() <= getTime()))) {
517 storeOk = true;
518 }
519 if (np->isLoad() && !np->isLastLoad()) {
520 loadOk = true;
521 }
522
523 if (np->isAtomic()) {
524 return (storeOk && loadOk);
525 } else {
526 return (storeOk || loadOk);
527 }
528}
529
530void
531TsoChecker::removeNode(TsoNode* np)
532{
533 // MS_ASSERT(np->noInEdges(), "Node that is to be removed has in-edges");
534
535 MSYNC_DEBUG(6, "Remove %s", np->toString().c_str());
536 if (np->isStore()) {
537 maccHist_.removeStoreNode(np);
538 }
539 np->removeEdges();
540}
541
542void
543TsoChecker::tmpListInsert(uint32_t tid, TsoNode *np)
544{
545 list<TsoNode*>::iterator ni;
546 list<TsoNode*>::iterator posi; // insert position iterator
547
548 if (tmpList_[tid].empty()) {
549 tmpList_[tid].push_back(np);
550 return;
551 }
552
553 for (ni = tmpList_[tid].begin(); ni != tmpList_[tid].end(); ni++) {
554 if ((*ni)->getIseq() > np->getIseq()) {
555 tmpList_[tid].insert(ni, np);
556 return;
557 }
558 }
559
560 tmpList_[tid].push_back(np);
561 return;
562}