StateMachine.py revision 6969:1ab268977bbb
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; 237 void 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 m_is_blocking = false; 353''') 354 code.indent() 355 356 # 357 # After initializing the universal machine parameters, initialize the 358 # this machines config parameters. Also detemine if these configuration 359 # params include a sequencer. This information will be used later for 360 # contecting the sequencer back to the L1 cache controller. 361 # 362 contains_sequencer = False 363 for param in self.config_parameters: 364 if param.name == "sequencer" or param.name == "dma_sequencer": 365 contains_sequencer = True 366 if param.pointer: 367 code('m_${{param.name}}_ptr = p->${{param.name}};') 368 else: 369 code('m_${{param.name}} = p->${{param.name}};') 370 371 # 372 # For the l1 cache controller, add the special atomic support which 373 # includes passing the sequencer a pointer to the controller. 374 # 375 if self.ident == "L1Cache": 376 if not contains_sequencer: 377 self.error("The L1Cache controller must include the sequencer " \ 378 "configuration parameter") 379 380 code(''' 381m_sequencer_ptr->setController(this); 382''') 383 # 384 # For the DMA controller, pass the sequencer a pointer to the 385 # controller. 386 # 387 if self.ident == "DMA": 388 if not contains_sequencer: 389 self.error("The DMA controller must include the sequencer " \ 390 "configuration parameter") 391 392 code(''' 393m_dma_sequencer_ptr->setController(this); 394''') 395 396 code('m_num_controllers++;') 397 for var in self.objects: 398 if var.ident.find("mandatoryQueue") >= 0: 399 code('m_${{var.c_ident}}_ptr = new ${{var.type.c_ident}}();') 400 401 code.dedent() 402 code(''' 403} 404 405void $c_ident::init() 406{ 407 m_machineID.type = MachineType_${ident}; 408 m_machineID.num = m_version; 409 410 // Objects 411 s_profiler.setVersion(m_version); 412''') 413 414 code.indent() 415 for var in self.objects: 416 vtype = var.type 417 vid = "m_%s_ptr" % var.c_ident 418 if "network" not in var: 419 # Not a network port object 420 if "primitive" in vtype: 421 code('$vid = new ${{vtype.c_ident}};') 422 if "default" in var: 423 code('(*$vid) = ${{var["default"]}};') 424 else: 425 # Normal Object 426 # added by SS 427 if "factory" in var: 428 code('$vid = ${{var["factory"]}};') 429 elif var.ident.find("mandatoryQueue") < 0: 430 th = var.get("template_hack", "") 431 expr = "%s = new %s%s" % (vid, vtype.c_ident, th) 432 433 args = "" 434 if "non_obj" not in vtype and not vtype.isEnumeration: 435 if expr.find("TBETable") >= 0: 436 args = "m_number_of_TBEs" 437 else: 438 args = var.get("constructor_hack", "") 439 args = "(%s)" % args 440 441 code('$expr$args;') 442 else: 443 code(';') 444 445 code('assert($vid != NULL);') 446 447 if "default" in var: 448 code('(*$vid) = ${{var["default"]}}; // Object default') 449 elif "default" in vtype: 450 code('(*$vid) = ${{vtype["default"]}}; // Type ${{vtype.ident}} default') 451 452 # Set ordering 453 if "ordered" in var and "trigger_queue" not in var: 454 # A buffer 455 code('$vid->setOrdering(${{var["ordered"]}});') 456 457 # Set randomization 458 if "random" in var: 459 # A buffer 460 code('$vid->setRandomization(${{var["random"]}});') 461 462 # Set Priority 463 if vtype.isBuffer and \ 464 "rank" in var and "trigger_queue" not in var: 465 code('$vid->setPriority(${{var["rank"]}});') 466 else: 467 # Network port object 468 network = var["network"] 469 ordered = var["ordered"] 470 vnet = var["virtual_network"] 471 472 assert var.machine is not None 473 code(''' 474$vid = m_net_ptr->get${network}NetQueue(m_version+MachineType_base_number(string_to_MachineType("${{var.machine.ident}}")), $ordered, $vnet); 475''') 476 477 code('assert($vid != NULL);') 478 479 # Set ordering 480 if "ordered" in var: 481 # A buffer 482 code('$vid->setOrdering(${{var["ordered"]}});') 483 484 # Set randomization 485 if "random" in var: 486 # A buffer 487 code('$vid->setRandomization(${{var["random"]}})') 488 489 # Set Priority 490 if "rank" in var: 491 code('$vid->setPriority(${{var["rank"]}})') 492 493 # Set buffer size 494 if vtype.isBuffer: 495 code(''' 496if (m_buffer_size > 0) { 497 $vid->setSize(m_buffer_size); 498} 499''') 500 501 # set description (may be overriden later by port def) 502 code('$vid->setDescription("[Version " + int_to_string(m_version) + ", ${ident}, name=${{var.c_ident}}]");') 503 504 # Set the queue consumers 505 code.insert_newline() 506 for port in self.in_ports: 507 code('${{port.code}}.setConsumer(this);') 508 509 # Set the queue descriptions 510 code.insert_newline() 511 for port in self.in_ports: 512 code('${{port.code}}.setDescription("[Version " + int_to_string(m_version) + ", $ident, $port]");') 513 514 # Initialize the transition profiling 515 code.insert_newline() 516 for trans in self.transitions: 517 # Figure out if we stall 518 stall = False 519 for action in trans.actions: 520 if action.ident == "z_stall": 521 stall = True 522 523 # Only possible if it is not a 'z' case 524 if not stall: 525 state = "%s_State_%s" % (self.ident, trans.state.ident) 526 event = "%s_Event_%s" % (self.ident, trans.event.ident) 527 code('s_profiler.possibleTransition($state, $event);') 528 529 # added by SS to initialize recycle_latency of message buffers 530 for buf in self.message_buffer_names: 531 code("$buf->setRecycleLatency(m_recycle_latency);") 532 533 code.dedent() 534 code('}') 535 536 has_mandatory_q = False 537 for port in self.in_ports: 538 if port.code.find("mandatoryQueue_ptr") >= 0: 539 has_mandatory_q = True 540 541 if has_mandatory_q: 542 mq_ident = "m_%s_mandatoryQueue_ptr" % self.ident 543 else: 544 mq_ident = "NULL" 545 546 code(''' 547int $c_ident::getNumControllers() { 548 return m_num_controllers; 549} 550 551MessageBuffer* $c_ident::getMandatoryQueue() const { 552 return $mq_ident; 553} 554 555const int & $c_ident::getVersion() const{ 556 return m_version; 557} 558 559const string $c_ident::toString() const{ 560 return "$c_ident"; 561} 562 563const string $c_ident::getName() const{ 564 return m_name; 565} 566const MachineType $c_ident::getMachineType() const{ 567 return MachineType_${ident}; 568} 569 570void $c_ident::blockOnQueue(Address addr, MessageBuffer* port) { 571 m_is_blocking = true; 572 m_block_map[addr] = port; 573} 574void $c_ident::unblock(Address addr) { 575 m_block_map.erase(addr); 576 if (m_block_map.size() == 0) { 577 m_is_blocking = false; 578 } 579} 580 581void $c_ident::print(ostream& out) const { out << "[$c_ident " << m_version << "]"; } 582 583void $c_ident::printConfig(ostream& out) const { 584 out << "$c_ident config: " << m_name << endl; 585 out << " version: " << m_version << endl; 586 for (map<string, string>::const_iterator it = m_cfg.begin(); it != m_cfg.end(); it++) { 587 out << " " << (*it).first << ": " << (*it).second << endl; 588 } 589} 590 591void $c_ident::printStats(ostream& out) const { 592''') 593 # 594 # Cache and Memory Controllers have specific profilers associated with 595 # them. Print out these stats before dumping state transition stats. 596 # 597 for param in self.config_parameters: 598 if param.type_ast.type.ident == "CacheMemory" or \ 599 param.type_ast.type.ident == "MemoryControl": 600 assert(param.pointer) 601 code(' m_${{param.ident}}_ptr->printStats(out);') 602 603 code(''' 604 s_profiler.dumpStats(out); 605} 606 607void $c_ident::clearStats() { 608''') 609 # 610 # Cache and Memory Controllers have specific profilers associated with 611 # them. These stats must be cleared too. 612 # 613 for param in self.config_parameters: 614 if param.type_ast.type.ident == "CacheMemory" or \ 615 param.type_ast.type.ident == "MemoryControl": 616 assert(param.pointer) 617 code(' m_${{param.ident}}_ptr->clearStats();') 618 619 code(''' 620 s_profiler.clearStats(); 621} 622 623// Actions 624''') 625 626 for action in self.actions.itervalues(): 627 if "c_code" not in action: 628 continue 629 630 code(''' 631/** \\brief ${{action.desc}} */ 632void $c_ident::${{action.ident}}(const Address& addr) 633{ 634 DEBUG_MSG(GENERATED_COMP, HighPrio, "executing"); 635 ${{action["c_code"]}} 636} 637 638''') 639 code.write(path, "%s.cc" % c_ident) 640 641 def printCWakeup(self, path): 642 '''Output the wakeup loop for the events''' 643 644 code = code_formatter() 645 ident = self.ident 646 647 code(''' 648// Auto generated C++ code started by $__file__:$__line__ 649// ${ident}: ${{self.short}} 650 651#include "mem/ruby/common/Global.hh" 652#include "mem/ruby/slicc_interface/RubySlicc_includes.hh" 653#include "mem/protocol/${ident}_Controller.hh" 654#include "mem/protocol/${ident}_State.hh" 655#include "mem/protocol/${ident}_Event.hh" 656#include "mem/protocol/Types.hh" 657#include "mem/ruby/system/System.hh" 658 659void ${ident}_Controller::wakeup() 660{ 661 662 int counter = 0; 663 while (true) { 664 // Some cases will put us into an infinite loop without this limit 665 assert(counter <= m_transitions_per_cycle); 666 if (counter == m_transitions_per_cycle) { 667 g_system_ptr->getProfiler()->controllerBusy(m_machineID); // Count how often we\'re fully utilized 668 g_eventQueue_ptr->scheduleEvent(this, 1); // Wakeup in another cycle and try again 669 break; 670 } 671''') 672 673 code.indent() 674 code.indent() 675 676 # InPorts 677 # 678 for port in self.in_ports: 679 code.indent() 680 code('// ${ident}InPort $port') 681 code('${{port["c_code_in_port"]}}') 682 code.dedent() 683 684 code('') 685 686 code.dedent() 687 code.dedent() 688 code(''' 689 break; // If we got this far, we have nothing left todo 690 } 691} 692''') 693 694 code.write(path, "%s_Wakeup.cc" % self.ident) 695 696 def printCSwitch(self, path): 697 '''Output switch statement for transition table''' 698 699 code = code_formatter() 700 ident = self.ident 701 702 code(''' 703// Auto generated C++ code started by $__file__:$__line__ 704// ${ident}: ${{self.short}} 705 706#include "mem/ruby/common/Global.hh" 707#include "mem/protocol/${ident}_Controller.hh" 708#include "mem/protocol/${ident}_State.hh" 709#include "mem/protocol/${ident}_Event.hh" 710#include "mem/protocol/Types.hh" 711#include "mem/ruby/system/System.hh" 712 713#define HASH_FUN(state, event) ((int(state)*${ident}_Event_NUM)+int(event)) 714 715#define GET_TRANSITION_COMMENT() (${ident}_transitionComment.str()) 716#define CLEAR_TRANSITION_COMMENT() (${ident}_transitionComment.str("")) 717 718TransitionResult ${ident}_Controller::doTransition(${ident}_Event event, ${ident}_State state, const Address& addr 719) 720{ 721 ${ident}_State next_state = state; 722 723 DEBUG_NEWLINE(GENERATED_COMP, MedPrio); 724 DEBUG_MSG(GENERATED_COMP, MedPrio, *this); 725 DEBUG_EXPR(GENERATED_COMP, MedPrio, g_eventQueue_ptr->getTime()); 726 DEBUG_EXPR(GENERATED_COMP, MedPrio,state); 727 DEBUG_EXPR(GENERATED_COMP, MedPrio,event); 728 DEBUG_EXPR(GENERATED_COMP, MedPrio,addr); 729 730 TransitionResult result = doTransitionWorker(event, state, next_state, addr); 731 732 if (result == TransitionResult_Valid) { 733 DEBUG_EXPR(GENERATED_COMP, MedPrio, next_state); 734 DEBUG_NEWLINE(GENERATED_COMP, MedPrio); 735 s_profiler.countTransition(state, event); 736 if (Debug::getProtocolTrace()) { 737 g_system_ptr->getProfiler()->profileTransition("${ident}", m_version, addr, 738 ${ident}_State_to_string(state), 739 ${ident}_Event_to_string(event), 740 ${ident}_State_to_string(next_state), GET_TRANSITION_COMMENT()); 741 } 742 CLEAR_TRANSITION_COMMENT(); 743 ${ident}_setState(addr, next_state); 744 745 } else if (result == TransitionResult_ResourceStall) { 746 if (Debug::getProtocolTrace()) { 747 g_system_ptr->getProfiler()->profileTransition("${ident}", m_version, addr, 748 ${ident}_State_to_string(state), 749 ${ident}_Event_to_string(event), 750 ${ident}_State_to_string(next_state), 751 "Resource Stall"); 752 } 753 } else if (result == TransitionResult_ProtocolStall) { 754 DEBUG_MSG(GENERATED_COMP, HighPrio, "stalling"); 755 DEBUG_NEWLINE(GENERATED_COMP, MedPrio); 756 if (Debug::getProtocolTrace()) { 757 g_system_ptr->getProfiler()->profileTransition("${ident}", m_version, addr, 758 ${ident}_State_to_string(state), 759 ${ident}_Event_to_string(event), 760 ${ident}_State_to_string(next_state), 761 "Protocol Stall"); 762 } 763 } 764 765 return result; 766} 767 768TransitionResult ${ident}_Controller::doTransitionWorker(${ident}_Event event, ${ident}_State state, ${ident}_State& next_state, const Address& addr 769) 770{ 771 switch(HASH_FUN(state, event)) { 772''') 773 774 # This map will allow suppress generating duplicate code 775 cases = orderdict() 776 777 for trans in self.transitions: 778 case_string = "%s_State_%s, %s_Event_%s" % \ 779 (self.ident, trans.state.ident, self.ident, trans.event.ident) 780 781 case = code_formatter() 782 # Only set next_state if it changes 783 if trans.state != trans.nextState: 784 ns_ident = trans.nextState.ident 785 case('next_state = ${ident}_State_${ns_ident};') 786 787 actions = trans.actions 788 789 # Check for resources 790 case_sorter = [] 791 res = trans.resources 792 for key,val in res.iteritems(): 793 if key.type.ident != "DNUCAStopTable": 794 val = ''' 795if (!%s.areNSlotsAvailable(%s)) { 796 return TransitionResult_ResourceStall; 797} 798''' % (key.code, val) 799 case_sorter.append(val) 800 801 802 # Emit the code sequences in a sorted order. This makes the 803 # output deterministic (without this the output order can vary 804 # since Map's keys() on a vector of pointers is not deterministic 805 for c in sorted(case_sorter): 806 case("$c") 807 808 # Figure out if we stall 809 stall = False 810 for action in actions: 811 if action.ident == "z_stall": 812 stall = True 813 break 814 815 if stall: 816 case('return TransitionResult_ProtocolStall;') 817 else: 818 for action in actions: 819 case('${{action.ident}}(addr);') 820 case('return TransitionResult_Valid;') 821 822 case = str(case) 823 824 # Look to see if this transition code is unique. 825 if case not in cases: 826 cases[case] = [] 827 828 cases[case].append(case_string) 829 830 # Walk through all of the unique code blocks and spit out the 831 # corresponding case statement elements 832 for case,transitions in cases.iteritems(): 833 # Iterative over all the multiple transitions that share 834 # the same code 835 for trans in transitions: 836 code(' case HASH_FUN($trans):') 837 code(' {') 838 code(' $case') 839 code(' }') 840 841 code(''' 842 default: 843 WARN_EXPR(m_version); 844 WARN_EXPR(g_eventQueue_ptr->getTime()); 845 WARN_EXPR(addr); 846 WARN_EXPR(event); 847 WARN_EXPR(state); 848 ERROR_MSG(\"Invalid transition\"); 849 } 850 return TransitionResult_Valid; 851} 852''') 853 code.write(path, "%s_Transitions.cc" % self.ident) 854 855 def printProfilerHH(self, path): 856 code = code_formatter() 857 ident = self.ident 858 859 code(''' 860// Auto generated C++ code started by $__file__:$__line__ 861// ${ident}: ${{self.short}} 862 863#ifndef ${ident}_PROFILER_H 864#define ${ident}_PROFILER_H 865 866#include "mem/ruby/common/Global.hh" 867#include "mem/protocol/${ident}_State.hh" 868#include "mem/protocol/${ident}_Event.hh" 869 870class ${ident}_Profiler { 871 public: 872 ${ident}_Profiler(); 873 void setVersion(int version); 874 void countTransition(${ident}_State state, ${ident}_Event event); 875 void possibleTransition(${ident}_State state, ${ident}_Event event); 876 void dumpStats(ostream& out) const; 877 void clearStats(); 878 879 private: 880 int m_counters[${ident}_State_NUM][${ident}_Event_NUM]; 881 int m_event_counters[${ident}_Event_NUM]; 882 bool m_possible[${ident}_State_NUM][${ident}_Event_NUM]; 883 int m_version; 884}; 885 886#endif // ${ident}_PROFILER_H 887''') 888 code.write(path, "%s_Profiler.hh" % self.ident) 889 890 def printProfilerCC(self, path): 891 code = code_formatter() 892 ident = self.ident 893 894 code(''' 895// Auto generated C++ code started by $__file__:$__line__ 896// ${ident}: ${{self.short}} 897 898#include "mem/protocol/${ident}_Profiler.hh" 899 900${ident}_Profiler::${ident}_Profiler() 901{ 902 for (int state = 0; state < ${ident}_State_NUM; state++) { 903 for (int event = 0; event < ${ident}_Event_NUM; event++) { 904 m_possible[state][event] = false; 905 m_counters[state][event] = 0; 906 } 907 } 908 for (int event = 0; event < ${ident}_Event_NUM; event++) { 909 m_event_counters[event] = 0; 910 } 911} 912void ${ident}_Profiler::setVersion(int version) 913{ 914 m_version = version; 915} 916void ${ident}_Profiler::clearStats() 917{ 918 for (int state = 0; state < ${ident}_State_NUM; state++) { 919 for (int event = 0; event < ${ident}_Event_NUM; event++) { 920 m_counters[state][event] = 0; 921 } 922 } 923 924 for (int event = 0; event < ${ident}_Event_NUM; event++) { 925 m_event_counters[event] = 0; 926 } 927} 928void ${ident}_Profiler::countTransition(${ident}_State state, ${ident}_Event event) 929{ 930 assert(m_possible[state][event]); 931 m_counters[state][event]++; 932 m_event_counters[event]++; 933} 934void ${ident}_Profiler::possibleTransition(${ident}_State state, ${ident}_Event event) 935{ 936 m_possible[state][event] = true; 937} 938void ${ident}_Profiler::dumpStats(ostream& out) const 939{ 940 out << " --- ${ident} " << m_version << " ---" << endl; 941 out << " - Event Counts -" << endl; 942 for (int event = 0; event < ${ident}_Event_NUM; event++) { 943 int count = m_event_counters[event]; 944 out << (${ident}_Event) event << " " << count << endl; 945 } 946 out << endl; 947 out << " - Transitions -" << endl; 948 for (int state = 0; state < ${ident}_State_NUM; state++) { 949 for (int event = 0; event < ${ident}_Event_NUM; event++) { 950 if (m_possible[state][event]) { 951 int count = m_counters[state][event]; 952 out << (${ident}_State) state << " " << (${ident}_Event) event << " " << count; 953 if (count == 0) { 954 out << " <-- "; 955 } 956 out << endl; 957 } 958 } 959 out << endl; 960 } 961} 962''') 963 code.write(path, "%s_Profiler.cc" % self.ident) 964 965 # ************************** 966 # ******* HTML Files ******* 967 # ************************** 968 def frameRef(self, click_href, click_target, over_href, over_target_num, 969 text): 970 code = code_formatter(fix_newlines=False) 971 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>""") 972 return str(code) 973 974 def writeHTMLFiles(self, path): 975 # Create table with no row hilighted 976 self.printHTMLTransitions(path, None) 977 978 # Generate transition tables 979 for state in self.states.itervalues(): 980 self.printHTMLTransitions(path, state) 981 982 # Generate action descriptions 983 for action in self.actions.itervalues(): 984 name = "%s_action_%s.html" % (self.ident, action.ident) 985 code = html.createSymbol(action, "Action") 986 code.write(path, name) 987 988 # Generate state descriptions 989 for state in self.states.itervalues(): 990 name = "%s_State_%s.html" % (self.ident, state.ident) 991 code = html.createSymbol(state, "State") 992 code.write(path, name) 993 994 # Generate event descriptions 995 for event in self.events.itervalues(): 996 name = "%s_Event_%s.html" % (self.ident, event.ident) 997 code = html.createSymbol(event, "Event") 998 code.write(path, name) 999 1000 def printHTMLTransitions(self, path, active_state): 1001 code = code_formatter() 1002 1003 code(''' 1004<HTML><BODY link="blue" vlink="blue"> 1005 1006<H1 align="center">${{html.formatShorthand(self.short)}}: 1007''') 1008 code.indent() 1009 for i,machine in enumerate(self.symtab.getAllType(StateMachine)): 1010 mid = machine.ident 1011 if i != 0: 1012 extra = " - " 1013 else: 1014 extra = "" 1015 if machine == self: 1016 code('$extra$mid') 1017 else: 1018 code('$extra<A target="Table" href="${mid}_table.html">$mid</A>') 1019 code.dedent() 1020 1021 code(""" 1022</H1> 1023 1024<TABLE border=1> 1025<TR> 1026 <TH> </TH> 1027""") 1028 1029 for event in self.events.itervalues(): 1030 href = "%s_Event_%s.html" % (self.ident, event.ident) 1031 ref = self.frameRef(href, "Status", href, "1", event.short) 1032 code('<TH bgcolor=white>$ref</TH>') 1033 1034 code('</TR>') 1035 # -- Body of table 1036 for state in self.states.itervalues(): 1037 # -- Each row 1038 if state == active_state: 1039 color = "yellow" 1040 else: 1041 color = "white" 1042 1043 click = "%s_table_%s.html" % (self.ident, state.ident) 1044 over = "%s_State_%s.html" % (self.ident, state.ident) 1045 text = html.formatShorthand(state.short) 1046 ref = self.frameRef(click, "Table", over, "1", state.short) 1047 code(''' 1048<TR> 1049 <TH bgcolor=$color>$ref</TH> 1050''') 1051 1052 # -- One column for each event 1053 for event in self.events.itervalues(): 1054 trans = self.table.get((state,event), None) 1055 if trans is None: 1056 # This is the no transition case 1057 if state == active_state: 1058 color = "#C0C000" 1059 else: 1060 color = "lightgrey" 1061 1062 code('<TD bgcolor=$color> </TD>') 1063 continue 1064 1065 next = trans.nextState 1066 stall_action = False 1067 1068 # -- Get the actions 1069 for action in trans.actions: 1070 if action.ident == "z_stall" or \ 1071 action.ident == "zz_recycleMandatoryQueue": 1072 stall_action = True 1073 1074 # -- Print out "actions/next-state" 1075 if stall_action: 1076 if state == active_state: 1077 color = "#C0C000" 1078 else: 1079 color = "lightgrey" 1080 1081 elif active_state and next.ident == active_state.ident: 1082 color = "aqua" 1083 elif state == active_state: 1084 color = "yellow" 1085 else: 1086 color = "white" 1087 1088 fix = code.nofix() 1089 code('<TD bgcolor=$color>') 1090 for action in trans.actions: 1091 href = "%s_action_%s.html" % (self.ident, action.ident) 1092 ref = self.frameRef(href, "Status", href, "1", 1093 action.short) 1094 code(' $ref\n') 1095 if next != state: 1096 if trans.actions: 1097 code('/') 1098 click = "%s_table_%s.html" % (self.ident, next.ident) 1099 over = "%s_State_%s.html" % (self.ident, next.ident) 1100 ref = self.frameRef(click, "Table", over, "1", next.short) 1101 code("$ref") 1102 code("</TD>\n") 1103 code.fix(fix) 1104 1105 # -- Each row 1106 if state == active_state: 1107 color = "yellow" 1108 else: 1109 color = "white" 1110 1111 click = "%s_table_%s.html" % (self.ident, state.ident) 1112 over = "%s_State_%s.html" % (self.ident, state.ident) 1113 ref = self.frameRef(click, "Table", over, "1", state.short) 1114 code(''' 1115 <TH bgcolor=$color>$ref</TH> 1116</TR> 1117''') 1118 code(''' 1119<TR> 1120 <TH> </TH> 1121''') 1122 1123 for event in self.events.itervalues(): 1124 href = "%s_Event_%s.html" % (self.ident, event.ident) 1125 ref = self.frameRef(href, "Status", href, "1", event.short) 1126 code('<TH bgcolor=white>$ref</TH>') 1127 code(''' 1128</TR> 1129</TABLE> 1130</BODY></HTML> 1131''') 1132 1133 1134 if active_state: 1135 name = "%s_table_%s.html" % (self.ident, active_state.ident) 1136 else: 1137 name = "%s_table.html" % self.ident 1138 code.write(path, name) 1139 1140__all__ = [ "StateMachine" ] 1141