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