StateMachine.py revision 7566
16145Snate@binkert.org# Copyright (c) 1999-2008 Mark D. Hill and David A. Wood 26145Snate@binkert.org# Copyright (c) 2009 The Hewlett-Packard Development Company 36145Snate@binkert.org# All rights reserved. 46145Snate@binkert.org# 56145Snate@binkert.org# Redistribution and use in source and binary forms, with or without 66145Snate@binkert.org# modification, are permitted provided that the following conditions are 76145Snate@binkert.org# met: redistributions of source code must retain the above copyright 86145Snate@binkert.org# notice, this list of conditions and the following disclaimer; 96145Snate@binkert.org# redistributions in binary form must reproduce the above copyright 106145Snate@binkert.org# notice, this list of conditions and the following disclaimer in the 116145Snate@binkert.org# documentation and/or other materials provided with the distribution; 126145Snate@binkert.org# neither the name of the copyright holders nor the names of its 136145Snate@binkert.org# contributors may be used to endorse or promote products derived from 146145Snate@binkert.org# this software without specific prior written permission. 156145Snate@binkert.org# 166145Snate@binkert.org# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 176145Snate@binkert.org# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 186145Snate@binkert.org# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 196145Snate@binkert.org# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 206145Snate@binkert.org# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 216145Snate@binkert.org# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 226145Snate@binkert.org# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 236145Snate@binkert.org# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 246145Snate@binkert.org# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 256145Snate@binkert.org# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 266145Snate@binkert.org# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 276145Snate@binkert.org 286145Snate@binkert.orgfrom m5.util import orderdict 296145Snate@binkert.org 306145Snate@binkert.orgfrom slicc.symbols.Symbol import Symbol 316145Snate@binkert.orgfrom slicc.symbols.Var import Var 326145Snate@binkert.orgimport slicc.generate.html as html 336145Snate@binkert.org 346145Snate@binkert.orgpython_class_map = {"int": "Int", 356145Snate@binkert.org "std::string": "String", 366145Snate@binkert.org "bool": "Bool", 376145Snate@binkert.org "CacheMemory": "RubyCache", 386145Snate@binkert.org "Sequencer": "RubySequencer", 396145Snate@binkert.org "DirectoryMemory": "RubyDirectoryMemory", 406145Snate@binkert.org "MemoryControl": "RubyMemoryControl", 416145Snate@binkert.org "DMASequencer": "DMASequencer" 426145Snate@binkert.org } 436145Snate@binkert.org 446145Snate@binkert.orgclass StateMachine(Symbol): 456145Snate@binkert.org 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 498 else: 499 # Network port object 500 network = var["network"] 501 ordered = var["ordered"] 502 vnet = var["virtual_network"] 503 504 assert var.machine is not None 505 code(''' 506machine_type = string_to_MachineType("${{var.machine.ident}}"); 507base = MachineType_base_number(machine_type); 508$vid = m_net_ptr->get${network}NetQueue(m_version + base, $ordered, $vnet); 509''') 510 511 code('assert($vid != NULL);') 512 513 # Set ordering 514 if "ordered" in var: 515 # A buffer 516 code('$vid->setOrdering(${{var["ordered"]}});') 517 518 # Set randomization 519 if "random" in var: 520 # A buffer 521 code('$vid->setRandomization(${{var["random"]}})') 522 523 # Set Priority 524 if "rank" in var: 525 code('$vid->setPriority(${{var["rank"]}})') 526 527 # Set buffer size 528 if vtype.isBuffer: 529 code(''' 530if (m_buffer_size > 0) { 531 $vid->resize(m_buffer_size); 532} 533''') 534 535 # set description (may be overriden later by port def) 536 code(''' 537$vid->setDescription("[Version " + to_string(m_version) + ", ${ident}, name=${{var.c_ident}}]"); 538 539''') 540 541 if vtype.isBuffer: 542 if "recycle_latency" in var: 543 code('$vid->setRecycleLatency(${{var["recycle_latency"]}});') 544 else: 545 code('$vid->setRecycleLatency(m_recycle_latency);') 546 547 548 # Set the queue consumers 549 code.insert_newline() 550 for port in self.in_ports: 551 code('${{port.code}}.setConsumer(this);') 552 553 # Set the queue descriptions 554 code.insert_newline() 555 for port in self.in_ports: 556 code('${{port.code}}.setDescription("[Version " + to_string(m_version) + ", $ident, $port]");') 557 558 # Initialize the transition profiling 559 code.insert_newline() 560 for trans in self.transitions: 561 # Figure out if we stall 562 stall = False 563 for action in trans.actions: 564 if action.ident == "z_stall": 565 stall = True 566 567 # Only possible if it is not a 'z' case 568 if not stall: 569 state = "%s_State_%s" % (self.ident, trans.state.ident) 570 event = "%s_Event_%s" % (self.ident, trans.event.ident) 571 code('m_profiler.possibleTransition($state, $event);') 572 573 code.dedent() 574 code('}') 575 576 has_mandatory_q = False 577 for port in self.in_ports: 578 if port.code.find("mandatoryQueue_ptr") >= 0: 579 has_mandatory_q = True 580 581 if has_mandatory_q: 582 mq_ident = "m_%s_mandatoryQueue_ptr" % self.ident 583 else: 584 mq_ident = "NULL" 585 586 code(''' 587int 588$c_ident::getNumControllers() 589{ 590 return m_num_controllers; 591} 592 593MessageBuffer* 594$c_ident::getMandatoryQueue() const 595{ 596 return $mq_ident; 597} 598 599const int & 600$c_ident::getVersion() const 601{ 602 return m_version; 603} 604 605const string 606$c_ident::toString() const 607{ 608 return "$c_ident"; 609} 610 611const string 612$c_ident::getName() const 613{ 614 return m_name; 615} 616 617const MachineType 618$c_ident::getMachineType() const 619{ 620 return MachineType_${ident}; 621} 622 623void 624$c_ident::blockOnQueue(Address addr, MessageBuffer* port) 625{ 626 m_is_blocking = true; 627 m_block_map[addr] = port; 628} 629 630void 631$c_ident::unblock(Address addr) 632{ 633 m_block_map.erase(addr); 634 if (m_block_map.size() == 0) { 635 m_is_blocking = false; 636 } 637} 638 639void 640$c_ident::print(ostream& out) const 641{ 642 out << "[$c_ident " << m_version << "]"; 643} 644 645void 646$c_ident::printConfig(ostream& out) const 647{ 648 out << "$c_ident config: " << m_name << endl; 649 out << " version: " << m_version << endl; 650 map<string, string>::const_iterator it; 651 for (it = m_cfg.begin(); it != m_cfg.end(); it++) 652 out << " " << it->first << ": " << it->second << endl; 653} 654 655void 656$c_ident::printStats(ostream& out) const 657{ 658''') 659 # 660 # Cache and Memory Controllers have specific profilers associated with 661 # them. Print out these stats before dumping state transition stats. 662 # 663 for param in self.config_parameters: 664 if param.type_ast.type.ident == "CacheMemory" or \ 665 param.type_ast.type.ident == "DirectoryMemory" or \ 666 param.type_ast.type.ident == "MemoryControl": 667 assert(param.pointer) 668 code(' m_${{param.ident}}_ptr->printStats(out);') 669 670 code(''' 671 if (m_version == 0) { 672 s_profileDumper.dumpStats(out); 673 } 674} 675 676void $c_ident::clearStats() { 677''') 678 # 679 # Cache and Memory Controllers have specific profilers associated with 680 # them. These stats must be cleared too. 681 # 682 for param in self.config_parameters: 683 if param.type_ast.type.ident == "CacheMemory" or \ 684 param.type_ast.type.ident == "MemoryControl": 685 assert(param.pointer) 686 code(' m_${{param.ident}}_ptr->clearStats();') 687 688 code(''' 689 m_profiler.clearStats(); 690} 691 692// Actions 693''') 694 695 for action in self.actions.itervalues(): 696 if "c_code" not in action: 697 continue 698 699 code(''' 700/** \\brief ${{action.desc}} */ 701void 702$c_ident::${{action.ident}}(const Address& addr) 703{ 704 DEBUG_MSG(GENERATED_COMP, HighPrio, "executing"); 705 ${{action["c_code"]}} 706} 707 708''') 709 code.write(path, "%s.cc" % c_ident) 710 711 def printCWakeup(self, path): 712 '''Output the wakeup loop for the events''' 713 714 code = self.symtab.codeFormatter() 715 ident = self.ident 716 717 code(''' 718// Auto generated C++ code started by $__file__:$__line__ 719// ${ident}: ${{self.short}} 720 721#include "base/misc.hh" 722#include "mem/ruby/common/Global.hh" 723#include "mem/ruby/slicc_interface/RubySlicc_includes.hh" 724#include "mem/protocol/${ident}_Controller.hh" 725#include "mem/protocol/${ident}_State.hh" 726#include "mem/protocol/${ident}_Event.hh" 727#include "mem/protocol/Types.hh" 728#include "mem/ruby/system/System.hh" 729 730using namespace std; 731 732void 733${ident}_Controller::wakeup() 734{ 735 // DEBUG_EXPR(GENERATED_COMP, MedPrio, *this); 736 // DEBUG_EXPR(GENERATED_COMP, MedPrio, g_eventQueue_ptr->getTime()); 737 738 int counter = 0; 739 while (true) { 740 // Some cases will put us into an infinite loop without this limit 741 assert(counter <= m_transitions_per_cycle); 742 if (counter == m_transitions_per_cycle) { 743 // Count how often we are fully utilized 744 g_system_ptr->getProfiler()->controllerBusy(m_machineID); 745 746 // Wakeup in another cycle and try again 747 g_eventQueue_ptr->scheduleEvent(this, 1); 748 break; 749 } 750''') 751 752 code.indent() 753 code.indent() 754 755 # InPorts 756 # 757 for port in self.in_ports: 758 code.indent() 759 code('// ${ident}InPort $port') 760 code('${{port["c_code_in_port"]}}') 761 code.dedent() 762 763 code('') 764 765 code.dedent() 766 code.dedent() 767 code(''' 768 break; // If we got this far, we have nothing left todo 769 } 770 // g_eventQueue_ptr->scheduleEvent(this, 1); 771 // DEBUG_NEWLINE(GENERATED_COMP, MedPrio); 772} 773''') 774 775 code.write(path, "%s_Wakeup.cc" % self.ident) 776 777 def printCSwitch(self, path): 778 '''Output switch statement for transition table''' 779 780 code = self.symtab.codeFormatter() 781 ident = self.ident 782 783 code(''' 784// Auto generated C++ code started by $__file__:$__line__ 785// ${ident}: ${{self.short}} 786 787#include "mem/ruby/common/Global.hh" 788#include "mem/protocol/${ident}_Controller.hh" 789#include "mem/protocol/${ident}_State.hh" 790#include "mem/protocol/${ident}_Event.hh" 791#include "mem/protocol/Types.hh" 792#include "mem/ruby/system/System.hh" 793 794#define HASH_FUN(state, event) ((int(state)*${ident}_Event_NUM)+int(event)) 795 796#define GET_TRANSITION_COMMENT() (${ident}_transitionComment.str()) 797#define CLEAR_TRANSITION_COMMENT() (${ident}_transitionComment.str("")) 798 799TransitionResult 800${ident}_Controller::doTransition(${ident}_Event event, 801 ${ident}_State state, 802 const Address &addr) 803{ 804 ${ident}_State next_state = state; 805 806 DEBUG_NEWLINE(GENERATED_COMP, MedPrio); 807 DEBUG_MSG(GENERATED_COMP, MedPrio, *this); 808 DEBUG_EXPR(GENERATED_COMP, MedPrio, g_eventQueue_ptr->getTime()); 809 DEBUG_EXPR(GENERATED_COMP, MedPrio,state); 810 DEBUG_EXPR(GENERATED_COMP, MedPrio,event); 811 DEBUG_EXPR(GENERATED_COMP, MedPrio,addr); 812 813 TransitionResult result = 814 doTransitionWorker(event, state, next_state, addr); 815 816 if (result == TransitionResult_Valid) { 817 DEBUG_EXPR(GENERATED_COMP, MedPrio, next_state); 818 DEBUG_NEWLINE(GENERATED_COMP, MedPrio); 819 m_profiler.countTransition(state, event); 820 if (Debug::getProtocolTrace()) { 821 g_system_ptr->getProfiler()->profileTransition("${ident}", 822 m_version, addr, 823 ${ident}_State_to_string(state), 824 ${ident}_Event_to_string(event), 825 ${ident}_State_to_string(next_state), 826 GET_TRANSITION_COMMENT()); 827 } 828 CLEAR_TRANSITION_COMMENT(); 829 ${ident}_setState(addr, next_state); 830 831 } else if (result == TransitionResult_ResourceStall) { 832 if (Debug::getProtocolTrace()) { 833 g_system_ptr->getProfiler()->profileTransition("${ident}", 834 m_version, addr, 835 ${ident}_State_to_string(state), 836 ${ident}_Event_to_string(event), 837 ${ident}_State_to_string(next_state), 838 "Resource Stall"); 839 } 840 } else if (result == TransitionResult_ProtocolStall) { 841 DEBUG_MSG(GENERATED_COMP, HighPrio, "stalling"); 842 DEBUG_NEWLINE(GENERATED_COMP, MedPrio); 843 if (Debug::getProtocolTrace()) { 844 g_system_ptr->getProfiler()->profileTransition("${ident}", 845 m_version, addr, 846 ${ident}_State_to_string(state), 847 ${ident}_Event_to_string(event), 848 ${ident}_State_to_string(next_state), 849 "Protocol Stall"); 850 } 851 } 852 853 return result; 854} 855 856TransitionResult 857${ident}_Controller::doTransitionWorker(${ident}_Event event, 858 ${ident}_State state, 859 ${ident}_State& next_state, 860 const Address& addr) 861{ 862 switch(HASH_FUN(state, event)) { 863''') 864 865 # This map will allow suppress generating duplicate code 866 cases = orderdict() 867 868 for trans in self.transitions: 869 case_string = "%s_State_%s, %s_Event_%s" % \ 870 (self.ident, trans.state.ident, self.ident, trans.event.ident) 871 872 case = self.symtab.codeFormatter() 873 # Only set next_state if it changes 874 if trans.state != trans.nextState: 875 ns_ident = trans.nextState.ident 876 case('next_state = ${ident}_State_${ns_ident};') 877 878 actions = trans.actions 879 880 # Check for resources 881 case_sorter = [] 882 res = trans.resources 883 for key,val in res.iteritems(): 884 if key.type.ident != "DNUCAStopTable": 885 val = ''' 886if (!%s.areNSlotsAvailable(%s)) 887 return TransitionResult_ResourceStall; 888''' % (key.code, val) 889 case_sorter.append(val) 890 891 892 # Emit the code sequences in a sorted order. This makes the 893 # output deterministic (without this the output order can vary 894 # since Map's keys() on a vector of pointers is not deterministic 895 for c in sorted(case_sorter): 896 case("$c") 897 898 # Figure out if we stall 899 stall = False 900 for action in actions: 901 if action.ident == "z_stall": 902 stall = True 903 break 904 905 if stall: 906 case('return TransitionResult_ProtocolStall;') 907 else: 908 for action in actions: 909 case('${{action.ident}}(addr);') 910 case('return TransitionResult_Valid;') 911 912 case = str(case) 913 914 # Look to see if this transition code is unique. 915 if case not in cases: 916 cases[case] = [] 917 918 cases[case].append(case_string) 919 920 # Walk through all of the unique code blocks and spit out the 921 # corresponding case statement elements 922 for case,transitions in cases.iteritems(): 923 # Iterative over all the multiple transitions that share 924 # the same code 925 for trans in transitions: 926 code(' case HASH_FUN($trans):') 927 code(' $case') 928 929 code(''' 930 default: 931 WARN_EXPR(m_version); 932 WARN_EXPR(g_eventQueue_ptr->getTime()); 933 WARN_EXPR(addr); 934 WARN_EXPR(event); 935 WARN_EXPR(state); 936 ERROR_MSG(\"Invalid transition\"); 937 } 938 return TransitionResult_Valid; 939} 940''') 941 code.write(path, "%s_Transitions.cc" % self.ident) 942 943 def printProfileDumperHH(self, path): 944 code = self.symtab.codeFormatter() 945 ident = self.ident 946 947 code(''' 948// Auto generated C++ code started by $__file__:$__line__ 949// ${ident}: ${{self.short}} 950 951#ifndef __${ident}_PROFILE_DUMPER_HH__ 952#define __${ident}_PROFILE_DUMPER_HH__ 953 954#include <iostream> 955#include <vector> 956 957#include "${ident}_Profiler.hh" 958#include "${ident}_Event.hh" 959 960typedef std::vector<${ident}_Profiler *> ${ident}_profilers; 961 962class ${ident}_ProfileDumper 963{ 964 public: 965 ${ident}_ProfileDumper(); 966 void registerProfiler(${ident}_Profiler* profiler); 967 void dumpStats(std::ostream& out) const; 968 969 private: 970 ${ident}_profilers m_profilers; 971}; 972 973#endif // __${ident}_PROFILE_DUMPER_HH__ 974''') 975 code.write(path, "%s_ProfileDumper.hh" % self.ident) 976 977 def printProfileDumperCC(self, path): 978 code = self.symtab.codeFormatter() 979 ident = self.ident 980 981 code(''' 982// Auto generated C++ code started by $__file__:$__line__ 983// ${ident}: ${{self.short}} 984 985#include "mem/protocol/${ident}_ProfileDumper.hh" 986 987${ident}_ProfileDumper::${ident}_ProfileDumper() 988{ 989} 990 991void 992${ident}_ProfileDumper::registerProfiler(${ident}_Profiler* profiler) 993{ 994 m_profilers.push_back(profiler); 995} 996 997void 998${ident}_ProfileDumper::dumpStats(std::ostream& out) const 999{ 1000 out << " --- ${ident} ---\\n"; 1001 out << " - Event Counts -\\n"; 1002 for (${ident}_Event event = ${ident}_Event_FIRST; 1003 event < ${ident}_Event_NUM; 1004 ++event) { 1005 out << (${ident}_Event) event << " ["; 1006 uint64 total = 0; 1007 for (int i = 0; i < m_profilers.size(); i++) { 1008 out << m_profilers[i]->getEventCount(event) << " "; 1009 total += m_profilers[i]->getEventCount(event); 1010 } 1011 out << "] " << total << "\\n"; 1012 } 1013 out << "\\n"; 1014 out << " - Transitions -\\n"; 1015 for (${ident}_State state = ${ident}_State_FIRST; 1016 state < ${ident}_State_NUM; 1017 ++state) { 1018 for (${ident}_Event event = ${ident}_Event_FIRST; 1019 event < ${ident}_Event_NUM; 1020 ++event) { 1021 if (m_profilers[0]->isPossible(state, event)) { 1022 out << (${ident}_State) state << " " 1023 << (${ident}_Event) event << " ["; 1024 uint64 total = 0; 1025 for (int i = 0; i < m_profilers.size(); i++) { 1026 out << m_profilers[i]->getTransitionCount(state, event) << " "; 1027 total += m_profilers[i]->getTransitionCount(state, event); 1028 } 1029 out << "] " << total << "\\n"; 1030 } 1031 } 1032 out << "\\n"; 1033 } 1034} 1035''') 1036 code.write(path, "%s_ProfileDumper.cc" % self.ident) 1037 1038 def printProfilerHH(self, path): 1039 code = self.symtab.codeFormatter() 1040 ident = self.ident 1041 1042 code(''' 1043// Auto generated C++ code started by $__file__:$__line__ 1044// ${ident}: ${{self.short}} 1045 1046#ifndef __${ident}_PROFILER_HH__ 1047#define __${ident}_PROFILER_HH__ 1048 1049#include <iostream> 1050 1051#include "mem/ruby/common/Global.hh" 1052#include "mem/protocol/${ident}_State.hh" 1053#include "mem/protocol/${ident}_Event.hh" 1054 1055class ${ident}_Profiler 1056{ 1057 public: 1058 ${ident}_Profiler(); 1059 void setVersion(int version); 1060 void countTransition(${ident}_State state, ${ident}_Event event); 1061 void possibleTransition(${ident}_State state, ${ident}_Event event); 1062 uint64 getEventCount(${ident}_Event event); 1063 bool isPossible(${ident}_State state, ${ident}_Event event); 1064 uint64 getTransitionCount(${ident}_State state, ${ident}_Event event); 1065 void clearStats(); 1066 1067 private: 1068 int m_counters[${ident}_State_NUM][${ident}_Event_NUM]; 1069 int m_event_counters[${ident}_Event_NUM]; 1070 bool m_possible[${ident}_State_NUM][${ident}_Event_NUM]; 1071 int m_version; 1072}; 1073 1074#endif // __${ident}_PROFILER_HH__ 1075''') 1076 code.write(path, "%s_Profiler.hh" % self.ident) 1077 1078 def printProfilerCC(self, path): 1079 code = self.symtab.codeFormatter() 1080 ident = self.ident 1081 1082 code(''' 1083// Auto generated C++ code started by $__file__:$__line__ 1084// ${ident}: ${{self.short}} 1085 1086#include "mem/protocol/${ident}_Profiler.hh" 1087 1088${ident}_Profiler::${ident}_Profiler() 1089{ 1090 for (int state = 0; state < ${ident}_State_NUM; state++) { 1091 for (int event = 0; event < ${ident}_Event_NUM; event++) { 1092 m_possible[state][event] = false; 1093 m_counters[state][event] = 0; 1094 } 1095 } 1096 for (int event = 0; event < ${ident}_Event_NUM; event++) { 1097 m_event_counters[event] = 0; 1098 } 1099} 1100 1101void 1102${ident}_Profiler::setVersion(int version) 1103{ 1104 m_version = version; 1105} 1106 1107void 1108${ident}_Profiler::clearStats() 1109{ 1110 for (int state = 0; state < ${ident}_State_NUM; state++) { 1111 for (int event = 0; event < ${ident}_Event_NUM; event++) { 1112 m_counters[state][event] = 0; 1113 } 1114 } 1115 1116 for (int event = 0; event < ${ident}_Event_NUM; event++) { 1117 m_event_counters[event] = 0; 1118 } 1119} 1120void 1121${ident}_Profiler::countTransition(${ident}_State state, ${ident}_Event event) 1122{ 1123 assert(m_possible[state][event]); 1124 m_counters[state][event]++; 1125 m_event_counters[event]++; 1126} 1127void 1128${ident}_Profiler::possibleTransition(${ident}_State state, 1129 ${ident}_Event event) 1130{ 1131 m_possible[state][event] = true; 1132} 1133 1134uint64 1135${ident}_Profiler::getEventCount(${ident}_Event event) 1136{ 1137 return m_event_counters[event]; 1138} 1139 1140bool 1141${ident}_Profiler::isPossible(${ident}_State state, ${ident}_Event event) 1142{ 1143 return m_possible[state][event]; 1144} 1145 1146uint64 1147${ident}_Profiler::getTransitionCount(${ident}_State state, 1148 ${ident}_Event event) 1149{ 1150 return m_counters[state][event]; 1151} 1152 1153''') 1154 code.write(path, "%s_Profiler.cc" % self.ident) 1155 1156 # ************************** 1157 # ******* HTML Files ******* 1158 # ************************** 1159 def frameRef(self, click_href, click_target, over_href, over_num, text): 1160 code = self.symtab.codeFormatter(fix_newlines=False) 1161 code("""<A href=\"$click_href\" target=\"$click_target\" onmouseover=\" 1162 if (parent.frames[$over_num].location != parent.location + '$over_href') { 1163 parent.frames[$over_num].location='$over_href' 1164 }\"> 1165 ${{html.formatShorthand(text)}} 1166 </A>""") 1167 return str(code) 1168 1169 def writeHTMLFiles(self, path): 1170 # Create table with no row hilighted 1171 self.printHTMLTransitions(path, None) 1172 1173 # Generate transition tables 1174 for state in self.states.itervalues(): 1175 self.printHTMLTransitions(path, state) 1176 1177 # Generate action descriptions 1178 for action in self.actions.itervalues(): 1179 name = "%s_action_%s.html" % (self.ident, action.ident) 1180 code = html.createSymbol(action, "Action") 1181 code.write(path, name) 1182 1183 # Generate state descriptions 1184 for state in self.states.itervalues(): 1185 name = "%s_State_%s.html" % (self.ident, state.ident) 1186 code = html.createSymbol(state, "State") 1187 code.write(path, name) 1188 1189 # Generate event descriptions 1190 for event in self.events.itervalues(): 1191 name = "%s_Event_%s.html" % (self.ident, event.ident) 1192 code = html.createSymbol(event, "Event") 1193 code.write(path, name) 1194 1195 def printHTMLTransitions(self, path, active_state): 1196 code = self.symtab.codeFormatter() 1197 1198 code(''' 1199<HTML> 1200<BODY link="blue" vlink="blue"> 1201 1202<H1 align="center">${{html.formatShorthand(self.short)}}: 1203''') 1204 code.indent() 1205 for i,machine in enumerate(self.symtab.getAllType(StateMachine)): 1206 mid = machine.ident 1207 if i != 0: 1208 extra = " - " 1209 else: 1210 extra = "" 1211 if machine == self: 1212 code('$extra$mid') 1213 else: 1214 code('$extra<A target="Table" href="${mid}_table.html">$mid</A>') 1215 code.dedent() 1216 1217 code(""" 1218</H1> 1219 1220<TABLE border=1> 1221<TR> 1222 <TH> </TH> 1223""") 1224 1225 for event in self.events.itervalues(): 1226 href = "%s_Event_%s.html" % (self.ident, event.ident) 1227 ref = self.frameRef(href, "Status", href, "1", event.short) 1228 code('<TH bgcolor=white>$ref</TH>') 1229 1230 code('</TR>') 1231 # -- Body of table 1232 for state in self.states.itervalues(): 1233 # -- Each row 1234 if state == active_state: 1235 color = "yellow" 1236 else: 1237 color = "white" 1238 1239 click = "%s_table_%s.html" % (self.ident, state.ident) 1240 over = "%s_State_%s.html" % (self.ident, state.ident) 1241 text = html.formatShorthand(state.short) 1242 ref = self.frameRef(click, "Table", over, "1", state.short) 1243 code(''' 1244<TR> 1245 <TH bgcolor=$color>$ref</TH> 1246''') 1247 1248 # -- One column for each event 1249 for event in self.events.itervalues(): 1250 trans = self.table.get((state,event), None) 1251 if trans is None: 1252 # This is the no transition case 1253 if state == active_state: 1254 color = "#C0C000" 1255 else: 1256 color = "lightgrey" 1257 1258 code('<TD bgcolor=$color> </TD>') 1259 continue 1260 1261 next = trans.nextState 1262 stall_action = False 1263 1264 # -- Get the actions 1265 for action in trans.actions: 1266 if action.ident == "z_stall" or \ 1267 action.ident == "zz_recycleMandatoryQueue": 1268 stall_action = True 1269 1270 # -- Print out "actions/next-state" 1271 if stall_action: 1272 if state == active_state: 1273 color = "#C0C000" 1274 else: 1275 color = "lightgrey" 1276 1277 elif active_state and next.ident == active_state.ident: 1278 color = "aqua" 1279 elif state == active_state: 1280 color = "yellow" 1281 else: 1282 color = "white" 1283 1284 code('<TD bgcolor=$color>') 1285 for action in trans.actions: 1286 href = "%s_action_%s.html" % (self.ident, action.ident) 1287 ref = self.frameRef(href, "Status", href, "1", 1288 action.short) 1289 code(' $ref') 1290 if next != state: 1291 if trans.actions: 1292 code('/') 1293 click = "%s_table_%s.html" % (self.ident, next.ident) 1294 over = "%s_State_%s.html" % (self.ident, next.ident) 1295 ref = self.frameRef(click, "Table", over, "1", next.short) 1296 code("$ref") 1297 code("</TD>") 1298 1299 # -- Each row 1300 if state == active_state: 1301 color = "yellow" 1302 else: 1303 color = "white" 1304 1305 click = "%s_table_%s.html" % (self.ident, state.ident) 1306 over = "%s_State_%s.html" % (self.ident, state.ident) 1307 ref = self.frameRef(click, "Table", over, "1", state.short) 1308 code(''' 1309 <TH bgcolor=$color>$ref</TH> 1310</TR> 1311''') 1312 code(''' 1313<!- Column footer-> 1314<TR> 1315 <TH> </TH> 1316''') 1317 1318 for event in self.events.itervalues(): 1319 href = "%s_Event_%s.html" % (self.ident, event.ident) 1320 ref = self.frameRef(href, "Status", href, "1", event.short) 1321 code('<TH bgcolor=white>$ref</TH>') 1322 code(''' 1323</TR> 1324</TABLE> 1325</BODY></HTML> 1326''') 1327 1328 1329 if active_state: 1330 name = "%s_table_%s.html" % (self.ident, active_state.ident) 1331 else: 1332 name = "%s_table.html" % self.ident 1333 code.write(path, name) 1334 1335__all__ = [ "StateMachine" ] 1336