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