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