StateMachine.py revision 7007
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 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 = self.symtab.codeFormatter() 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 = self.symtab.codeFormatter() 184 ident = self.ident 185 c_ident = "%s_Controller" % self.ident 186 187 self.message_buffer_names = [] 188 189 code(''' 190/** \\file $c_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_HH__ 197#define __${ident}_CONTROLLER_HH__ 198 199#include <iostream> 200#include <sstream> 201#include <string> 202 203#include "params/$c_ident.hh" 204 205#include "mem/ruby/common/Global.hh" 206#include "mem/ruby/common/Consumer.hh" 207#include "mem/ruby/slicc_interface/AbstractController.hh" 208#include "mem/protocol/TransitionResult.hh" 209#include "mem/protocol/Types.hh" 210#include "mem/protocol/${ident}_Profiler.hh" 211''') 212 213 seen_types = set() 214 for var in self.objects: 215 if var.type.ident not in seen_types and not var.type.isPrimitive: 216 code('#include "mem/protocol/${{var.type.c_ident}}.hh"') 217 seen_types.add(var.type.ident) 218 219 # for adding information to the protocol debug trace 220 code(''' 221extern std::stringstream ${ident}_transitionComment; 222 223class $c_ident : public AbstractController 224{ 225// the coherence checker needs to call isBlockExclusive() and isBlockShared() 226// making the Chip a friend class is an easy way to do this for now 227 228public: 229 typedef ${c_ident}Params Params; 230 $c_ident(const Params *p); 231 static int getNumControllers(); 232 void init(); 233 MessageBuffer* getMandatoryQueue() const; 234 const int & getVersion() const; 235 const std::string toString() const; 236 const std::string getName() const; 237 const MachineType getMachineType() const; 238 void initNetworkPtr(Network* net_ptr) { m_net_ptr = net_ptr; } 239 void print(std::ostream& out) const; 240 void printConfig(std::ostream& out) const; 241 void wakeup(); 242 void printStats(std::ostream& out) const; 243 void clearStats(); 244 void blockOnQueue(Address addr, MessageBuffer* port); 245 void unblock(Address addr); 246 247private: 248''') 249 250 code.indent() 251 # added by SS 252 for param in self.config_parameters: 253 if param.pointer: 254 code('${{param.type_ast.type}}* m_${{param.ident}}_ptr;') 255 else: 256 code('${{param.type_ast.type}} m_${{param.ident}};') 257 258 code(''' 259int m_number_of_TBEs; 260 261TransitionResult doTransition(${ident}_Event event, 262 ${ident}_State state, 263 const Address& addr); 264 265TransitionResult doTransitionWorker(${ident}_Event event, 266 ${ident}_State state, 267 ${ident}_State& next_state, 268 const Address& addr); 269 270std::string m_name; 271int m_transitions_per_cycle; 272int m_buffer_size; 273int m_recycle_latency; 274map<std::string, std::string> m_cfg; 275NodeID m_version; 276Network* m_net_ptr; 277MachineID m_machineID; 278bool m_is_blocking; 279map< Address, MessageBuffer* > m_block_map; 280${ident}_Profiler s_profiler; 281static int m_num_controllers; 282 283// Internal functions 284''') 285 286 for func in self.functions: 287 proto = func.prototype 288 if proto: 289 code('$proto') 290 291 code(''' 292 293// Actions 294''') 295 for action in self.actions.itervalues(): 296 code('/** \\brief ${{action.desc}} */') 297 code('void ${{action.ident}}(const Address& addr);') 298 299 # the controller internal variables 300 code(''' 301 302// Objects 303''') 304 for var in self.objects: 305 th = var.get("template_hack", "") 306 code('${{var.type.c_ident}}$th* m_${{var.c_ident}}_ptr;') 307 308 if var.type.ident == "MessageBuffer": 309 self.message_buffer_names.append("m_%s_ptr" % var.c_ident) 310 311 code.dedent() 312 code('};') 313 code('#endif // __${ident}_CONTROLLER_H__') 314 code.write(path, '%s.hh' % c_ident) 315 316 def printControllerCC(self, path): 317 '''Output the actions for performing the actions''' 318 319 code = self.symtab.codeFormatter() 320 ident = self.ident 321 c_ident = "%s_Controller" % self.ident 322 323 code(''' 324/** \\file $c_ident.cc 325 * 326 * Auto generated C++ code started by $__file__:$__line__ 327 * Created by slicc definition of Module "${{self.short}}" 328 */ 329 330#include <sstream> 331#include <string> 332 333#include "mem/ruby/common/Global.hh" 334#include "mem/ruby/slicc_interface/RubySlicc_includes.hh" 335#include "mem/protocol/${ident}_Controller.hh" 336#include "mem/protocol/${ident}_State.hh" 337#include "mem/protocol/${ident}_Event.hh" 338#include "mem/protocol/Types.hh" 339#include "mem/ruby/system/System.hh" 340 341using namespace std; 342''') 343 344 # include object classes 345 seen_types = set() 346 for var in self.objects: 347 if var.type.ident not in seen_types and not var.type.isPrimitive: 348 code('#include "mem/protocol/${{var.type.c_ident}}.hh"') 349 seen_types.add(var.type.ident) 350 351 code(''' 352$c_ident * 353${c_ident}Params::create() 354{ 355 return new $c_ident(this); 356} 357 358int $c_ident::m_num_controllers = 0; 359 360// for adding information to the protocol debug trace 361stringstream ${ident}_transitionComment; 362#define APPEND_TRANSITION_COMMENT(str) (${ident}_transitionComment << str) 363 364/** \\brief constructor */ 365$c_ident::$c_ident(const Params *p) 366 : AbstractController(p) 367{ 368 m_version = p->version; 369 m_transitions_per_cycle = p->transitions_per_cycle; 370 m_buffer_size = p->buffer_size; 371 m_recycle_latency = p->recycle_latency; 372 m_number_of_TBEs = p->number_of_TBEs; 373 m_is_blocking = false; 374''') 375 code.indent() 376 377 # 378 # After initializing the universal machine parameters, initialize the 379 # this machines config parameters. Also detemine if these configuration 380 # params include a sequencer. This information will be used later for 381 # contecting the sequencer back to the L1 cache controller. 382 # 383 contains_sequencer = False 384 for param in self.config_parameters: 385 if param.name == "sequencer" or param.name == "dma_sequencer": 386 contains_sequencer = True 387 if param.pointer: 388 code('m_${{param.name}}_ptr = p->${{param.name}};') 389 else: 390 code('m_${{param.name}} = p->${{param.name}};') 391 392 # 393 # For the l1 cache controller, add the special atomic support which 394 # includes passing the sequencer a pointer to the controller. 395 # 396 if self.ident == "L1Cache": 397 if not contains_sequencer: 398 self.error("The L1Cache controller must include the sequencer " \ 399 "configuration parameter") 400 401 code(''' 402m_sequencer_ptr->setController(this); 403''') 404 # 405 # For the DMA controller, pass the sequencer a pointer to the 406 # controller. 407 # 408 if self.ident == "DMA": 409 if not contains_sequencer: 410 self.error("The DMA controller must include the sequencer " \ 411 "configuration parameter") 412 413 code(''' 414m_dma_sequencer_ptr->setController(this); 415''') 416 417 code('m_num_controllers++;') 418 for var in self.objects: 419 if var.ident.find("mandatoryQueue") >= 0: 420 code('m_${{var.c_ident}}_ptr = new ${{var.type.c_ident}}();') 421 422 code.dedent() 423 code(''' 424} 425 426void 427$c_ident::init() 428{ 429 MachineType machine_type; 430 int base; 431 432 m_machineID.type = MachineType_${ident}; 433 m_machineID.num = m_version; 434 435 // initialize objects 436 s_profiler.setVersion(m_version); 437 438''') 439 440 code.indent() 441 for var in self.objects: 442 vtype = var.type 443 vid = "m_%s_ptr" % var.c_ident 444 if "network" not in var: 445 # Not a network port object 446 if "primitive" in vtype: 447 code('$vid = new ${{vtype.c_ident}};') 448 if "default" in var: 449 code('(*$vid) = ${{var["default"]}};') 450 else: 451 # Normal Object 452 # added by SS 453 if "factory" in var: 454 code('$vid = ${{var["factory"]}};') 455 elif var.ident.find("mandatoryQueue") < 0: 456 th = var.get("template_hack", "") 457 expr = "%s = new %s%s" % (vid, vtype.c_ident, th) 458 459 args = "" 460 if "non_obj" not in vtype and not vtype.isEnumeration: 461 if expr.find("TBETable") >= 0: 462 args = "m_number_of_TBEs" 463 else: 464 args = var.get("constructor_hack", "") 465 466 code('$expr($args);') 467 468 code('assert($vid != NULL);') 469 470 if "default" in var: 471 code('*$vid = ${{var["default"]}}; // Object default') 472 elif "default" in vtype: 473 comment = "Type %s default" % vtype.ident 474 code('*$vid = ${{vtype["default"]}}; // $comment') 475 476 # Set ordering 477 if "ordered" in var and "trigger_queue" not in var: 478 # A buffer 479 code('$vid->setOrdering(${{var["ordered"]}});') 480 481 # Set randomization 482 if "random" in var: 483 # A buffer 484 code('$vid->setRandomization(${{var["random"]}});') 485 486 # Set Priority 487 if vtype.isBuffer and \ 488 "rank" in var and "trigger_queue" not in var: 489 code('$vid->setPriority(${{var["rank"]}});') 490 else: 491 # Network port object 492 network = var["network"] 493 ordered = var["ordered"] 494 vnet = var["virtual_network"] 495 496 assert var.machine is not None 497 code(''' 498machine_type = string_to_MachineType("${{var.machine.ident}}"); 499base = MachineType_base_number(machine_type); 500$vid = m_net_ptr->get${network}NetQueue(m_version + base, $ordered, $vnet); 501''') 502 503 code('assert($vid != NULL);') 504 505 # Set ordering 506 if "ordered" in var: 507 # A buffer 508 code('$vid->setOrdering(${{var["ordered"]}});') 509 510 # Set randomization 511 if "random" in var: 512 # A buffer 513 code('$vid->setRandomization(${{var["random"]}})') 514 515 # Set Priority 516 if "rank" in var: 517 code('$vid->setPriority(${{var["rank"]}})') 518 519 # Set buffer size 520 if vtype.isBuffer: 521 code(''' 522if (m_buffer_size > 0) { 523 $vid->setSize(m_buffer_size); 524} 525''') 526 527 # set description (may be overriden later by port def) 528 code(''' 529$vid->setDescription("[Version " + int_to_string(m_version) + ", ${ident}, name=${{var.c_ident}}]"); 530 531''') 532 533 # Set the queue consumers 534 code.insert_newline() 535 for port in self.in_ports: 536 code('${{port.code}}.setConsumer(this);') 537 538 # Set the queue descriptions 539 code.insert_newline() 540 for port in self.in_ports: 541 code('${{port.code}}.setDescription("[Version " + int_to_string(m_version) + ", $ident, $port]");') 542 543 # Initialize the transition profiling 544 code.insert_newline() 545 for trans in self.transitions: 546 # Figure out if we stall 547 stall = False 548 for action in trans.actions: 549 if action.ident == "z_stall": 550 stall = True 551 552 # Only possible if it is not a 'z' case 553 if not stall: 554 state = "%s_State_%s" % (self.ident, trans.state.ident) 555 event = "%s_Event_%s" % (self.ident, trans.event.ident) 556 code('s_profiler.possibleTransition($state, $event);') 557 558 # added by SS to initialize recycle_latency of message buffers 559 for buf in self.message_buffer_names: 560 code("$buf->setRecycleLatency(m_recycle_latency);") 561 562 code.dedent() 563 code('}') 564 565 has_mandatory_q = False 566 for port in self.in_ports: 567 if port.code.find("mandatoryQueue_ptr") >= 0: 568 has_mandatory_q = True 569 570 if has_mandatory_q: 571 mq_ident = "m_%s_mandatoryQueue_ptr" % self.ident 572 else: 573 mq_ident = "NULL" 574 575 code(''' 576int 577$c_ident::getNumControllers() 578{ 579 return m_num_controllers; 580} 581 582MessageBuffer* 583$c_ident::getMandatoryQueue() const 584{ 585 return $mq_ident; 586} 587 588const int & 589$c_ident::getVersion() const 590{ 591 return m_version; 592} 593 594const string 595$c_ident::toString() const 596{ 597 return "$c_ident"; 598} 599 600const string 601$c_ident::getName() const 602{ 603 return m_name; 604} 605 606const MachineType 607$c_ident::getMachineType() const 608{ 609 return MachineType_${ident}; 610} 611 612void 613$c_ident::blockOnQueue(Address addr, MessageBuffer* port) 614{ 615 m_is_blocking = true; 616 m_block_map[addr] = port; 617} 618 619void 620$c_ident::unblock(Address addr) 621{ 622 m_block_map.erase(addr); 623 if (m_block_map.size() == 0) { 624 m_is_blocking = false; 625 } 626} 627 628void 629$c_ident::print(ostream& out) const 630{ 631 out << "[$c_ident " << m_version << "]"; 632} 633 634void 635$c_ident::printConfig(ostream& out) const 636{ 637 out << "$c_ident config: " << m_name << endl; 638 out << " version: " << m_version << endl; 639 map<string, string>::const_iterator it; 640 for (it = m_cfg.begin(); it != m_cfg.end(); it++) 641 out << " " << it->first << ": " << it->second << endl; 642} 643 644void 645$c_ident::printStats(ostream& out) const 646{ 647''') 648 # 649 # Cache and Memory Controllers have specific profilers associated with 650 # them. Print out these stats before dumping state transition stats. 651 # 652 for param in self.config_parameters: 653 if param.type_ast.type.ident == "CacheMemory" or \ 654 param.type_ast.type.ident == "MemoryControl": 655 assert(param.pointer) 656 code(' m_${{param.ident}}_ptr->printStats(out);') 657 658 code(''' 659 s_profiler.dumpStats(out); 660} 661 662void $c_ident::clearStats() { 663''') 664 # 665 # Cache and Memory Controllers have specific profilers associated with 666 # them. These stats must be cleared too. 667 # 668 for param in self.config_parameters: 669 if param.type_ast.type.ident == "CacheMemory" or \ 670 param.type_ast.type.ident == "MemoryControl": 671 assert(param.pointer) 672 code(' m_${{param.ident}}_ptr->clearStats();') 673 674 code(''' 675 s_profiler.clearStats(); 676} 677 678// Actions 679''') 680 681 for action in self.actions.itervalues(): 682 if "c_code" not in action: 683 continue 684 685 code(''' 686/** \\brief ${{action.desc}} */ 687void 688$c_ident::${{action.ident}}(const Address& addr) 689{ 690 DEBUG_MSG(GENERATED_COMP, HighPrio, "executing"); 691 ${{action["c_code"]}} 692} 693 694''') 695 code.write(path, "%s.cc" % c_ident) 696 697 def printCWakeup(self, path): 698 '''Output the wakeup loop for the events''' 699 700 code = self.symtab.codeFormatter() 701 ident = self.ident 702 703 code(''' 704// Auto generated C++ code started by $__file__:$__line__ 705// ${ident}: ${{self.short}} 706 707#include "base/misc.hh" 708#include "mem/ruby/common/Global.hh" 709#include "mem/ruby/slicc_interface/RubySlicc_includes.hh" 710#include "mem/protocol/${ident}_Controller.hh" 711#include "mem/protocol/${ident}_State.hh" 712#include "mem/protocol/${ident}_Event.hh" 713#include "mem/protocol/Types.hh" 714#include "mem/ruby/system/System.hh" 715 716void 717${ident}_Controller::wakeup() 718{ 719 // DEBUG_EXPR(GENERATED_COMP, MedPrio, *this); 720 // DEBUG_EXPR(GENERATED_COMP, MedPrio, g_eventQueue_ptr->getTime()); 721 722 int counter = 0; 723 while (true) { 724 // Some cases will put us into an infinite loop without this limit 725 assert(counter <= m_transitions_per_cycle); 726 if (counter == m_transitions_per_cycle) { 727 // Count how often we are fully utilized 728 g_system_ptr->getProfiler()->controllerBusy(m_machineID); 729 730 // Wakeup in another cycle and try again 731 g_eventQueue_ptr->scheduleEvent(this, 1); 732 break; 733 } 734''') 735 736 code.indent() 737 code.indent() 738 739 # InPorts 740 # 741 for port in self.in_ports: 742 code.indent() 743 code('// ${ident}InPort $port') 744 code('${{port["c_code_in_port"]}}') 745 code.dedent() 746 747 code('') 748 749 code.dedent() 750 code.dedent() 751 code(''' 752 break; // If we got this far, we have nothing left todo 753 } 754 // g_eventQueue_ptr->scheduleEvent(this, 1); 755 // DEBUG_NEWLINE(GENERATED_COMP, MedPrio); 756} 757''') 758 759 code.write(path, "%s_Wakeup.cc" % self.ident) 760 761 def printCSwitch(self, path): 762 '''Output switch statement for transition table''' 763 764 code = self.symtab.codeFormatter() 765 ident = self.ident 766 767 code(''' 768// Auto generated C++ code started by $__file__:$__line__ 769// ${ident}: ${{self.short}} 770 771#include "mem/ruby/common/Global.hh" 772#include "mem/protocol/${ident}_Controller.hh" 773#include "mem/protocol/${ident}_State.hh" 774#include "mem/protocol/${ident}_Event.hh" 775#include "mem/protocol/Types.hh" 776#include "mem/ruby/system/System.hh" 777 778#define HASH_FUN(state, event) ((int(state)*${ident}_Event_NUM)+int(event)) 779 780#define GET_TRANSITION_COMMENT() (${ident}_transitionComment.str()) 781#define CLEAR_TRANSITION_COMMENT() (${ident}_transitionComment.str("")) 782 783TransitionResult 784${ident}_Controller::doTransition(${ident}_Event event, 785 ${ident}_State state, 786 const Address &addr) 787{ 788 ${ident}_State next_state = state; 789 790 DEBUG_NEWLINE(GENERATED_COMP, MedPrio); 791 DEBUG_MSG(GENERATED_COMP, MedPrio, *this); 792 DEBUG_EXPR(GENERATED_COMP, MedPrio, g_eventQueue_ptr->getTime()); 793 DEBUG_EXPR(GENERATED_COMP, MedPrio,state); 794 DEBUG_EXPR(GENERATED_COMP, MedPrio,event); 795 DEBUG_EXPR(GENERATED_COMP, MedPrio,addr); 796 797 TransitionResult result = 798 doTransitionWorker(event, state, next_state, addr); 799 800 if (result == TransitionResult_Valid) { 801 DEBUG_EXPR(GENERATED_COMP, MedPrio, next_state); 802 DEBUG_NEWLINE(GENERATED_COMP, MedPrio); 803 s_profiler.countTransition(state, event); 804 if (Debug::getProtocolTrace()) { 805 g_system_ptr->getProfiler()->profileTransition("${ident}", 806 m_version, addr, 807 ${ident}_State_to_string(state), 808 ${ident}_Event_to_string(event), 809 ${ident}_State_to_string(next_state), 810 GET_TRANSITION_COMMENT()); 811 } 812 CLEAR_TRANSITION_COMMENT(); 813 ${ident}_setState(addr, next_state); 814 815 } else if (result == TransitionResult_ResourceStall) { 816 if (Debug::getProtocolTrace()) { 817 g_system_ptr->getProfiler()->profileTransition("${ident}", 818 m_version, addr, 819 ${ident}_State_to_string(state), 820 ${ident}_Event_to_string(event), 821 ${ident}_State_to_string(next_state), 822 "Resource Stall"); 823 } 824 } else if (result == TransitionResult_ProtocolStall) { 825 DEBUG_MSG(GENERATED_COMP, HighPrio, "stalling"); 826 DEBUG_NEWLINE(GENERATED_COMP, MedPrio); 827 if (Debug::getProtocolTrace()) { 828 g_system_ptr->getProfiler()->profileTransition("${ident}", 829 m_version, addr, 830 ${ident}_State_to_string(state), 831 ${ident}_Event_to_string(event), 832 ${ident}_State_to_string(next_state), 833 "Protocol Stall"); 834 } 835 } 836 837 return result; 838} 839 840TransitionResult 841${ident}_Controller::doTransitionWorker(${ident}_Event event, 842 ${ident}_State state, 843 ${ident}_State& next_state, 844 const Address& addr) 845{ 846 switch(HASH_FUN(state, event)) { 847''') 848 849 # This map will allow suppress generating duplicate code 850 cases = orderdict() 851 852 for trans in self.transitions: 853 case_string = "%s_State_%s, %s_Event_%s" % \ 854 (self.ident, trans.state.ident, self.ident, trans.event.ident) 855 856 case = self.symtab.codeFormatter() 857 # Only set next_state if it changes 858 if trans.state != trans.nextState: 859 ns_ident = trans.nextState.ident 860 case('next_state = ${ident}_State_${ns_ident};') 861 862 actions = trans.actions 863 864 # Check for resources 865 case_sorter = [] 866 res = trans.resources 867 for key,val in res.iteritems(): 868 if key.type.ident != "DNUCAStopTable": 869 val = ''' 870if (!%s.areNSlotsAvailable(%s)) 871 return TransitionResult_ResourceStall; 872''' % (key.code, val) 873 case_sorter.append(val) 874 875 876 # Emit the code sequences in a sorted order. This makes the 877 # output deterministic (without this the output order can vary 878 # since Map's keys() on a vector of pointers is not deterministic 879 for c in sorted(case_sorter): 880 case("$c") 881 882 # Figure out if we stall 883 stall = False 884 for action in actions: 885 if action.ident == "z_stall": 886 stall = True 887 break 888 889 if stall: 890 case('return TransitionResult_ProtocolStall;') 891 else: 892 for action in actions: 893 case('${{action.ident}}(addr);') 894 case('return TransitionResult_Valid;') 895 896 case = str(case) 897 898 # Look to see if this transition code is unique. 899 if case not in cases: 900 cases[case] = [] 901 902 cases[case].append(case_string) 903 904 # Walk through all of the unique code blocks and spit out the 905 # corresponding case statement elements 906 for case,transitions in cases.iteritems(): 907 # Iterative over all the multiple transitions that share 908 # the same code 909 for trans in transitions: 910 code(' case HASH_FUN($trans):') 911 code(' $case') 912 913 code(''' 914 default: 915 WARN_EXPR(m_version); 916 WARN_EXPR(g_eventQueue_ptr->getTime()); 917 WARN_EXPR(addr); 918 WARN_EXPR(event); 919 WARN_EXPR(state); 920 ERROR_MSG(\"Invalid transition\"); 921 } 922 return TransitionResult_Valid; 923} 924''') 925 code.write(path, "%s_Transitions.cc" % self.ident) 926 927 def printProfilerHH(self, path): 928 code = self.symtab.codeFormatter() 929 ident = self.ident 930 931 code(''' 932// Auto generated C++ code started by $__file__:$__line__ 933// ${ident}: ${{self.short}} 934 935#ifndef __${ident}_PROFILER_HH_ 936#define __${ident}_PROFILER_HH_ 937 938#include <iostream> 939 940#include "mem/ruby/common/Global.hh" 941#include "mem/protocol/${ident}_State.hh" 942#include "mem/protocol/${ident}_Event.hh" 943 944class ${ident}_Profiler 945{ 946 public: 947 ${ident}_Profiler(); 948 void setVersion(int version); 949 void countTransition(${ident}_State state, ${ident}_Event event); 950 void possibleTransition(${ident}_State state, ${ident}_Event event); 951 void dumpStats(std::ostream& out) const; 952 void clearStats(); 953 954 private: 955 int m_counters[${ident}_State_NUM][${ident}_Event_NUM]; 956 int m_event_counters[${ident}_Event_NUM]; 957 bool m_possible[${ident}_State_NUM][${ident}_Event_NUM]; 958 int m_version; 959}; 960 961#endif // __${ident}_PROFILER_HH__ 962''') 963 code.write(path, "%s_Profiler.hh" % self.ident) 964 965 def printProfilerCC(self, path): 966 code = self.symtab.codeFormatter() 967 ident = self.ident 968 969 code(''' 970// Auto generated C++ code started by $__file__:$__line__ 971// ${ident}: ${{self.short}} 972 973#include "mem/protocol/${ident}_Profiler.hh" 974 975${ident}_Profiler::${ident}_Profiler() 976{ 977 for (int state = 0; state < ${ident}_State_NUM; state++) { 978 for (int event = 0; event < ${ident}_Event_NUM; event++) { 979 m_possible[state][event] = false; 980 m_counters[state][event] = 0; 981 } 982 } 983 for (int event = 0; event < ${ident}_Event_NUM; event++) { 984 m_event_counters[event] = 0; 985 } 986} 987 988void 989${ident}_Profiler::setVersion(int version) 990{ 991 m_version = version; 992} 993 994void 995${ident}_Profiler::clearStats() 996{ 997 for (int state = 0; state < ${ident}_State_NUM; state++) { 998 for (int event = 0; event < ${ident}_Event_NUM; event++) { 999 m_counters[state][event] = 0; 1000 } 1001 } 1002 1003 for (int event = 0; event < ${ident}_Event_NUM; event++) { 1004 m_event_counters[event] = 0; 1005 } 1006} 1007void 1008${ident}_Profiler::countTransition(${ident}_State state, ${ident}_Event event) 1009{ 1010 assert(m_possible[state][event]); 1011 m_counters[state][event]++; 1012 m_event_counters[event]++; 1013} 1014void 1015${ident}_Profiler::possibleTransition(${ident}_State state, 1016 ${ident}_Event event) 1017{ 1018 m_possible[state][event] = true; 1019} 1020 1021void 1022${ident}_Profiler::dumpStats(std::ostream& out) const 1023{ 1024 using namespace std; 1025 1026 out << " --- ${ident} " << m_version << " ---" << endl; 1027 out << " - Event Counts -" << endl; 1028 for (int event = 0; event < ${ident}_Event_NUM; event++) { 1029 int count = m_event_counters[event]; 1030 out << (${ident}_Event) event << " " << count << endl; 1031 } 1032 out << endl; 1033 out << " - Transitions -" << endl; 1034 for (int state = 0; state < ${ident}_State_NUM; state++) { 1035 for (int event = 0; event < ${ident}_Event_NUM; event++) { 1036 if (m_possible[state][event]) { 1037 int count = m_counters[state][event]; 1038 out << (${ident}_State) state << " " 1039 << (${ident}_Event) event << " " << count; 1040 if (count == 0) { 1041 out << " <-- "; 1042 } 1043 out << endl; 1044 } 1045 } 1046 out << endl; 1047 } 1048} 1049''') 1050 code.write(path, "%s_Profiler.cc" % self.ident) 1051 1052 # ************************** 1053 # ******* HTML Files ******* 1054 # ************************** 1055 def frameRef(self, click_href, click_target, over_href, over_num, text): 1056 code = self.symtab.codeFormatter(fix_newlines=False) 1057 code("""<A href=\"$click_href\" target=\"$click_target\" onmouseover=\" 1058 if (parent.frames[$over_num].location != parent.location + '$over_href') { 1059 parent.frames[$over_num].location='$over_href' 1060 }\"> 1061 ${{html.formatShorthand(text)}} 1062 </A>""") 1063 return str(code) 1064 1065 def writeHTMLFiles(self, path): 1066 # Create table with no row hilighted 1067 self.printHTMLTransitions(path, None) 1068 1069 # Generate transition tables 1070 for state in self.states.itervalues(): 1071 self.printHTMLTransitions(path, state) 1072 1073 # Generate action descriptions 1074 for action in self.actions.itervalues(): 1075 name = "%s_action_%s.html" % (self.ident, action.ident) 1076 code = html.createSymbol(action, "Action") 1077 code.write(path, name) 1078 1079 # Generate state descriptions 1080 for state in self.states.itervalues(): 1081 name = "%s_State_%s.html" % (self.ident, state.ident) 1082 code = html.createSymbol(state, "State") 1083 code.write(path, name) 1084 1085 # Generate event descriptions 1086 for event in self.events.itervalues(): 1087 name = "%s_Event_%s.html" % (self.ident, event.ident) 1088 code = html.createSymbol(event, "Event") 1089 code.write(path, name) 1090 1091 def printHTMLTransitions(self, path, active_state): 1092 code = self.symtab.codeFormatter() 1093 1094 code(''' 1095<HTML> 1096<BODY link="blue" vlink="blue"> 1097 1098<H1 align="center">${{html.formatShorthand(self.short)}}: 1099''') 1100 code.indent() 1101 for i,machine in enumerate(self.symtab.getAllType(StateMachine)): 1102 mid = machine.ident 1103 if i != 0: 1104 extra = " - " 1105 else: 1106 extra = "" 1107 if machine == self: 1108 code('$extra$mid') 1109 else: 1110 code('$extra<A target="Table" href="${mid}_table.html">$mid</A>') 1111 code.dedent() 1112 1113 code(""" 1114</H1> 1115 1116<TABLE border=1> 1117<TR> 1118 <TH> </TH> 1119""") 1120 1121 for event in self.events.itervalues(): 1122 href = "%s_Event_%s.html" % (self.ident, event.ident) 1123 ref = self.frameRef(href, "Status", href, "1", event.short) 1124 code('<TH bgcolor=white>$ref</TH>') 1125 1126 code('</TR>') 1127 # -- Body of table 1128 for state in self.states.itervalues(): 1129 # -- Each row 1130 if state == active_state: 1131 color = "yellow" 1132 else: 1133 color = "white" 1134 1135 click = "%s_table_%s.html" % (self.ident, state.ident) 1136 over = "%s_State_%s.html" % (self.ident, state.ident) 1137 text = html.formatShorthand(state.short) 1138 ref = self.frameRef(click, "Table", over, "1", state.short) 1139 code(''' 1140<TR> 1141 <TH bgcolor=$color>$ref</TH> 1142''') 1143 1144 # -- One column for each event 1145 for event in self.events.itervalues(): 1146 trans = self.table.get((state,event), None) 1147 if trans is None: 1148 # This is the no transition case 1149 if state == active_state: 1150 color = "#C0C000" 1151 else: 1152 color = "lightgrey" 1153 1154 code('<TD bgcolor=$color> </TD>') 1155 continue 1156 1157 next = trans.nextState 1158 stall_action = False 1159 1160 # -- Get the actions 1161 for action in trans.actions: 1162 if action.ident == "z_stall" or \ 1163 action.ident == "zz_recycleMandatoryQueue": 1164 stall_action = True 1165 1166 # -- Print out "actions/next-state" 1167 if stall_action: 1168 if state == active_state: 1169 color = "#C0C000" 1170 else: 1171 color = "lightgrey" 1172 1173 elif active_state and next.ident == active_state.ident: 1174 color = "aqua" 1175 elif state == active_state: 1176 color = "yellow" 1177 else: 1178 color = "white" 1179 1180 code('<TD bgcolor=$color>') 1181 for action in trans.actions: 1182 href = "%s_action_%s.html" % (self.ident, action.ident) 1183 ref = self.frameRef(href, "Status", href, "1", 1184 action.short) 1185 code(' $ref') 1186 if next != state: 1187 if trans.actions: 1188 code('/') 1189 click = "%s_table_%s.html" % (self.ident, next.ident) 1190 over = "%s_State_%s.html" % (self.ident, next.ident) 1191 ref = self.frameRef(click, "Table", over, "1", next.short) 1192 code("$ref") 1193 code("</TD>") 1194 1195 # -- Each row 1196 if state == active_state: 1197 color = "yellow" 1198 else: 1199 color = "white" 1200 1201 click = "%s_table_%s.html" % (self.ident, state.ident) 1202 over = "%s_State_%s.html" % (self.ident, state.ident) 1203 ref = self.frameRef(click, "Table", over, "1", state.short) 1204 code(''' 1205 <TH bgcolor=$color>$ref</TH> 1206</TR> 1207''') 1208 code(''' 1209<!- Column footer-> 1210<TR> 1211 <TH> </TH> 1212''') 1213 1214 for event in self.events.itervalues(): 1215 href = "%s_Event_%s.html" % (self.ident, event.ident) 1216 ref = self.frameRef(href, "Status", href, "1", event.short) 1217 code('<TH bgcolor=white>$ref</TH>') 1218 code(''' 1219</TR> 1220</TABLE> 1221</BODY></HTML> 1222''') 1223 1224 1225 if active_state: 1226 name = "%s_table_%s.html" % (self.ident, active_state.ident) 1227 else: 1228 name = "%s_table.html" % self.ident 1229 code.write(path, name) 1230 1231__all__ = [ "StateMachine" ] 1232