StateMachine.py revision 7055:4e24742201d7
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 "std::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; 274std::map<std::string, std::string> m_cfg; 275NodeID m_version; 276Network* m_net_ptr; 277MachineID m_machineID; 278bool m_is_blocking; 279std::map<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 == "DirectoryMemory" or \ 655 param.type_ast.type.ident == "MemoryControl": 656 assert(param.pointer) 657 code(' m_${{param.ident}}_ptr->printStats(out);') 658 659 code(''' 660 s_profiler.dumpStats(out); 661} 662 663void $c_ident::clearStats() { 664''') 665 # 666 # Cache and Memory Controllers have specific profilers associated with 667 # them. These stats must be cleared too. 668 # 669 for param in self.config_parameters: 670 if param.type_ast.type.ident == "CacheMemory" or \ 671 param.type_ast.type.ident == "MemoryControl": 672 assert(param.pointer) 673 code(' m_${{param.ident}}_ptr->clearStats();') 674 675 code(''' 676 s_profiler.clearStats(); 677} 678 679// Actions 680''') 681 682 for action in self.actions.itervalues(): 683 if "c_code" not in action: 684 continue 685 686 code(''' 687/** \\brief ${{action.desc}} */ 688void 689$c_ident::${{action.ident}}(const Address& addr) 690{ 691 DEBUG_MSG(GENERATED_COMP, HighPrio, "executing"); 692 ${{action["c_code"]}} 693} 694 695''') 696 code.write(path, "%s.cc" % c_ident) 697 698 def printCWakeup(self, path): 699 '''Output the wakeup loop for the events''' 700 701 code = self.symtab.codeFormatter() 702 ident = self.ident 703 704 code(''' 705// Auto generated C++ code started by $__file__:$__line__ 706// ${ident}: ${{self.short}} 707 708#include "base/misc.hh" 709#include "mem/ruby/common/Global.hh" 710#include "mem/ruby/slicc_interface/RubySlicc_includes.hh" 711#include "mem/protocol/${ident}_Controller.hh" 712#include "mem/protocol/${ident}_State.hh" 713#include "mem/protocol/${ident}_Event.hh" 714#include "mem/protocol/Types.hh" 715#include "mem/ruby/system/System.hh" 716 717using namespace std; 718 719void 720${ident}_Controller::wakeup() 721{ 722 // DEBUG_EXPR(GENERATED_COMP, MedPrio, *this); 723 // DEBUG_EXPR(GENERATED_COMP, MedPrio, g_eventQueue_ptr->getTime()); 724 725 int counter = 0; 726 while (true) { 727 // Some cases will put us into an infinite loop without this limit 728 assert(counter <= m_transitions_per_cycle); 729 if (counter == m_transitions_per_cycle) { 730 // Count how often we are fully utilized 731 g_system_ptr->getProfiler()->controllerBusy(m_machineID); 732 733 // Wakeup in another cycle and try again 734 g_eventQueue_ptr->scheduleEvent(this, 1); 735 break; 736 } 737''') 738 739 code.indent() 740 code.indent() 741 742 # InPorts 743 # 744 for port in self.in_ports: 745 code.indent() 746 code('// ${ident}InPort $port') 747 code('${{port["c_code_in_port"]}}') 748 code.dedent() 749 750 code('') 751 752 code.dedent() 753 code.dedent() 754 code(''' 755 break; // If we got this far, we have nothing left todo 756 } 757 // g_eventQueue_ptr->scheduleEvent(this, 1); 758 // DEBUG_NEWLINE(GENERATED_COMP, MedPrio); 759} 760''') 761 762 code.write(path, "%s_Wakeup.cc" % self.ident) 763 764 def printCSwitch(self, path): 765 '''Output switch statement for transition table''' 766 767 code = self.symtab.codeFormatter() 768 ident = self.ident 769 770 code(''' 771// Auto generated C++ code started by $__file__:$__line__ 772// ${ident}: ${{self.short}} 773 774#include "mem/ruby/common/Global.hh" 775#include "mem/protocol/${ident}_Controller.hh" 776#include "mem/protocol/${ident}_State.hh" 777#include "mem/protocol/${ident}_Event.hh" 778#include "mem/protocol/Types.hh" 779#include "mem/ruby/system/System.hh" 780 781#define HASH_FUN(state, event) ((int(state)*${ident}_Event_NUM)+int(event)) 782 783#define GET_TRANSITION_COMMENT() (${ident}_transitionComment.str()) 784#define CLEAR_TRANSITION_COMMENT() (${ident}_transitionComment.str("")) 785 786TransitionResult 787${ident}_Controller::doTransition(${ident}_Event event, 788 ${ident}_State state, 789 const Address &addr) 790{ 791 ${ident}_State next_state = state; 792 793 DEBUG_NEWLINE(GENERATED_COMP, MedPrio); 794 DEBUG_MSG(GENERATED_COMP, MedPrio, *this); 795 DEBUG_EXPR(GENERATED_COMP, MedPrio, g_eventQueue_ptr->getTime()); 796 DEBUG_EXPR(GENERATED_COMP, MedPrio,state); 797 DEBUG_EXPR(GENERATED_COMP, MedPrio,event); 798 DEBUG_EXPR(GENERATED_COMP, MedPrio,addr); 799 800 TransitionResult result = 801 doTransitionWorker(event, state, next_state, addr); 802 803 if (result == TransitionResult_Valid) { 804 DEBUG_EXPR(GENERATED_COMP, MedPrio, next_state); 805 DEBUG_NEWLINE(GENERATED_COMP, MedPrio); 806 s_profiler.countTransition(state, event); 807 if (Debug::getProtocolTrace()) { 808 g_system_ptr->getProfiler()->profileTransition("${ident}", 809 m_version, addr, 810 ${ident}_State_to_string(state), 811 ${ident}_Event_to_string(event), 812 ${ident}_State_to_string(next_state), 813 GET_TRANSITION_COMMENT()); 814 } 815 CLEAR_TRANSITION_COMMENT(); 816 ${ident}_setState(addr, next_state); 817 818 } else if (result == TransitionResult_ResourceStall) { 819 if (Debug::getProtocolTrace()) { 820 g_system_ptr->getProfiler()->profileTransition("${ident}", 821 m_version, addr, 822 ${ident}_State_to_string(state), 823 ${ident}_Event_to_string(event), 824 ${ident}_State_to_string(next_state), 825 "Resource Stall"); 826 } 827 } else if (result == TransitionResult_ProtocolStall) { 828 DEBUG_MSG(GENERATED_COMP, HighPrio, "stalling"); 829 DEBUG_NEWLINE(GENERATED_COMP, MedPrio); 830 if (Debug::getProtocolTrace()) { 831 g_system_ptr->getProfiler()->profileTransition("${ident}", 832 m_version, addr, 833 ${ident}_State_to_string(state), 834 ${ident}_Event_to_string(event), 835 ${ident}_State_to_string(next_state), 836 "Protocol Stall"); 837 } 838 } 839 840 return result; 841} 842 843TransitionResult 844${ident}_Controller::doTransitionWorker(${ident}_Event event, 845 ${ident}_State state, 846 ${ident}_State& next_state, 847 const Address& addr) 848{ 849 switch(HASH_FUN(state, event)) { 850''') 851 852 # This map will allow suppress generating duplicate code 853 cases = orderdict() 854 855 for trans in self.transitions: 856 case_string = "%s_State_%s, %s_Event_%s" % \ 857 (self.ident, trans.state.ident, self.ident, trans.event.ident) 858 859 case = self.symtab.codeFormatter() 860 # Only set next_state if it changes 861 if trans.state != trans.nextState: 862 ns_ident = trans.nextState.ident 863 case('next_state = ${ident}_State_${ns_ident};') 864 865 actions = trans.actions 866 867 # Check for resources 868 case_sorter = [] 869 res = trans.resources 870 for key,val in res.iteritems(): 871 if key.type.ident != "DNUCAStopTable": 872 val = ''' 873if (!%s.areNSlotsAvailable(%s)) 874 return TransitionResult_ResourceStall; 875''' % (key.code, val) 876 case_sorter.append(val) 877 878 879 # Emit the code sequences in a sorted order. This makes the 880 # output deterministic (without this the output order can vary 881 # since Map's keys() on a vector of pointers is not deterministic 882 for c in sorted(case_sorter): 883 case("$c") 884 885 # Figure out if we stall 886 stall = False 887 for action in actions: 888 if action.ident == "z_stall": 889 stall = True 890 break 891 892 if stall: 893 case('return TransitionResult_ProtocolStall;') 894 else: 895 for action in actions: 896 case('${{action.ident}}(addr);') 897 case('return TransitionResult_Valid;') 898 899 case = str(case) 900 901 # Look to see if this transition code is unique. 902 if case not in cases: 903 cases[case] = [] 904 905 cases[case].append(case_string) 906 907 # Walk through all of the unique code blocks and spit out the 908 # corresponding case statement elements 909 for case,transitions in cases.iteritems(): 910 # Iterative over all the multiple transitions that share 911 # the same code 912 for trans in transitions: 913 code(' case HASH_FUN($trans):') 914 code(' $case') 915 916 code(''' 917 default: 918 WARN_EXPR(m_version); 919 WARN_EXPR(g_eventQueue_ptr->getTime()); 920 WARN_EXPR(addr); 921 WARN_EXPR(event); 922 WARN_EXPR(state); 923 ERROR_MSG(\"Invalid transition\"); 924 } 925 return TransitionResult_Valid; 926} 927''') 928 code.write(path, "%s_Transitions.cc" % self.ident) 929 930 def printProfilerHH(self, path): 931 code = self.symtab.codeFormatter() 932 ident = self.ident 933 934 code(''' 935// Auto generated C++ code started by $__file__:$__line__ 936// ${ident}: ${{self.short}} 937 938#ifndef __${ident}_PROFILER_HH_ 939#define __${ident}_PROFILER_HH_ 940 941#include <iostream> 942 943#include "mem/ruby/common/Global.hh" 944#include "mem/protocol/${ident}_State.hh" 945#include "mem/protocol/${ident}_Event.hh" 946 947class ${ident}_Profiler 948{ 949 public: 950 ${ident}_Profiler(); 951 void setVersion(int version); 952 void countTransition(${ident}_State state, ${ident}_Event event); 953 void possibleTransition(${ident}_State state, ${ident}_Event event); 954 void dumpStats(std::ostream& out) const; 955 void clearStats(); 956 957 private: 958 int m_counters[${ident}_State_NUM][${ident}_Event_NUM]; 959 int m_event_counters[${ident}_Event_NUM]; 960 bool m_possible[${ident}_State_NUM][${ident}_Event_NUM]; 961 int m_version; 962}; 963 964#endif // __${ident}_PROFILER_HH__ 965''') 966 code.write(path, "%s_Profiler.hh" % self.ident) 967 968 def printProfilerCC(self, path): 969 code = self.symtab.codeFormatter() 970 ident = self.ident 971 972 code(''' 973// Auto generated C++ code started by $__file__:$__line__ 974// ${ident}: ${{self.short}} 975 976#include "mem/protocol/${ident}_Profiler.hh" 977 978${ident}_Profiler::${ident}_Profiler() 979{ 980 for (int state = 0; state < ${ident}_State_NUM; state++) { 981 for (int event = 0; event < ${ident}_Event_NUM; event++) { 982 m_possible[state][event] = false; 983 m_counters[state][event] = 0; 984 } 985 } 986 for (int event = 0; event < ${ident}_Event_NUM; event++) { 987 m_event_counters[event] = 0; 988 } 989} 990 991void 992${ident}_Profiler::setVersion(int version) 993{ 994 m_version = version; 995} 996 997void 998${ident}_Profiler::clearStats() 999{ 1000 for (int state = 0; state < ${ident}_State_NUM; state++) { 1001 for (int event = 0; event < ${ident}_Event_NUM; event++) { 1002 m_counters[state][event] = 0; 1003 } 1004 } 1005 1006 for (int event = 0; event < ${ident}_Event_NUM; event++) { 1007 m_event_counters[event] = 0; 1008 } 1009} 1010void 1011${ident}_Profiler::countTransition(${ident}_State state, ${ident}_Event event) 1012{ 1013 assert(m_possible[state][event]); 1014 m_counters[state][event]++; 1015 m_event_counters[event]++; 1016} 1017void 1018${ident}_Profiler::possibleTransition(${ident}_State state, 1019 ${ident}_Event event) 1020{ 1021 m_possible[state][event] = true; 1022} 1023 1024void 1025${ident}_Profiler::dumpStats(std::ostream& out) const 1026{ 1027 using namespace std; 1028 1029 out << " --- ${ident} " << m_version << " ---" << endl; 1030 out << " - Event Counts -" << endl; 1031 for (int event = 0; event < ${ident}_Event_NUM; event++) { 1032 int count = m_event_counters[event]; 1033 out << (${ident}_Event) event << " " << count << endl; 1034 } 1035 out << endl; 1036 out << " - Transitions -" << endl; 1037 for (int state = 0; state < ${ident}_State_NUM; state++) { 1038 for (int event = 0; event < ${ident}_Event_NUM; event++) { 1039 if (m_possible[state][event]) { 1040 int count = m_counters[state][event]; 1041 out << (${ident}_State) state << " " 1042 << (${ident}_Event) event << " " << count; 1043 if (count == 0) { 1044 out << " <-- "; 1045 } 1046 out << endl; 1047 } 1048 } 1049 out << endl; 1050 } 1051} 1052''') 1053 code.write(path, "%s_Profiler.cc" % self.ident) 1054 1055 # ************************** 1056 # ******* HTML Files ******* 1057 # ************************** 1058 def frameRef(self, click_href, click_target, over_href, over_num, text): 1059 code = self.symtab.codeFormatter(fix_newlines=False) 1060 code("""<A href=\"$click_href\" target=\"$click_target\" onmouseover=\" 1061 if (parent.frames[$over_num].location != parent.location + '$over_href') { 1062 parent.frames[$over_num].location='$over_href' 1063 }\"> 1064 ${{html.formatShorthand(text)}} 1065 </A>""") 1066 return str(code) 1067 1068 def writeHTMLFiles(self, path): 1069 # Create table with no row hilighted 1070 self.printHTMLTransitions(path, None) 1071 1072 # Generate transition tables 1073 for state in self.states.itervalues(): 1074 self.printHTMLTransitions(path, state) 1075 1076 # Generate action descriptions 1077 for action in self.actions.itervalues(): 1078 name = "%s_action_%s.html" % (self.ident, action.ident) 1079 code = html.createSymbol(action, "Action") 1080 code.write(path, name) 1081 1082 # Generate state descriptions 1083 for state in self.states.itervalues(): 1084 name = "%s_State_%s.html" % (self.ident, state.ident) 1085 code = html.createSymbol(state, "State") 1086 code.write(path, name) 1087 1088 # Generate event descriptions 1089 for event in self.events.itervalues(): 1090 name = "%s_Event_%s.html" % (self.ident, event.ident) 1091 code = html.createSymbol(event, "Event") 1092 code.write(path, name) 1093 1094 def printHTMLTransitions(self, path, active_state): 1095 code = self.symtab.codeFormatter() 1096 1097 code(''' 1098<HTML> 1099<BODY link="blue" vlink="blue"> 1100 1101<H1 align="center">${{html.formatShorthand(self.short)}}: 1102''') 1103 code.indent() 1104 for i,machine in enumerate(self.symtab.getAllType(StateMachine)): 1105 mid = machine.ident 1106 if i != 0: 1107 extra = " - " 1108 else: 1109 extra = "" 1110 if machine == self: 1111 code('$extra$mid') 1112 else: 1113 code('$extra<A target="Table" href="${mid}_table.html">$mid</A>') 1114 code.dedent() 1115 1116 code(""" 1117</H1> 1118 1119<TABLE border=1> 1120<TR> 1121 <TH> </TH> 1122""") 1123 1124 for event in self.events.itervalues(): 1125 href = "%s_Event_%s.html" % (self.ident, event.ident) 1126 ref = self.frameRef(href, "Status", href, "1", event.short) 1127 code('<TH bgcolor=white>$ref</TH>') 1128 1129 code('</TR>') 1130 # -- Body of table 1131 for state in self.states.itervalues(): 1132 # -- Each row 1133 if state == active_state: 1134 color = "yellow" 1135 else: 1136 color = "white" 1137 1138 click = "%s_table_%s.html" % (self.ident, state.ident) 1139 over = "%s_State_%s.html" % (self.ident, state.ident) 1140 text = html.formatShorthand(state.short) 1141 ref = self.frameRef(click, "Table", over, "1", state.short) 1142 code(''' 1143<TR> 1144 <TH bgcolor=$color>$ref</TH> 1145''') 1146 1147 # -- One column for each event 1148 for event in self.events.itervalues(): 1149 trans = self.table.get((state,event), None) 1150 if trans is None: 1151 # This is the no transition case 1152 if state == active_state: 1153 color = "#C0C000" 1154 else: 1155 color = "lightgrey" 1156 1157 code('<TD bgcolor=$color> </TD>') 1158 continue 1159 1160 next = trans.nextState 1161 stall_action = False 1162 1163 # -- Get the actions 1164 for action in trans.actions: 1165 if action.ident == "z_stall" or \ 1166 action.ident == "zz_recycleMandatoryQueue": 1167 stall_action = True 1168 1169 # -- Print out "actions/next-state" 1170 if stall_action: 1171 if state == active_state: 1172 color = "#C0C000" 1173 else: 1174 color = "lightgrey" 1175 1176 elif active_state and next.ident == active_state.ident: 1177 color = "aqua" 1178 elif state == active_state: 1179 color = "yellow" 1180 else: 1181 color = "white" 1182 1183 code('<TD bgcolor=$color>') 1184 for action in trans.actions: 1185 href = "%s_action_%s.html" % (self.ident, action.ident) 1186 ref = self.frameRef(href, "Status", href, "1", 1187 action.short) 1188 code(' $ref') 1189 if next != state: 1190 if trans.actions: 1191 code('/') 1192 click = "%s_table_%s.html" % (self.ident, next.ident) 1193 over = "%s_State_%s.html" % (self.ident, next.ident) 1194 ref = self.frameRef(click, "Table", over, "1", next.short) 1195 code("$ref") 1196 code("</TD>") 1197 1198 # -- Each row 1199 if state == active_state: 1200 color = "yellow" 1201 else: 1202 color = "white" 1203 1204 click = "%s_table_%s.html" % (self.ident, state.ident) 1205 over = "%s_State_%s.html" % (self.ident, state.ident) 1206 ref = self.frameRef(click, "Table", over, "1", state.short) 1207 code(''' 1208 <TH bgcolor=$color>$ref</TH> 1209</TR> 1210''') 1211 code(''' 1212<!- Column footer-> 1213<TR> 1214 <TH> </TH> 1215''') 1216 1217 for event in self.events.itervalues(): 1218 href = "%s_Event_%s.html" % (self.ident, event.ident) 1219 ref = self.frameRef(href, "Status", href, "1", event.short) 1220 code('<TH bgcolor=white>$ref</TH>') 1221 code(''' 1222</TR> 1223</TABLE> 1224</BODY></HTML> 1225''') 1226 1227 1228 if active_state: 1229 name = "%s_table_%s.html" % (self.ident, active_state.ident) 1230 else: 1231 name = "%s_table.html" % self.ident 1232 code.write(path, name) 1233 1234__all__ = [ "StateMachine" ] 1235