StateMachine.py revision 6779:4e611eba2b13
1# Copyright (c) 1999-2008 Mark D. Hill and David A. Wood 2# Copyright (c) 2009 The Hewlett-Packard Development Company 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 7# met: redistributions of source code must retain the above copyright 8# notice, this list of conditions and the following disclaimer; 9# redistributions in binary form must reproduce the above copyright 10# notice, this list of conditions and the following disclaimer in the 11# documentation and/or other materials provided with the distribution; 12# neither the name of the copyright holders nor the names of its 13# contributors may be used to endorse or promote products derived from 14# this software without specific prior written permission. 15# 16# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28from m5.util import code_formatter, orderdict 29 30from slicc.symbols.Symbol import Symbol 31from slicc.symbols.Var import Var 32import slicc.generate.html as html 33 34class StateMachine(Symbol): 35 def __init__(self, symtab, ident, location, pairs, config_parameters): 36 super(StateMachine, self).__init__(symtab, ident, location, pairs) 37 self.table = None 38 self.config_parameters = config_parameters 39 for param in config_parameters: 40 var = Var(symtab, param.name, location, param.type_ast.type, 41 "m_%s" % param.name, {}, self) 42 self.symtab.registerSym(param.name, var) 43 44 self.states = orderdict() 45 self.events = orderdict() 46 self.actions = orderdict() 47 self.transitions = [] 48 self.in_ports = [] 49 self.functions = [] 50 self.objects = [] 51 52 self.message_buffer_names = [] 53 54 def __repr__(self): 55 return "[StateMachine: %s]" % self.ident 56 57 def addState(self, state): 58 assert self.table is None 59 self.states[state.ident] = state 60 61 def addEvent(self, event): 62 assert self.table is None 63 self.events[event.ident] = event 64 65 def addAction(self, action): 66 assert self.table is None 67 68 # Check for duplicate action 69 for other in self.actions.itervalues(): 70 if action.ident == other.ident: 71 action.warning("Duplicate action definition: %s" % action.ident) 72 action.error("Duplicate action definition: %s" % action.ident) 73 if action.short == other.short: 74 other.warning("Duplicate action shorthand: %s" % other.ident) 75 other.warning(" shorthand = %s" % other.short) 76 action.warning("Duplicate action shorthand: %s" % action.ident) 77 action.error(" shorthand = %s" % action.short) 78 79 self.actions[action.ident] = action 80 81 def addTransition(self, trans): 82 assert self.table is None 83 self.transitions.append(trans) 84 85 def addInPort(self, var): 86 self.in_ports.append(var) 87 88 def addFunc(self, func): 89 # register func in the symbol table 90 self.symtab.registerSym(str(func), func) 91 self.functions.append(func) 92 93 def addObject(self, obj): 94 self.objects.append(obj) 95 96 # Needs to be called before accessing the table 97 def buildTable(self): 98 assert self.table is None 99 100 table = {} 101 102 for trans in self.transitions: 103 # Track which actions we touch so we know if we use them 104 # all -- really this should be done for all symbols as 105 # part of the symbol table, then only trigger it for 106 # Actions, States, Events, etc. 107 108 for action in trans.actions: 109 action.used = True 110 111 index = (trans.state, trans.event) 112 if index in table: 113 table[index].warning("Duplicate transition: %s" % table[index]) 114 trans.error("Duplicate transition: %s" % trans) 115 table[index] = trans 116 117 # Look at all actions to make sure we used them all 118 for action in self.actions.itervalues(): 119 if not action.used: 120 error_msg = "Unused action: %s" % action.ident 121 if "desc" in action: 122 error_msg += ", " + action.desc 123 action.warning(error_msg) 124 self.table = table 125 126 def writeCodeFiles(self, path): 127 self.printControllerHH(path) 128 self.printControllerCC(path) 129 self.printCSwitch(path) 130 self.printCWakeup(path) 131 self.printProfilerCC(path) 132 self.printProfilerHH(path) 133 134 for func in self.functions: 135 func.writeCodeFiles(path) 136 137 def printControllerHH(self, path): 138 '''Output the method declarations for the class declaration''' 139 code = code_formatter() 140 ident = self.ident 141 c_ident = "%s_Controller" % self.ident 142 143 self.message_buffer_names = [] 144 145 code(''' 146/** \\file $ident.hh 147 * 148 * Auto generated C++ code started by $__file__:$__line__ 149 * Created by slicc definition of Module "${{self.short}}" 150 */ 151 152#ifndef ${ident}_CONTROLLER_H 153#define ${ident}_CONTROLLER_H 154 155#include "mem/ruby/common/Global.hh" 156#include "mem/ruby/common/Consumer.hh" 157#include "mem/ruby/slicc_interface/AbstractController.hh" 158#include "mem/protocol/TransitionResult.hh" 159#include "mem/protocol/Types.hh" 160#include "mem/protocol/${ident}_Profiler.hh" 161''') 162 163 seen_types = set() 164 for var in self.objects: 165 if var.type.ident not in seen_types: 166 code('#include "mem/protocol/${{var.type.c_ident}}.hh"') 167 seen_types.add(var.type.ident) 168 169 # for adding information to the protocol debug trace 170 code(''' 171extern stringstream ${ident}_transitionComment; 172 173class $c_ident : public AbstractController { 174#ifdef CHECK_COHERENCE 175#endif /* CHECK_COHERENCE */ 176public: 177 $c_ident(const string & name); 178 static int getNumControllers(); 179 void init(Network* net_ptr, const vector<string> & argv); 180 MessageBuffer* getMandatoryQueue() const; 181 const int & getVersion() const; 182 const string toString() const; 183 const string getName() const; 184 const MachineType getMachineType() const; 185 void print(ostream& out) const; 186 void printConfig(ostream& out) const; 187 void wakeup(); 188 void set_atomic(Address addr); 189 void started_writes(); 190 void clear_atomic(); 191 void printStats(ostream& out) const { s_profiler.dumpStats(out); } 192 void clearStats() { s_profiler.clearStats(); } 193private: 194''') 195 196 code.indent() 197 # added by SS 198 for param in self.config_parameters: 199 code('int m_${{param.ident}};') 200 201 if self.ident == "L1Cache": 202 code(''' 203int servicing_atomic; 204bool started_receiving_writes; 205Address locked_read_request1; 206Address locked_read_request2; 207Address locked_read_request3; 208Address locked_read_request4; 209int read_counter; 210''') 211 212 code(''' 213int m_number_of_TBEs; 214 215TransitionResult doTransition(${ident}_Event event, ${ident}_State state, const Address& addr); // in ${ident}_Transitions.cc 216TransitionResult doTransitionWorker(${ident}_Event event, ${ident}_State state, ${ident}_State& next_state, const Address& addr); // in ${ident}_Transitions.cc 217string m_name; 218int m_transitions_per_cycle; 219int m_buffer_size; 220int m_recycle_latency; 221map< string, string > m_cfg; 222NodeID m_version; 223Network* m_net_ptr; 224MachineID m_machineID; 225${ident}_Profiler s_profiler; 226static int m_num_controllers; 227// Internal functions 228''') 229 230 for func in self.functions: 231 proto = func.prototype 232 if proto: 233 code('$proto') 234 235 code(''' 236 237// Actions 238''') 239 for action in self.actions.itervalues(): 240 code('/** \\brief ${{action.desc}} */') 241 code('void ${{action.ident}}(const Address& addr);') 242 243 # the controller internal variables 244 code(''' 245 246// Object 247''') 248 for var in self.objects: 249 th = var.get("template_hack", "") 250 code('${{var.type.c_ident}}$th* m_${{var.c_ident}}_ptr;') 251 252 if var.type.ident == "MessageBuffer": 253 self.message_buffer_names.append("m_%s_ptr" % var.c_ident) 254 255 code.dedent() 256 code('};') 257 code('#endif // ${ident}_CONTROLLER_H') 258 code.write(path, '%s.hh' % c_ident) 259 260 def printControllerCC(self, path): 261 '''Output the actions for performing the actions''' 262 263 code = code_formatter() 264 ident = self.ident 265 c_ident = "%s_Controller" % self.ident 266 267 code(''' 268/** \\file $ident.cc 269 * 270 * Auto generated C++ code started by $__file__:$__line__ 271 * Created by slicc definition of Module "${{self.short}}" 272 */ 273 274#include "mem/ruby/common/Global.hh" 275#include "mem/ruby/slicc_interface/RubySlicc_includes.hh" 276#include "mem/protocol/${ident}_Controller.hh" 277#include "mem/protocol/${ident}_State.hh" 278#include "mem/protocol/${ident}_Event.hh" 279#include "mem/protocol/Types.hh" 280#include "mem/ruby/system/System.hh" 281''') 282 283 # include object classes 284 seen_types = set() 285 for var in self.objects: 286 if var.type.ident not in seen_types: 287 code('#include "mem/protocol/${{var.type.c_ident}}.hh"') 288 seen_types.add(var.type.ident) 289 290 code(''' 291int $c_ident::m_num_controllers = 0; 292 293stringstream ${ident}_transitionComment; 294#define APPEND_TRANSITION_COMMENT(str) (${ident}_transitionComment << str) 295/** \\brief constructor */ 296$c_ident::$c_ident(const string &name) 297 : m_name(name) 298{ 299''') 300 code.indent() 301 if self.ident == "L1Cache": 302 code(''' 303servicing_atomic = 0; 304started_receiving_writes = false; 305locked_read_request1 = Address(-1); 306locked_read_request2 = Address(-1); 307locked_read_request3 = Address(-1); 308locked_read_request4 = Address(-1); 309read_counter = 0; 310''') 311 312 code('m_num_controllers++;') 313 for var in self.objects: 314 if var.ident.find("mandatoryQueue") >= 0: 315 code('m_${{var.c_ident}}_ptr = new ${{var.type.c_ident}}();') 316 317 code.dedent() 318 code(''' 319} 320 321void $c_ident::init(Network *net_ptr, const vector<string> &argv) 322{ 323 for (size_t i = 0; i < argv.size(); i += 2) { 324 if (argv[i] == "version") 325 m_version = atoi(argv[i+1].c_str()); 326 else if (argv[i] == "transitions_per_cycle") 327 m_transitions_per_cycle = atoi(argv[i+1].c_str()); 328 else if (argv[i] == "buffer_size") 329 m_buffer_size = atoi(argv[i+1].c_str()); 330 else if (argv[i] == "recycle_latency") 331 m_recycle_latency = atoi(argv[i+1].c_str()); 332 else if (argv[i] == "number_of_TBEs") 333 m_number_of_TBEs = atoi(argv[i+1].c_str()); 334''') 335 336 code.indent() 337 code.indent() 338 for param in self.config_parameters: 339 code('else if (argv[i] == "${{param.name}}")') 340 if param.type_ast.type.ident == "int": 341 code(' m_${{param.name}} = atoi(argv[i+1].c_str());') 342 else: 343 self.error("only int parameters are supported right now") 344 code.dedent() 345 code.dedent() 346 code(''' 347 } 348 349 m_net_ptr = net_ptr; 350 m_machineID.type = MachineType_${ident}; 351 m_machineID.num = m_version; 352 for (size_t i = 0; i < argv.size(); i += 2) { 353 if (argv[i] != "version") 354 m_cfg[argv[i]] = argv[i+1]; 355 } 356 357 // Objects 358 s_profiler.setVersion(m_version); 359''') 360 361 code.indent() 362 for var in self.objects: 363 vtype = var.type 364 vid = "m_%s_ptr" % var.c_ident 365 if "network" not in var: 366 # Not a network port object 367 if "primitive" in vtype: 368 code('$vid = new ${{vtype.c_ident}};') 369 if "default" in var: 370 code('(*$vid) = ${{var["default"]}};') 371 else: 372 # Normal Object 373 # added by SS 374 if "factory" in var: 375 code('$vid = ${{var["factory"]}};') 376 elif var.ident.find("mandatoryQueue") < 0: 377 th = var.get("template_hack", "") 378 expr = "%s = new %s%s" % (vid, vtype.c_ident, th) 379 380 args = "" 381 if "non_obj" not in vtype and not vtype.isEnumeration: 382 if expr.find("TBETable") >= 0: 383 args = "m_number_of_TBEs" 384 else: 385 args = var.get("constructor_hack", "") 386 args = "(%s)" % args 387 388 code('$expr$args;') 389 else: 390 code(';') 391 392 code('assert($vid != NULL);') 393 394 if "default" in var: 395 code('(*$vid) = ${{var["default"]}}; // Object default') 396 elif "default" in vtype: 397 code('(*$vid) = ${{vtype["default"]}}; // Type ${{vtype.ident}} default') 398 399 # Set ordering 400 if "ordered" in var and "trigger_queue" not in var: 401 # A buffer 402 code('$vid->setOrdering(${{var["ordered"]}});') 403 404 # Set randomization 405 if "random" in var: 406 # A buffer 407 code('$vid->setRandomization(${{var["random"]}});') 408 409 # Set Priority 410 if vtype.isBuffer and \ 411 "rank" in var and "trigger_queue" not in var: 412 code('$vid->setPriority(${{var["rank"]}});') 413 else: 414 # Network port object 415 network = var["network"] 416 ordered = var["ordered"] 417 vnet = var["virtual_network"] 418 419 assert var.machine is not None 420 code(''' 421$vid = m_net_ptr->get${network}NetQueue(m_version+MachineType_base_number(string_to_MachineType("${{var.machine.ident}}")), $ordered, $vnet); 422''') 423 424 code('assert($vid != NULL);') 425 426 # Set ordering 427 if "ordered" in var: 428 # A buffer 429 code('$vid->setOrdering(${{var["ordered"]}});') 430 431 # Set randomization 432 if "random" in var: 433 # A buffer 434 code('$vid->setRandomization(${{var["random"]}})') 435 436 # Set Priority 437 if "rank" in var: 438 code('$vid->setPriority(${{var["rank"]}})') 439 440 # Set buffer size 441 if vtype.isBuffer: 442 code(''' 443if (m_buffer_size > 0) { 444 $vid->setSize(m_buffer_size); 445} 446''') 447 448 # set description (may be overriden later by port def) 449 code('$vid->setDescription("[Version " + int_to_string(m_version) + ", ${ident}, name=${{var.c_ident}}]");') 450 451 # Set the queue consumers 452 code.insert_newline() 453 for port in self.in_ports: 454 code('${{port.code}}.setConsumer(this);') 455 456 # Set the queue descriptions 457 code.insert_newline() 458 for port in self.in_ports: 459 code('${{port.code}}.setDescription("[Version " + int_to_string(m_version) + ", $ident, $port]");') 460 461 # Initialize the transition profiling 462 code.insert_newline() 463 for trans in self.transitions: 464 # Figure out if we stall 465 stall = False 466 for action in trans.actions: 467 if action.ident == "z_stall": 468 stall = True 469 470 # Only possible if it is not a 'z' case 471 if not stall: 472 state = "%s_State_%s" % (self.ident, trans.state.ident) 473 event = "%s_Event_%s" % (self.ident, trans.event.ident) 474 code('s_profiler.possibleTransition($state, $event);') 475 476 # added by SS to initialize recycle_latency of message buffers 477 for buf in self.message_buffer_names: 478 code("$buf->setRecycleLatency(m_recycle_latency);") 479 480 code.dedent() 481 code('}') 482 483 has_mandatory_q = False 484 for port in self.in_ports: 485 if port.code.find("mandatoryQueue_ptr") >= 0: 486 has_mandatory_q = True 487 488 if has_mandatory_q: 489 mq_ident = "m_%s_mandatoryQueue_ptr" % self.ident 490 else: 491 mq_ident = "NULL" 492 493 code(''' 494int $c_ident::getNumControllers() { 495 return m_num_controllers; 496} 497 498MessageBuffer* $c_ident::getMandatoryQueue() const { 499 return $mq_ident; 500} 501 502const int & $c_ident::getVersion() const{ 503 return m_version; 504} 505 506const string $c_ident::toString() const{ 507 return "$c_ident"; 508} 509 510const string $c_ident::getName() const{ 511 return m_name; 512} 513const MachineType $c_ident::getMachineType() const{ 514 return MachineType_${ident}; 515} 516 517void $c_ident::print(ostream& out) const { out << "[$c_ident " << m_version << "]"; } 518 519void $c_ident::printConfig(ostream& out) const { 520 out << "$c_ident config: " << m_name << endl; 521 out << " version: " << m_version << endl; 522 for (map<string, string>::const_iterator it = m_cfg.begin(); it != m_cfg.end(); it++) { 523 out << " " << (*it).first << ": " << (*it).second << endl; 524 } 525} 526 527// Actions 528''') 529 530 for action in self.actions.itervalues(): 531 if "c_code" not in action: 532 continue 533 534 code(''' 535/** \\brief ${{action.desc}} */ 536void $c_ident::${{action.ident}}(const Address& addr) 537{ 538 DEBUG_MSG(GENERATED_COMP, HighPrio, "executing"); 539 ${{action["c_code"]}} 540} 541 542''') 543 code.write(path, "%s.cc" % c_ident) 544 545 def printCWakeup(self, path): 546 '''Output the wakeup loop for the events''' 547 548 code = code_formatter() 549 ident = self.ident 550 551 code(''' 552// Auto generated C++ code started by $__file__:$__line__ 553// ${ident}: ${{self.short}} 554 555#include "mem/ruby/common/Global.hh" 556#include "mem/ruby/slicc_interface/RubySlicc_includes.hh" 557#include "mem/protocol/${ident}_Controller.hh" 558#include "mem/protocol/${ident}_State.hh" 559#include "mem/protocol/${ident}_Event.hh" 560#include "mem/protocol/Types.hh" 561#include "mem/ruby/system/System.hh" 562 563void ${ident}_Controller::wakeup() 564{ 565 566 int counter = 0; 567 while (true) { 568 // Some cases will put us into an infinite loop without this limit 569 assert(counter <= m_transitions_per_cycle); 570 if (counter == m_transitions_per_cycle) { 571 g_system_ptr->getProfiler()->controllerBusy(m_machineID); // Count how often we\'re fully utilized 572 g_eventQueue_ptr->scheduleEvent(this, 1); // Wakeup in another cycle and try again 573 break; 574 } 575''') 576 577 code.indent() 578 code.indent() 579 580 # InPorts 581 # 582 # Find the position of the mandatory queue in the vector so 583 # that we can print it out first 584 585 mandatory_q = None 586 if self.ident == "L1Cache": 587 for i,port in enumerate(self.in_ports): 588 assert "c_code_in_port" in port 589 if str(port).find("mandatoryQueue_in") >= 0: 590 assert mandatory_q is None 591 mandatory_q = port 592 593 assert mandatory_q is not None 594 595 # print out the mandatory queue here 596 port = mandatory_q 597 code('// ${ident}InPort $port') 598 output = port["c_code_in_port"] 599 600 pos = output.find("TransitionResult result = doTransition((L1Cache_mandatory_request_type_to_event(((*in_msg_ptr)).m_Type)), L1Cache_getState(addr), addr);") 601 assert pos >= 0 602 atomics_string = ''' 603if ((((*in_msg_ptr)).m_Type) == CacheRequestType_ATOMIC) { 604 if (servicing_atomic == 0) { 605 if (locked_read_request1 == Address(-1)) { 606 assert(read_counter == 0); 607 locked_read_request1 = addr; 608 assert(read_counter == 0); 609 read_counter++; 610 } 611 else if (addr == locked_read_request1) { 612 ; // do nothing 613 } 614 else { 615 assert(0); // should never be here if servicing one request at a time 616 } 617 } 618 else if (!started_receiving_writes) { 619 if (servicing_atomic == 1) { 620 if (locked_read_request2 == Address(-1)) { 621 assert(locked_read_request1 != Address(-1)); 622 assert(read_counter == 1); 623 locked_read_request2 = addr; 624 assert(read_counter == 1); 625 read_counter++; 626 } 627 else if (addr == locked_read_request2) { 628 ; // do nothing 629 } 630 else { 631 assert(0); // should never be here if servicing one request at a time 632 } 633 } 634 else if (servicing_atomic == 2) { 635 if (locked_read_request3 == Address(-1)) { 636 assert(locked_read_request1 != Address(-1)); 637 assert(locked_read_request2 != Address(-1)); 638 assert(read_counter == 1); 639 locked_read_request3 = addr; 640 assert(read_counter == 2); 641 read_counter++; 642 } 643 else if (addr == locked_read_request3) { 644 ; // do nothing 645 } 646 else { 647 assert(0); // should never be here if servicing one request at a time 648 } 649 } 650 else if (servicing_atomic == 3) { 651 if (locked_read_request4 == Address(-1)) { 652 assert(locked_read_request1 != Address(-1)); 653 assert(locked_read_request2 != Address(-1)); 654 assert(locked_read_request3 != Address(-1)); 655 assert(read_counter == 1); 656 locked_read_request4 = addr; 657 assert(read_counter == 3); 658 read_counter++; 659 } 660 else if (addr == locked_read_request4) { 661 ; // do nothing 662 } 663 else { 664 assert(0); // should never be here if servicing one request at a time 665 } 666 } 667 else { 668 assert(0); 669 } 670 } 671} 672else { 673 if (servicing_atomic > 0) { 674 // reset 675 servicing_atomic = 0; 676 read_counter = 0; 677 started_receiving_writes = false; 678 locked_read_request1 = Address(-1); 679 locked_read_request2 = Address(-1); 680 locked_read_request3 = Address(-1); 681 locked_read_request4 = Address(-1); 682 } 683} 684''' 685 686 output = output[:pos] + atomics_string + output[pos:] 687 code('$output') 688 689 for port in self.in_ports: 690 # don't print out mandatory queue twice 691 if port == mandatory_q: 692 continue 693 694 if ident == "L1Cache": 695 if str(port).find("forwardRequestNetwork_in") >= 0: 696 code(''' 697bool postpone = false; 698if ((((*m_L1Cache_forwardToCache_ptr)).isReady())) { 699 const RequestMsg* in_msg_ptr; 700 in_msg_ptr = dynamic_cast<const RequestMsg*>(((*m_L1Cache_forwardToCache_ptr)).peek()); 701 if ((((servicing_atomic == 1) && (locked_read_request1 == ((*in_msg_ptr)).m_Address)) || 702 ((servicing_atomic == 2) && (locked_read_request1 == ((*in_msg_ptr)).m_Address || locked_read_request2 == ((*in_msg_ptr)).m_Address)) || 703 ((servicing_atomic == 3) && (locked_read_request1 == ((*in_msg_ptr)).m_Address || locked_read_request2 == ((*in_msg_ptr)).m_Address || locked_read_request3 == ((*in_msg_ptr)).m_Address)) || 704 ((servicing_atomic == 4) && (locked_read_request1 == ((*in_msg_ptr)).m_Address || locked_read_request2 == ((*in_msg_ptr)).m_Address || locked_read_request3 == ((*in_msg_ptr)).m_Address || locked_read_request1 == ((*in_msg_ptr)).m_Address)))) { 705 postpone = true; 706 } 707} 708if (!postpone) { 709''') 710 code.indent() 711 code('// ${ident}InPort $port') 712 code('${{port["c_code_in_port"]}}') 713 code.dedent() 714 715 if ident == "L1Cache": 716 if str(port).find("forwardRequestNetwork_in") >= 0: 717 code.dedent() 718 code('}') 719 code.indent() 720 code('') 721 722 code.dedent() 723 code.dedent() 724 code(''' 725 break; // If we got this far, we have nothing left todo 726 } 727} 728''') 729 730 if self.ident == "L1Cache": 731 code(''' 732void ${ident}_Controller::set_atomic(Address addr) 733{ 734 servicing_atomic++; 735} 736 737void ${ident}_Controller::started_writes() 738{ 739 started_receiving_writes = true; 740} 741 742void ${ident}_Controller::clear_atomic() 743{ 744 assert(servicing_atomic > 0); 745 read_counter--; 746 servicing_atomic--; 747 if (read_counter == 0) { 748 servicing_atomic = 0; 749 started_receiving_writes = false; 750 locked_read_request1 = Address(-1); 751 locked_read_request2 = Address(-1); 752 locked_read_request3 = Address(-1); 753 locked_read_request4 = Address(-1); 754 } 755} 756''') 757 else: 758 code(''' 759void ${ident}_Controller::started_writes() 760{ 761 assert(0); 762} 763 764void ${ident}_Controller::set_atomic(Address addr) 765{ 766 assert(0); 767} 768 769void ${ident}_Controller::clear_atomic() 770{ 771 assert(0); 772} 773''') 774 775 776 code.write(path, "%s_Wakeup.cc" % self.ident) 777 778 def printCSwitch(self, path): 779 '''Output switch statement for transition table''' 780 781 code = code_formatter() 782 ident = self.ident 783 784 code(''' 785// Auto generated C++ code started by $__file__:$__line__ 786// ${ident}: ${{self.short}} 787 788#include "mem/ruby/common/Global.hh" 789#include "mem/protocol/${ident}_Controller.hh" 790#include "mem/protocol/${ident}_State.hh" 791#include "mem/protocol/${ident}_Event.hh" 792#include "mem/protocol/Types.hh" 793#include "mem/ruby/system/System.hh" 794 795#define HASH_FUN(state, event) ((int(state)*${ident}_Event_NUM)+int(event)) 796 797#define GET_TRANSITION_COMMENT() (${ident}_transitionComment.str()) 798#define CLEAR_TRANSITION_COMMENT() (${ident}_transitionComment.str("")) 799 800TransitionResult ${ident}_Controller::doTransition(${ident}_Event event, ${ident}_State state, const Address& addr 801) 802{ 803 ${ident}_State next_state = state; 804 805 DEBUG_NEWLINE(GENERATED_COMP, MedPrio); 806 DEBUG_MSG(GENERATED_COMP, MedPrio, *this); 807 DEBUG_EXPR(GENERATED_COMP, MedPrio, g_eventQueue_ptr->getTime()); 808 DEBUG_EXPR(GENERATED_COMP, MedPrio,state); 809 DEBUG_EXPR(GENERATED_COMP, MedPrio,event); 810 DEBUG_EXPR(GENERATED_COMP, MedPrio,addr); 811 812 TransitionResult result = doTransitionWorker(event, state, next_state, addr); 813 814 if (result == TransitionResult_Valid) { 815 DEBUG_EXPR(GENERATED_COMP, MedPrio, next_state); 816 DEBUG_NEWLINE(GENERATED_COMP, MedPrio); 817 s_profiler.countTransition(state, event); 818 if (Debug::getProtocolTrace()) { 819 g_system_ptr->getProfiler()->profileTransition("${ident}", m_version, addr, 820 ${ident}_State_to_string(state), 821 ${ident}_Event_to_string(event), 822 ${ident}_State_to_string(next_state), GET_TRANSITION_COMMENT()); 823 } 824 CLEAR_TRANSITION_COMMENT(); 825 ${ident}_setState(addr, next_state); 826 827 } else if (result == TransitionResult_ResourceStall) { 828 if (Debug::getProtocolTrace()) { 829 g_system_ptr->getProfiler()->profileTransition("${ident}", m_version, addr, 830 ${ident}_State_to_string(state), 831 ${ident}_Event_to_string(event), 832 ${ident}_State_to_string(next_state), 833 "Resource Stall"); 834 } 835 } else if (result == TransitionResult_ProtocolStall) { 836 DEBUG_MSG(GENERATED_COMP, HighPrio, "stalling"); 837 DEBUG_NEWLINE(GENERATED_COMP, MedPrio); 838 if (Debug::getProtocolTrace()) { 839 g_system_ptr->getProfiler()->profileTransition("${ident}", m_version, addr, 840 ${ident}_State_to_string(state), 841 ${ident}_Event_to_string(event), 842 ${ident}_State_to_string(next_state), 843 "Protocol Stall"); 844 } 845 } 846 847 return result; 848} 849 850TransitionResult ${ident}_Controller::doTransitionWorker(${ident}_Event event, ${ident}_State state, ${ident}_State& next_state, const Address& addr 851) 852{ 853 switch(HASH_FUN(state, event)) { 854''') 855 856 # This map will allow suppress generating duplicate code 857 cases = orderdict() 858 859 for trans in self.transitions: 860 case_string = "%s_State_%s, %s_Event_%s" % \ 861 (self.ident, trans.state.ident, self.ident, trans.event.ident) 862 863 case = code_formatter() 864 # Only set next_state if it changes 865 if trans.state != trans.nextState: 866 ns_ident = trans.nextState.ident 867 case('next_state = ${ident}_State_${ns_ident};') 868 869 actions = trans.actions 870 871 # Check for resources 872 case_sorter = [] 873 res = trans.resources 874 for key,val in res.iteritems(): 875 if key.type.ident != "DNUCAStopTable": 876 val = ''' 877if (!%s.areNSlotsAvailable(%s)) { 878 return TransitionResult_ResourceStall; 879} 880''' % (key.code, val) 881 case_sorter.append(val) 882 883 884 # Emit the code sequences in a sorted order. This makes the 885 # output deterministic (without this the output order can vary 886 # since Map's keys() on a vector of pointers is not deterministic 887 for c in sorted(case_sorter): 888 case("$c") 889 890 # Figure out if we stall 891 stall = False 892 for action in actions: 893 if action.ident == "z_stall": 894 stall = True 895 break 896 897 if stall: 898 case('return TransitionResult_ProtocolStall;') 899 else: 900 for action in actions: 901 case('${{action.ident}}(addr);') 902 case('return TransitionResult_Valid;') 903 904 case = str(case) 905 906 # Look to see if this transition code is unique. 907 if case not in cases: 908 cases[case] = [] 909 910 cases[case].append(case_string) 911 912 # Walk through all of the unique code blocks and spit out the 913 # corresponding case statement elements 914 for case,transitions in cases.iteritems(): 915 # Iterative over all the multiple transitions that share 916 # the same code 917 for trans in transitions: 918 code(' case HASH_FUN($trans):') 919 code(' {') 920 code(' $case') 921 code(' }') 922 923 code(''' 924 default: 925 WARN_EXPR(m_version); 926 WARN_EXPR(g_eventQueue_ptr->getTime()); 927 WARN_EXPR(addr); 928 WARN_EXPR(event); 929 WARN_EXPR(state); 930 ERROR_MSG(\"Invalid transition\"); 931 } 932 return TransitionResult_Valid; 933} 934''') 935 code.write(path, "%s_Transitions.cc" % self.ident) 936 937 def printProfilerHH(self, path): 938 code = code_formatter() 939 ident = self.ident 940 941 code(''' 942// Auto generated C++ code started by $__file__:$__line__ 943// ${ident}: ${{self.short}} 944 945#ifndef ${ident}_PROFILER_H 946#define ${ident}_PROFILER_H 947 948#include "mem/ruby/common/Global.hh" 949#include "mem/protocol/${ident}_State.hh" 950#include "mem/protocol/${ident}_Event.hh" 951 952class ${ident}_Profiler { 953 public: 954 ${ident}_Profiler(); 955 void setVersion(int version); 956 void countTransition(${ident}_State state, ${ident}_Event event); 957 void possibleTransition(${ident}_State state, ${ident}_Event event); 958 void dumpStats(ostream& out) const; 959 void clearStats(); 960 961 private: 962 int m_counters[${ident}_State_NUM][${ident}_Event_NUM]; 963 int m_event_counters[${ident}_Event_NUM]; 964 bool m_possible[${ident}_State_NUM][${ident}_Event_NUM]; 965 int m_version; 966}; 967 968#endif // ${ident}_PROFILER_H 969''') 970 code.write(path, "%s_Profiler.hh" % self.ident) 971 972 def printProfilerCC(self, path): 973 code = code_formatter() 974 ident = self.ident 975 976 code(''' 977// Auto generated C++ code started by $__file__:$__line__ 978// ${ident}: ${{self.short}} 979 980#include "mem/protocol/${ident}_Profiler.hh" 981 982${ident}_Profiler::${ident}_Profiler() 983{ 984 for (int state = 0; state < ${ident}_State_NUM; state++) { 985 for (int event = 0; event < ${ident}_Event_NUM; event++) { 986 m_possible[state][event] = false; 987 m_counters[state][event] = 0; 988 } 989 } 990 for (int event = 0; event < ${ident}_Event_NUM; event++) { 991 m_event_counters[event] = 0; 992 } 993} 994void ${ident}_Profiler::setVersion(int version) 995{ 996 m_version = version; 997} 998void ${ident}_Profiler::clearStats() 999{ 1000 for (int state = 0; state < ${ident}_State_NUM; state++) { 1001 for (int event = 0; event < ${ident}_Event_NUM; event++) { 1002 m_counters[state][event] = 0; 1003 } 1004 } 1005 1006 for (int event = 0; event < ${ident}_Event_NUM; event++) { 1007 m_event_counters[event] = 0; 1008 } 1009} 1010void ${ident}_Profiler::countTransition(${ident}_State state, ${ident}_Event event) 1011{ 1012 assert(m_possible[state][event]); 1013 m_counters[state][event]++; 1014 m_event_counters[event]++; 1015} 1016void ${ident}_Profiler::possibleTransition(${ident}_State state, ${ident}_Event event) 1017{ 1018 m_possible[state][event] = true; 1019} 1020void ${ident}_Profiler::dumpStats(ostream& out) const 1021{ 1022 out << " --- ${ident} " << m_version << " ---" << endl; 1023 out << " - Event Counts -" << endl; 1024 for (int event = 0; event < ${ident}_Event_NUM; event++) { 1025 int count = m_event_counters[event]; 1026 out << (${ident}_Event) event << " " << count << endl; 1027 } 1028 out << endl; 1029 out << " - Transitions -" << endl; 1030 for (int state = 0; state < ${ident}_State_NUM; state++) { 1031 for (int event = 0; event < ${ident}_Event_NUM; event++) { 1032 if (m_possible[state][event]) { 1033 int count = m_counters[state][event]; 1034 out << (${ident}_State) state << " " << (${ident}_Event) event << " " << count; 1035 if (count == 0) { 1036 out << " <-- "; 1037 } 1038 out << endl; 1039 } 1040 } 1041 out << endl; 1042 } 1043} 1044''') 1045 code.write(path, "%s_Profiler.cc" % self.ident) 1046 1047 # ************************** 1048 # ******* HTML Files ******* 1049 # ************************** 1050 def frameRef(self, click_href, click_target, over_href, over_target_num, 1051 text): 1052 code = code_formatter(fix_newlines=False) 1053 code("""<A href=\"$click_href\" target=\"$click_target\" onMouseOver=\"if (parent.frames[$over_target_num].location != parent.location + '$over_href') { parent.frames[$over_target_num].location='$over_href' }\" >${{html.formatShorthand(text)}}</A>""") 1054 return str(code) 1055 1056 def writeHTMLFiles(self, path): 1057 # Create table with no row hilighted 1058 self.printHTMLTransitions(path, None) 1059 1060 # Generate transition tables 1061 for state in self.states.itervalues(): 1062 self.printHTMLTransitions(path, state) 1063 1064 # Generate action descriptions 1065 for action in self.actions.itervalues(): 1066 name = "%s_action_%s.html" % (self.ident, action.ident) 1067 code = html.createSymbol(action, "Action") 1068 code.write(path, name) 1069 1070 # Generate state descriptions 1071 for state in self.states.itervalues(): 1072 name = "%s_State_%s.html" % (self.ident, state.ident) 1073 code = html.createSymbol(state, "State") 1074 code.write(path, name) 1075 1076 # Generate event descriptions 1077 for event in self.events.itervalues(): 1078 name = "%s_Event_%s.html" % (self.ident, event.ident) 1079 code = html.createSymbol(event, "Event") 1080 code.write(path, name) 1081 1082 def printHTMLTransitions(self, path, active_state): 1083 code = code_formatter() 1084 1085 code(''' 1086<HTML><BODY link="blue" vlink="blue"> 1087 1088<H1 align="center">${{html.formatShorthand(self.short)}}: 1089''') 1090 code.indent() 1091 for i,machine in enumerate(self.symtab.getAllType(StateMachine)): 1092 mid = machine.ident 1093 if i != 0: 1094 extra = " - " 1095 else: 1096 extra = "" 1097 if machine == self: 1098 code('$extra$mid') 1099 else: 1100 code('$extra<A target="Table" href="${mid}_table.html">$mid</A>') 1101 code.dedent() 1102 1103 code(""" 1104</H1> 1105 1106<TABLE border=1> 1107<TR> 1108 <TH> </TH> 1109""") 1110 1111 for event in self.events.itervalues(): 1112 href = "%s_Event_%s.html" % (self.ident, event.ident) 1113 ref = self.frameRef(href, "Status", href, "1", event.short) 1114 code('<TH bgcolor=white>$ref</TH>') 1115 1116 code('</TR>') 1117 # -- Body of table 1118 for state in self.states.itervalues(): 1119 # -- Each row 1120 if state == active_state: 1121 color = "yellow" 1122 else: 1123 color = "white" 1124 1125 click = "%s_table_%s.html" % (self.ident, state.ident) 1126 over = "%s_State_%s.html" % (self.ident, state.ident) 1127 text = html.formatShorthand(state.short) 1128 ref = self.frameRef(click, "Table", over, "1", state.short) 1129 code(''' 1130<TR> 1131 <TH bgcolor=$color>$ref</TH> 1132''') 1133 1134 # -- One column for each event 1135 for event in self.events.itervalues(): 1136 trans = self.table.get((state,event), None) 1137 if trans is None: 1138 # This is the no transition case 1139 if state == active_state: 1140 color = "#C0C000" 1141 else: 1142 color = "lightgrey" 1143 1144 code('<TD bgcolor=$color> </TD>') 1145 continue 1146 1147 next = trans.nextState 1148 stall_action = False 1149 1150 # -- Get the actions 1151 for action in trans.actions: 1152 if action.ident == "z_stall" or \ 1153 action.ident == "zz_recycleMandatoryQueue": 1154 stall_action = True 1155 1156 # -- Print out "actions/next-state" 1157 if stall_action: 1158 if state == active_state: 1159 color = "#C0C000" 1160 else: 1161 color = "lightgrey" 1162 1163 elif active_state and next.ident == active_state.ident: 1164 color = "aqua" 1165 elif state == active_state: 1166 color = "yellow" 1167 else: 1168 color = "white" 1169 1170 fix = code.nofix() 1171 code('<TD bgcolor=$color>') 1172 for action in trans.actions: 1173 href = "%s_action_%s.html" % (self.ident, action.ident) 1174 ref = self.frameRef(href, "Status", href, "1", 1175 action.short) 1176 code(' $ref\n') 1177 if next != state: 1178 if trans.actions: 1179 code('/') 1180 click = "%s_table_%s.html" % (self.ident, next.ident) 1181 over = "%s_State_%s.html" % (self.ident, next.ident) 1182 ref = self.frameRef(click, "Table", over, "1", next.short) 1183 code("$ref") 1184 code("</TD>\n") 1185 code.fix(fix) 1186 1187 # -- Each row 1188 if state == active_state: 1189 color = "yellow" 1190 else: 1191 color = "white" 1192 1193 click = "%s_table_%s.html" % (self.ident, state.ident) 1194 over = "%s_State_%s.html" % (self.ident, state.ident) 1195 ref = self.frameRef(click, "Table", over, "1", state.short) 1196 code(''' 1197 <TH bgcolor=$color>$ref</TH> 1198</TR> 1199''') 1200 code(''' 1201<TR> 1202 <TH> </TH> 1203''') 1204 1205 for event in self.events.itervalues(): 1206 href = "%s_Event_%s.html" % (self.ident, event.ident) 1207 ref = self.frameRef(href, "Status", href, "1", event.short) 1208 code('<TH bgcolor=white>$ref</TH>') 1209 code(''' 1210</TR> 1211</TABLE> 1212</BODY></HTML> 1213''') 1214 1215 1216 if active_state: 1217 name = "%s_table_%s.html" % (self.ident, active_state.ident) 1218 else: 1219 name = "%s_table.html" % self.ident 1220 code.write(path, name) 1221 1222__all__ = [ "StateMachine" ] 1223