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