StateMachine.py revision 10972
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# Copyright (c) 2013 Advanced Micro Devices, Inc. 46145Snate@binkert.org# All rights reserved. 56145Snate@binkert.org# 66145Snate@binkert.org# Redistribution and use in source and binary forms, with or without 76145Snate@binkert.org# modification, are permitted provided that the following conditions are 86145Snate@binkert.org# met: redistributions of source code must retain the above copyright 96145Snate@binkert.org# notice, this list of conditions and the following disclaimer; 106145Snate@binkert.org# redistributions in binary form must reproduce the above copyright 116145Snate@binkert.org# notice, this list of conditions and the following disclaimer in the 126145Snate@binkert.org# documentation and/or other materials provided with the distribution; 136145Snate@binkert.org# neither the name of the copyright holders nor the names of its 146145Snate@binkert.org# contributors may be used to endorse or promote products derived from 156145Snate@binkert.org# this software without specific prior written permission. 166145Snate@binkert.org# 176145Snate@binkert.org# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 186145Snate@binkert.org# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 196145Snate@binkert.org# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 206145Snate@binkert.org# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 216145Snate@binkert.org# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 226145Snate@binkert.org# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 236145Snate@binkert.org# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 246145Snate@binkert.org# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 256145Snate@binkert.org# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 266145Snate@binkert.org# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 276145Snate@binkert.org# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 286145Snate@binkert.org 296154Snate@binkert.orgfrom m5.util import orderdict 306145Snate@binkert.org 317039Snate@binkert.orgfrom slicc.symbols.Symbol import Symbol 327039Snate@binkert.orgfrom slicc.symbols.Var import Var 338086SBrad.Beckmann@amd.comimport slicc.generate.html as html 347039Snate@binkert.orgimport re 357839Snilay@cs.wisc.edu 366145Snate@binkert.orgpython_class_map = { 376145Snate@binkert.org "int": "Int", 387039Snate@binkert.org "uint32_t" : "UInt32", 397039Snate@binkert.org "std::string": "String", 406145Snate@binkert.org "bool": "Bool", 416145Snate@binkert.org "CacheMemory": "RubyCache", 427839Snilay@cs.wisc.edu "WireBuffer": "RubyWireBuffer", 437839Snilay@cs.wisc.edu "Sequencer": "RubySequencer", 447839Snilay@cs.wisc.edu "DirectoryMemory": "RubyDirectoryMemory", 458086SBrad.Beckmann@amd.com "MemoryControl": "MemoryControl", 467839Snilay@cs.wisc.edu "DMASequencer": "DMASequencer", 478084SBrad.Beckmann@amd.com "Prefetcher":"Prefetcher", 487839Snilay@cs.wisc.edu "Cycles":"Cycles", 497839Snilay@cs.wisc.edu } 507839Snilay@cs.wisc.edu 51class StateMachine(Symbol): 52 def __init__(self, symtab, ident, location, pairs, config_parameters): 53 super(StateMachine, self).__init__(symtab, ident, location, pairs) 54 self.table = None 55 56 # Data members in the State Machine that have been declared before 57 # the opening brace '{' of the machine. Note that these along with 58 # the members in self.objects form the entire set of data members. 59 self.config_parameters = config_parameters 60 61 self.prefetchers = [] 62 63 for param in config_parameters: 64 if param.pointer: 65 var = Var(symtab, param.ident, location, param.type_ast.type, 66 "(*m_%s_ptr)" % param.ident, {}, self) 67 else: 68 var = Var(symtab, param.ident, location, param.type_ast.type, 69 "m_%s" % param.ident, {}, self) 70 71 self.symtab.registerSym(param.ident, var) 72 73 if str(param.type_ast.type) == "Prefetcher": 74 self.prefetchers.append(var) 75 76 self.states = orderdict() 77 self.events = orderdict() 78 self.actions = orderdict() 79 self.request_types = orderdict() 80 self.transitions = [] 81 self.in_ports = [] 82 self.functions = [] 83 84 # Data members in the State Machine that have been declared inside 85 # the {} machine. Note that these along with the config params 86 # form the entire set of data members of the machine. 87 self.objects = [] 88 self.TBEType = None 89 self.EntryType = None 90 self.debug_flags = set() 91 self.debug_flags.add('RubyGenerated') 92 self.debug_flags.add('RubySlicc') 93 94 def __repr__(self): 95 return "[StateMachine: %s]" % self.ident 96 97 def addState(self, state): 98 assert self.table is None 99 self.states[state.ident] = state 100 101 def addEvent(self, event): 102 assert self.table is None 103 self.events[event.ident] = event 104 105 def addAction(self, action): 106 assert self.table is None 107 108 # Check for duplicate action 109 for other in self.actions.itervalues(): 110 if action.ident == other.ident: 111 action.warning("Duplicate action definition: %s" % action.ident) 112 action.error("Duplicate action definition: %s" % action.ident) 113 if action.short == other.short: 114 other.warning("Duplicate action shorthand: %s" % other.ident) 115 other.warning(" shorthand = %s" % other.short) 116 action.warning("Duplicate action shorthand: %s" % action.ident) 117 action.error(" shorthand = %s" % action.short) 118 119 self.actions[action.ident] = action 120 121 def addDebugFlag(self, flag): 122 self.debug_flags.add(flag) 123 124 def addRequestType(self, request_type): 125 assert self.table is None 126 self.request_types[request_type.ident] = request_type 127 128 def addTransition(self, trans): 129 assert self.table is None 130 self.transitions.append(trans) 131 132 def addInPort(self, var): 133 self.in_ports.append(var) 134 135 def addFunc(self, func): 136 # register func in the symbol table 137 self.symtab.registerSym(str(func), func) 138 self.functions.append(func) 139 140 def addObject(self, obj): 141 self.symtab.registerSym(str(obj), obj) 142 self.objects.append(obj) 143 144 def addType(self, type): 145 type_ident = '%s' % type.c_ident 146 147 if type_ident == "%s_TBE" %self.ident: 148 if self.TBEType != None: 149 self.error("Multiple Transaction Buffer types in a " \ 150 "single machine."); 151 self.TBEType = type 152 153 elif "interface" in type and "AbstractCacheEntry" == type["interface"]: 154 if "main" in type and "false" == type["main"].lower(): 155 pass # this isn't the EntryType 156 else: 157 if self.EntryType != None: 158 self.error("Multiple AbstractCacheEntry types in a " \ 159 "single machine."); 160 self.EntryType = type 161 162 # Needs to be called before accessing the table 163 def buildTable(self): 164 assert self.table is None 165 166 table = {} 167 168 for trans in self.transitions: 169 # Track which actions we touch so we know if we use them 170 # all -- really this should be done for all symbols as 171 # part of the symbol table, then only trigger it for 172 # Actions, States, Events, etc. 173 174 for action in trans.actions: 175 action.used = True 176 177 index = (trans.state, trans.event) 178 if index in table: 179 table[index].warning("Duplicate transition: %s" % table[index]) 180 trans.error("Duplicate transition: %s" % trans) 181 table[index] = trans 182 183 # Look at all actions to make sure we used them all 184 for action in self.actions.itervalues(): 185 if not action.used: 186 error_msg = "Unused action: %s" % action.ident 187 if "desc" in action: 188 error_msg += ", " + action.desc 189 action.warning(error_msg) 190 self.table = table 191 192 # determine the port->msg buffer mappings 193 def getBufferMaps(self, ident): 194 msg_bufs = [] 195 port_to_buf_map = {} 196 in_msg_bufs = {} 197 for port in self.in_ports: 198 buf_name = "m_%s_ptr" % port.buffer_expr.name 199 msg_bufs.append(buf_name) 200 port_to_buf_map[port] = msg_bufs.index(buf_name) 201 if buf_name not in in_msg_bufs: 202 in_msg_bufs[buf_name] = [port] 203 else: 204 in_msg_bufs[buf_name].append(port) 205 return port_to_buf_map, in_msg_bufs, msg_bufs 206 207 def writeCodeFiles(self, path, includes): 208 self.printControllerPython(path) 209 self.printControllerHH(path) 210 self.printControllerCC(path, includes) 211 self.printCSwitch(path) 212 self.printCWakeup(path, includes) 213 214 def printControllerPython(self, path): 215 code = self.symtab.codeFormatter() 216 ident = self.ident 217 218 py_ident = "%s_Controller" % ident 219 c_ident = "%s_Controller" % self.ident 220 221 code(''' 222from m5.params import * 223from m5.SimObject import SimObject 224from Controller import RubyController 225 226class $py_ident(RubyController): 227 type = '$py_ident' 228 cxx_header = 'mem/protocol/${c_ident}.hh' 229''') 230 code.indent() 231 for param in self.config_parameters: 232 dflt_str = '' 233 234 if param.rvalue is not None: 235 dflt_str = str(param.rvalue.inline()) + ', ' 236 237 if param.type_ast.type.c_ident == "MessageBuffer": 238 if param["network"] == "To": 239 code('${{param.ident}} = MasterPort(${dflt_str}"")') 240 else: 241 code('${{param.ident}} = SlavePort(${dflt_str}"")') 242 243 elif python_class_map.has_key(param.type_ast.type.c_ident): 244 python_type = python_class_map[param.type_ast.type.c_ident] 245 code('${{param.ident}} = Param.${{python_type}}(${dflt_str}"")') 246 247 else: 248 self.error("Unknown c++ to python class conversion for c++ " \ 249 "type: '%s'. Please update the python_class_map " \ 250 "in StateMachine.py", param.type_ast.type.c_ident) 251 code.dedent() 252 code.write(path, '%s.py' % py_ident) 253 254 255 def printControllerHH(self, path): 256 '''Output the method declarations for the class declaration''' 257 code = self.symtab.codeFormatter() 258 ident = self.ident 259 c_ident = "%s_Controller" % self.ident 260 261 code(''' 262/** \\file $c_ident.hh 263 * 264 * Auto generated C++ code started by $__file__:$__line__ 265 * Created by slicc definition of Module "${{self.short}}" 266 */ 267 268#ifndef __${ident}_CONTROLLER_HH__ 269#define __${ident}_CONTROLLER_HH__ 270 271#include <iostream> 272#include <sstream> 273#include <string> 274 275#include "mem/protocol/TransitionResult.hh" 276#include "mem/protocol/Types.hh" 277#include "mem/ruby/common/Consumer.hh" 278#include "mem/ruby/slicc_interface/AbstractController.hh" 279#include "params/$c_ident.hh" 280 281''') 282 283 seen_types = set() 284 for var in self.objects: 285 if var.type.ident not in seen_types and not var.type.isPrimitive: 286 code('#include "mem/protocol/${{var.type.c_ident}}.hh"') 287 seen_types.add(var.type.ident) 288 289 # for adding information to the protocol debug trace 290 code(''' 291extern std::stringstream ${ident}_transitionComment; 292 293class $c_ident : public AbstractController 294{ 295 public: 296 typedef ${c_ident}Params Params; 297 $c_ident(const Params *p); 298 static int getNumControllers(); 299 void init(); 300 301 MessageBuffer* getMandatoryQueue() const; 302 void setNetQueue(const std::string& name, MessageBuffer *b); 303 304 void print(std::ostream& out) const; 305 void wakeup(); 306 void resetStats(); 307 void regStats(); 308 void collateStats(); 309 310 void recordCacheTrace(int cntrl, CacheRecorder* tr); 311 Sequencer* getSequencer() const; 312 313 int functionalWriteBuffers(PacketPtr&); 314 315 void countTransition(${ident}_State state, ${ident}_Event event); 316 void possibleTransition(${ident}_State state, ${ident}_Event event); 317 uint64 getEventCount(${ident}_Event event); 318 bool isPossible(${ident}_State state, ${ident}_Event event); 319 uint64 getTransitionCount(${ident}_State state, ${ident}_Event event); 320 321private: 322''') 323 324 code.indent() 325 # added by SS 326 for param in self.config_parameters: 327 if param.pointer: 328 code('${{param.type_ast.type}}* m_${{param.ident}}_ptr;') 329 else: 330 code('${{param.type_ast.type}} m_${{param.ident}};') 331 332 code(''' 333TransitionResult doTransition(${ident}_Event event, 334''') 335 336 if self.EntryType != None: 337 code(''' 338 ${{self.EntryType.c_ident}}* m_cache_entry_ptr, 339''') 340 if self.TBEType != None: 341 code(''' 342 ${{self.TBEType.c_ident}}* m_tbe_ptr, 343''') 344 345 code(''' 346 const Address addr); 347 348TransitionResult doTransitionWorker(${ident}_Event event, 349 ${ident}_State state, 350 ${ident}_State& next_state, 351''') 352 353 if self.TBEType != None: 354 code(''' 355 ${{self.TBEType.c_ident}}*& m_tbe_ptr, 356''') 357 if self.EntryType != None: 358 code(''' 359 ${{self.EntryType.c_ident}}*& m_cache_entry_ptr, 360''') 361 362 code(''' 363 const Address& addr); 364 365int m_counters[${ident}_State_NUM][${ident}_Event_NUM]; 366int m_event_counters[${ident}_Event_NUM]; 367bool m_possible[${ident}_State_NUM][${ident}_Event_NUM]; 368 369static std::vector<Stats::Vector *> eventVec; 370static std::vector<std::vector<Stats::Vector *> > transVec; 371static int m_num_controllers; 372 373// Internal functions 374''') 375 376 for func in self.functions: 377 proto = func.prototype 378 if proto: 379 code('$proto') 380 381 if self.EntryType != None: 382 code(''' 383 384// Set and Reset for cache_entry variable 385void set_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, AbstractCacheEntry* m_new_cache_entry); 386void unset_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr); 387''') 388 389 if self.TBEType != None: 390 code(''' 391 392// Set and Reset for tbe variable 393void set_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${ident}_TBE* m_new_tbe); 394void unset_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr); 395''') 396 397 # Prototype the actions that the controller can take 398 code(''' 399 400// Actions 401''') 402 if self.TBEType != None and self.EntryType != None: 403 for action in self.actions.itervalues(): 404 code('/** \\brief ${{action.desc}} */') 405 code('void ${{action.ident}}(${{self.TBEType.c_ident}}*& ' 406 'm_tbe_ptr, ${{self.EntryType.c_ident}}*& ' 407 'm_cache_entry_ptr, const Address& addr);') 408 elif self.TBEType != None: 409 for action in self.actions.itervalues(): 410 code('/** \\brief ${{action.desc}} */') 411 code('void ${{action.ident}}(${{self.TBEType.c_ident}}*& ' 412 'm_tbe_ptr, const Address& addr);') 413 elif self.EntryType != None: 414 for action in self.actions.itervalues(): 415 code('/** \\brief ${{action.desc}} */') 416 code('void ${{action.ident}}(${{self.EntryType.c_ident}}*& ' 417 'm_cache_entry_ptr, const Address& addr);') 418 else: 419 for action in self.actions.itervalues(): 420 code('/** \\brief ${{action.desc}} */') 421 code('void ${{action.ident}}(const Address& addr);') 422 423 # the controller internal variables 424 code(''' 425 426// Objects 427''') 428 for var in self.objects: 429 th = var.get("template", "") 430 code('${{var.type.c_ident}}$th* m_${{var.ident}}_ptr;') 431 432 code.dedent() 433 code('};') 434 code('#endif // __${ident}_CONTROLLER_H__') 435 code.write(path, '%s.hh' % c_ident) 436 437 def printControllerCC(self, path, includes): 438 '''Output the actions for performing the actions''' 439 440 code = self.symtab.codeFormatter() 441 ident = self.ident 442 c_ident = "%s_Controller" % self.ident 443 444 code(''' 445/** \\file $c_ident.cc 446 * 447 * Auto generated C++ code started by $__file__:$__line__ 448 * Created by slicc definition of Module "${{self.short}}" 449 */ 450 451#include <sys/types.h> 452#include <unistd.h> 453 454#include <cassert> 455#include <sstream> 456#include <string> 457#include <typeinfo> 458 459#include "base/compiler.hh" 460#include "base/cprintf.hh" 461 462''') 463 for f in self.debug_flags: 464 code('#include "debug/${{f}}.hh"') 465 code(''' 466#include "mem/protocol/${ident}_Controller.hh" 467#include "mem/protocol/${ident}_Event.hh" 468#include "mem/protocol/${ident}_State.hh" 469#include "mem/protocol/Types.hh" 470#include "mem/ruby/system/System.hh" 471 472''') 473 for include_path in includes: 474 code('#include "${{include_path}}"') 475 476 code(''' 477 478using namespace std; 479''') 480 481 # include object classes 482 seen_types = set() 483 for var in self.objects: 484 if var.type.ident not in seen_types and not var.type.isPrimitive: 485 code('#include "mem/protocol/${{var.type.c_ident}}.hh"') 486 seen_types.add(var.type.ident) 487 488 num_in_ports = len(self.in_ports) 489 490 code(''' 491$c_ident * 492${c_ident}Params::create() 493{ 494 return new $c_ident(this); 495} 496 497int $c_ident::m_num_controllers = 0; 498std::vector<Stats::Vector *> $c_ident::eventVec; 499std::vector<std::vector<Stats::Vector *> > $c_ident::transVec; 500 501// for adding information to the protocol debug trace 502stringstream ${ident}_transitionComment; 503 504#ifndef NDEBUG 505#define APPEND_TRANSITION_COMMENT(str) (${ident}_transitionComment << str) 506#else 507#define APPEND_TRANSITION_COMMENT(str) do {} while (0) 508#endif 509 510/** \\brief constructor */ 511$c_ident::$c_ident(const Params *p) 512 : AbstractController(p) 513{ 514 m_machineID.type = MachineType_${ident}; 515 m_machineID.num = m_version; 516 m_num_controllers++; 517 518 m_in_ports = $num_in_ports; 519''') 520 code.indent() 521 522 # 523 # After initializing the universal machine parameters, initialize the 524 # this machines config parameters. Also if these configuration params 525 # include a sequencer, connect the it to the controller. 526 # 527 for param in self.config_parameters: 528 529 # Do not initialize messgage buffers since they are initialized 530 # when the port based connections are made. 531 if param.type_ast.type.c_ident == "MessageBuffer": 532 continue 533 534 if param.pointer: 535 code('m_${{param.ident}}_ptr = p->${{param.ident}};') 536 else: 537 code('m_${{param.ident}} = p->${{param.ident}};') 538 539 if re.compile("sequencer").search(param.ident): 540 code('m_${{param.ident}}_ptr->setController(this);') 541 542 for var in self.objects: 543 if var.ident.find("mandatoryQueue") >= 0: 544 code(''' 545m_${{var.ident}}_ptr = new ${{var.type.c_ident}}(); 546m_${{var.ident}}_ptr->setReceiver(this); 547''') 548 549 code(''' 550 551for (int state = 0; state < ${ident}_State_NUM; state++) { 552 for (int event = 0; event < ${ident}_Event_NUM; event++) { 553 m_possible[state][event] = false; 554 m_counters[state][event] = 0; 555 } 556} 557for (int event = 0; event < ${ident}_Event_NUM; event++) { 558 m_event_counters[event] = 0; 559} 560''') 561 code.dedent() 562 code(''' 563} 564 565void 566$c_ident::setNetQueue(const std::string& name, MessageBuffer *b) 567{ 568 MachineType machine_type = string_to_MachineType("${{self.ident}}"); 569 int base M5_VAR_USED = MachineType_base_number(machine_type); 570 571''') 572 code.indent() 573 574 # set for maintaining the vnet, direction pairs already seen for this 575 # machine. This map helps in implementing the check for avoiding 576 # multiple message buffers being mapped to the same vnet. 577 vnet_dir_set = set() 578 579 for var in self.config_parameters: 580 if "network" in var: 581 vtype = var.type_ast.type 582 vid = "m_%s_ptr" % var.ident 583 584 code(''' 585if ("${{var.ident}}" == name) { 586 $vid = b; 587 assert($vid != NULL); 588''') 589 code.indent() 590 # Network port object 591 network = var["network"] 592 ordered = var["ordered"] 593 594 if "virtual_network" in var: 595 vnet = var["virtual_network"] 596 vnet_type = var["vnet_type"] 597 598 assert (vnet, network) not in vnet_dir_set 599 vnet_dir_set.add((vnet,network)) 600 601 code(''' 602m_net_ptr->set${network}NetQueue(m_version + base, $ordered, $vnet, 603 "$vnet_type", b); 604''') 605 # Set the end 606 if network == "To": 607 code('$vid->setSender(this);') 608 else: 609 code('$vid->setReceiver(this);') 610 611 # Set ordering 612 code('$vid->setOrdering(${{var["ordered"]}});') 613 614 # Set randomization 615 if "random" in var: 616 # A buffer 617 code('$vid->setRandomization(${{var["random"]}});') 618 619 # Set Priority 620 if "rank" in var: 621 code('$vid->setPriority(${{var["rank"]}})') 622 623 # Set buffer size 624 code('$vid->resize(m_buffer_size);') 625 626 if "recycle_latency" in var: 627 code('$vid->setRecycleLatency( ' \ 628 'Cycles(${{var["recycle_latency"]}}));') 629 else: 630 code('$vid->setRecycleLatency(m_recycle_latency);') 631 632 # set description (may be overriden later by port def) 633 code(''' 634$vid->setDescription("[Version " + to_string(m_version) + ", ${ident}, name=${{var.ident}}]"); 635''') 636 code.dedent() 637 code('}\n') 638 639 code.dedent() 640 code(''' 641} 642 643void 644$c_ident::init() 645{ 646 // initialize objects 647 648''') 649 650 code.indent() 651 652 for var in self.objects: 653 vtype = var.type 654 vid = "m_%s_ptr" % var.ident 655 if "network" not in var: 656 # Not a network port object 657 if "primitive" in vtype: 658 code('$vid = new ${{vtype.c_ident}};') 659 if "default" in var: 660 code('(*$vid) = ${{var["default"]}};') 661 else: 662 # Normal Object 663 if var.ident.find("mandatoryQueue") < 0: 664 th = var.get("template", "") 665 expr = "%s = new %s%s" % (vid, vtype.c_ident, th) 666 args = "" 667 if "non_obj" not in vtype and not vtype.isEnumeration: 668 args = var.get("constructor", "") 669 code('$expr($args);') 670 671 code('assert($vid != NULL);') 672 673 if "default" in var: 674 code('*$vid = ${{var["default"]}}; // Object default') 675 elif "default" in vtype: 676 comment = "Type %s default" % vtype.ident 677 code('*$vid = ${{vtype["default"]}}; // $comment') 678 679 # Set ordering 680 if "ordered" in var: 681 # A buffer 682 code('$vid->setOrdering(${{var["ordered"]}});') 683 684 # Set randomization 685 if "random" in var: 686 # A buffer 687 code('$vid->setRandomization(${{var["random"]}});') 688 689 # Set Priority 690 if vtype.isBuffer and "rank" in var: 691 code('$vid->setPriority(${{var["rank"]}});') 692 693 # Set sender and receiver for trigger queue 694 if var.ident.find("triggerQueue") >= 0: 695 code('$vid->setSender(this);') 696 code('$vid->setReceiver(this);') 697 elif vtype.c_ident == "TimerTable": 698 code('$vid->setClockObj(this);') 699 elif var.ident.find("optionalQueue") >= 0: 700 code('$vid->setSender(this);') 701 code('$vid->setReceiver(this);') 702 703 if vtype.isBuffer: 704 if "recycle_latency" in var: 705 code('$vid->setRecycleLatency( ' \ 706 'Cycles(${{var["recycle_latency"]}}));') 707 else: 708 code('$vid->setRecycleLatency(m_recycle_latency);') 709 710 # Set the prefetchers 711 code() 712 for prefetcher in self.prefetchers: 713 code('${{prefetcher.code}}.setController(this);') 714 715 code() 716 for port in self.in_ports: 717 # Set the queue consumers 718 code('${{port.code}}.setConsumer(this);') 719 # Set the queue descriptions 720 code('${{port.code}}.setDescription("[Version " + to_string(m_version) + ", $ident, $port]");') 721 722 # Initialize the transition profiling 723 code() 724 for trans in self.transitions: 725 # Figure out if we stall 726 stall = False 727 for action in trans.actions: 728 if action.ident == "z_stall": 729 stall = True 730 731 # Only possible if it is not a 'z' case 732 if not stall: 733 state = "%s_State_%s" % (self.ident, trans.state.ident) 734 event = "%s_Event_%s" % (self.ident, trans.event.ident) 735 code('possibleTransition($state, $event);') 736 737 code.dedent() 738 code(''' 739 AbstractController::init(); 740 resetStats(); 741} 742''') 743 744 mq_ident = "NULL" 745 for port in self.in_ports: 746 if port.code.find("mandatoryQueue_ptr") >= 0: 747 mq_ident = "m_mandatoryQueue_ptr" 748 749 seq_ident = "NULL" 750 for param in self.config_parameters: 751 if param.ident == "sequencer": 752 assert(param.pointer) 753 seq_ident = "m_%s_ptr" % param.ident 754 755 code(''' 756 757void 758$c_ident::regStats() 759{ 760 AbstractController::regStats(); 761 762 if (m_version == 0) { 763 for (${ident}_Event event = ${ident}_Event_FIRST; 764 event < ${ident}_Event_NUM; ++event) { 765 Stats::Vector *t = new Stats::Vector(); 766 t->init(m_num_controllers); 767 t->name(params()->ruby_system->name() + ".${c_ident}." + 768 ${ident}_Event_to_string(event)); 769 t->flags(Stats::pdf | Stats::total | Stats::oneline | 770 Stats::nozero); 771 772 eventVec.push_back(t); 773 } 774 775 for (${ident}_State state = ${ident}_State_FIRST; 776 state < ${ident}_State_NUM; ++state) { 777 778 transVec.push_back(std::vector<Stats::Vector *>()); 779 780 for (${ident}_Event event = ${ident}_Event_FIRST; 781 event < ${ident}_Event_NUM; ++event) { 782 783 Stats::Vector *t = new Stats::Vector(); 784 t->init(m_num_controllers); 785 t->name(params()->ruby_system->name() + ".${c_ident}." + 786 ${ident}_State_to_string(state) + 787 "." + ${ident}_Event_to_string(event)); 788 789 t->flags(Stats::pdf | Stats::total | Stats::oneline | 790 Stats::nozero); 791 transVec[state].push_back(t); 792 } 793 } 794 } 795} 796 797void 798$c_ident::collateStats() 799{ 800 for (${ident}_Event event = ${ident}_Event_FIRST; 801 event < ${ident}_Event_NUM; ++event) { 802 for (unsigned int i = 0; i < m_num_controllers; ++i) { 803 RubySystem *rs = params()->ruby_system; 804 std::map<uint32_t, AbstractController *>::iterator it = 805 rs->m_abstract_controls[MachineType_${ident}].find(i); 806 assert(it != rs->m_abstract_controls[MachineType_${ident}].end()); 807 (*eventVec[event])[i] = 808 (($c_ident *)(*it).second)->getEventCount(event); 809 } 810 } 811 812 for (${ident}_State state = ${ident}_State_FIRST; 813 state < ${ident}_State_NUM; ++state) { 814 815 for (${ident}_Event event = ${ident}_Event_FIRST; 816 event < ${ident}_Event_NUM; ++event) { 817 818 for (unsigned int i = 0; i < m_num_controllers; ++i) { 819 RubySystem *rs = params()->ruby_system; 820 std::map<uint32_t, AbstractController *>::iterator it = 821 rs->m_abstract_controls[MachineType_${ident}].find(i); 822 assert(it != rs->m_abstract_controls[MachineType_${ident}].end()); 823 (*transVec[state][event])[i] = 824 (($c_ident *)(*it).second)->getTransitionCount(state, event); 825 } 826 } 827 } 828} 829 830void 831$c_ident::countTransition(${ident}_State state, ${ident}_Event event) 832{ 833 assert(m_possible[state][event]); 834 m_counters[state][event]++; 835 m_event_counters[event]++; 836} 837void 838$c_ident::possibleTransition(${ident}_State state, 839 ${ident}_Event event) 840{ 841 m_possible[state][event] = true; 842} 843 844uint64 845$c_ident::getEventCount(${ident}_Event event) 846{ 847 return m_event_counters[event]; 848} 849 850bool 851$c_ident::isPossible(${ident}_State state, ${ident}_Event event) 852{ 853 return m_possible[state][event]; 854} 855 856uint64 857$c_ident::getTransitionCount(${ident}_State state, 858 ${ident}_Event event) 859{ 860 return m_counters[state][event]; 861} 862 863int 864$c_ident::getNumControllers() 865{ 866 return m_num_controllers; 867} 868 869MessageBuffer* 870$c_ident::getMandatoryQueue() const 871{ 872 return $mq_ident; 873} 874 875Sequencer* 876$c_ident::getSequencer() const 877{ 878 return $seq_ident; 879} 880 881void 882$c_ident::print(ostream& out) const 883{ 884 out << "[$c_ident " << m_version << "]"; 885} 886 887void $c_ident::resetStats() 888{ 889 for (int state = 0; state < ${ident}_State_NUM; state++) { 890 for (int event = 0; event < ${ident}_Event_NUM; event++) { 891 m_counters[state][event] = 0; 892 } 893 } 894 895 for (int event = 0; event < ${ident}_Event_NUM; event++) { 896 m_event_counters[event] = 0; 897 } 898 899 AbstractController::resetStats(); 900} 901''') 902 903 if self.EntryType != None: 904 code(''' 905 906// Set and Reset for cache_entry variable 907void 908$c_ident::set_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, AbstractCacheEntry* m_new_cache_entry) 909{ 910 m_cache_entry_ptr = (${{self.EntryType.c_ident}}*)m_new_cache_entry; 911} 912 913void 914$c_ident::unset_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr) 915{ 916 m_cache_entry_ptr = 0; 917} 918''') 919 920 if self.TBEType != None: 921 code(''' 922 923// Set and Reset for tbe variable 924void 925$c_ident::set_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.TBEType.c_ident}}* m_new_tbe) 926{ 927 m_tbe_ptr = m_new_tbe; 928} 929 930void 931$c_ident::unset_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr) 932{ 933 m_tbe_ptr = NULL; 934} 935''') 936 937 code(''' 938 939void 940$c_ident::recordCacheTrace(int cntrl, CacheRecorder* tr) 941{ 942''') 943 # 944 # Record cache contents for all associated caches. 945 # 946 code.indent() 947 for param in self.config_parameters: 948 if param.type_ast.type.ident == "CacheMemory": 949 assert(param.pointer) 950 code('m_${{param.ident}}_ptr->recordCacheContents(cntrl, tr);') 951 952 code.dedent() 953 code(''' 954} 955 956// Actions 957''') 958 if self.TBEType != None and self.EntryType != None: 959 for action in self.actions.itervalues(): 960 if "c_code" not in action: 961 continue 962 963 code(''' 964/** \\brief ${{action.desc}} */ 965void 966$c_ident::${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr) 967{ 968 DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n"); 969 try { 970 ${{action["c_code"]}} 971 } catch (const RejectException & e) { 972 fatal("Error in action ${{ident}}:${{action.ident}}: " 973 "executed a peek statement with the wrong message " 974 "type specified. "); 975 } 976} 977 978''') 979 elif self.TBEType != None: 980 for action in self.actions.itervalues(): 981 if "c_code" not in action: 982 continue 983 984 code(''' 985/** \\brief ${{action.desc}} */ 986void 987$c_ident::${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, const Address& addr) 988{ 989 DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n"); 990 ${{action["c_code"]}} 991} 992 993''') 994 elif self.EntryType != None: 995 for action in self.actions.itervalues(): 996 if "c_code" not in action: 997 continue 998 999 code(''' 1000/** \\brief ${{action.desc}} */ 1001void 1002$c_ident::${{action.ident}}(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr) 1003{ 1004 DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n"); 1005 ${{action["c_code"]}} 1006} 1007 1008''') 1009 else: 1010 for action in self.actions.itervalues(): 1011 if "c_code" not in action: 1012 continue 1013 1014 code(''' 1015/** \\brief ${{action.desc}} */ 1016void 1017$c_ident::${{action.ident}}(const Address& addr) 1018{ 1019 DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n"); 1020 ${{action["c_code"]}} 1021} 1022 1023''') 1024 for func in self.functions: 1025 code(func.generateCode()) 1026 1027 # Function for functional writes to messages buffered in the controller 1028 code(''' 1029int 1030$c_ident::functionalWriteBuffers(PacketPtr& pkt) 1031{ 1032 int num_functional_writes = 0; 1033''') 1034 for var in self.objects: 1035 vtype = var.type 1036 if vtype.isBuffer: 1037 vid = "m_%s_ptr" % var.ident 1038 code('num_functional_writes += $vid->functionalWrite(pkt);') 1039 1040 for var in self.config_parameters: 1041 vtype = var.type_ast.type 1042 if vtype.isBuffer: 1043 vid = "m_%s_ptr" % var.ident 1044 code('num_functional_writes += $vid->functionalWrite(pkt);') 1045 1046 code(''' 1047 return num_functional_writes; 1048} 1049''') 1050 1051 code.write(path, "%s.cc" % c_ident) 1052 1053 def printCWakeup(self, path, includes): 1054 '''Output the wakeup loop for the events''' 1055 1056 code = self.symtab.codeFormatter() 1057 ident = self.ident 1058 1059 outputRequest_types = True 1060 if len(self.request_types) == 0: 1061 outputRequest_types = False 1062 1063 code(''' 1064// Auto generated C++ code started by $__file__:$__line__ 1065// ${ident}: ${{self.short}} 1066 1067#include <sys/types.h> 1068#include <unistd.h> 1069 1070#include <cassert> 1071#include <typeinfo> 1072 1073#include "base/misc.hh" 1074 1075''') 1076 for f in self.debug_flags: 1077 code('#include "debug/${{f}}.hh"') 1078 code(''' 1079#include "mem/protocol/${ident}_Controller.hh" 1080#include "mem/protocol/${ident}_Event.hh" 1081#include "mem/protocol/${ident}_State.hh" 1082 1083''') 1084 1085 if outputRequest_types: 1086 code('''#include "mem/protocol/${ident}_RequestType.hh"''') 1087 1088 code(''' 1089#include "mem/protocol/Types.hh" 1090#include "mem/ruby/system/System.hh" 1091 1092''') 1093 1094 1095 for include_path in includes: 1096 code('#include "${{include_path}}"') 1097 1098 port_to_buf_map, in_msg_bufs, msg_bufs = self.getBufferMaps(ident) 1099 1100 code(''' 1101 1102using namespace std; 1103 1104void 1105${ident}_Controller::wakeup() 1106{ 1107 int counter = 0; 1108 while (true) { 1109 unsigned char rejected[${{len(msg_bufs)}}]; 1110 memset(rejected, 0, sizeof(unsigned char)*${{len(msg_bufs)}}); 1111 // Some cases will put us into an infinite loop without this limit 1112 assert(counter <= m_transitions_per_cycle); 1113 if (counter == m_transitions_per_cycle) { 1114 // Count how often we are fully utilized 1115 m_fully_busy_cycles++; 1116 1117 // Wakeup in another cycle and try again 1118 scheduleEvent(Cycles(1)); 1119 break; 1120 } 1121''') 1122 1123 code.indent() 1124 code.indent() 1125 1126 # InPorts 1127 # 1128 for port in self.in_ports: 1129 code.indent() 1130 code('// ${ident}InPort $port') 1131 if port.pairs.has_key("rank"): 1132 code('m_cur_in_port = ${{port.pairs["rank"]}};') 1133 else: 1134 code('m_cur_in_port = 0;') 1135 if port in port_to_buf_map: 1136 code('try {') 1137 code.indent() 1138 code('${{port["c_code_in_port"]}}') 1139 1140 if port in port_to_buf_map: 1141 code.dedent() 1142 code(''' 1143 } catch (const RejectException & e) { 1144 rejected[${{port_to_buf_map[port]}}]++; 1145 } 1146''') 1147 code.dedent() 1148 code('') 1149 1150 code.dedent() 1151 code.dedent() 1152 code(''' 1153 // If we got this far, we have nothing left todo or something went 1154 // wrong''') 1155 for buf_name, ports in in_msg_bufs.items(): 1156 if len(ports) > 1: 1157 # only produce checks when a buffer is shared by multiple ports 1158 code(''' 1159 if (${{buf_name}}->isReady() && rejected[${{port_to_buf_map[ports[0]]}}] == ${{len(ports)}}) 1160 { 1161 // no port claimed the message on the top of this buffer 1162 panic("Runtime Error at Ruby Time: %d. " 1163 "All ports rejected a message. " 1164 "You are probably sending a message type to this controller " 1165 "over a virtual network that do not define an in_port for " 1166 "the incoming message type.\\n", 1167 Cycles(1)); 1168 } 1169''') 1170 code(''' 1171 break; 1172 } 1173} 1174''') 1175 1176 code.write(path, "%s_Wakeup.cc" % self.ident) 1177 1178 def printCSwitch(self, path): 1179 '''Output switch statement for transition table''' 1180 1181 code = self.symtab.codeFormatter() 1182 ident = self.ident 1183 1184 code(''' 1185// Auto generated C++ code started by $__file__:$__line__ 1186// ${ident}: ${{self.short}} 1187 1188#include <cassert> 1189 1190#include "base/misc.hh" 1191#include "base/trace.hh" 1192#include "debug/ProtocolTrace.hh" 1193#include "debug/RubyGenerated.hh" 1194#include "mem/protocol/${ident}_Controller.hh" 1195#include "mem/protocol/${ident}_Event.hh" 1196#include "mem/protocol/${ident}_State.hh" 1197#include "mem/protocol/Types.hh" 1198#include "mem/ruby/system/System.hh" 1199 1200#define HASH_FUN(state, event) ((int(state)*${ident}_Event_NUM)+int(event)) 1201 1202#define GET_TRANSITION_COMMENT() (${ident}_transitionComment.str()) 1203#define CLEAR_TRANSITION_COMMENT() (${ident}_transitionComment.str("")) 1204 1205TransitionResult 1206${ident}_Controller::doTransition(${ident}_Event event, 1207''') 1208 if self.EntryType != None: 1209 code(''' 1210 ${{self.EntryType.c_ident}}* m_cache_entry_ptr, 1211''') 1212 if self.TBEType != None: 1213 code(''' 1214 ${{self.TBEType.c_ident}}* m_tbe_ptr, 1215''') 1216 code(''' 1217 const Address addr) 1218{ 1219''') 1220 code.indent() 1221 1222 if self.TBEType != None and self.EntryType != None: 1223 code('${ident}_State state = getState(m_tbe_ptr, m_cache_entry_ptr, addr);') 1224 elif self.TBEType != None: 1225 code('${ident}_State state = getState(m_tbe_ptr, addr);') 1226 elif self.EntryType != None: 1227 code('${ident}_State state = getState(m_cache_entry_ptr, addr);') 1228 else: 1229 code('${ident}_State state = getState(addr);') 1230 1231 code(''' 1232${ident}_State next_state = state; 1233 1234DPRINTF(RubyGenerated, "%s, Time: %lld, state: %s, event: %s, addr: %s\\n", 1235 *this, curCycle(), ${ident}_State_to_string(state), 1236 ${ident}_Event_to_string(event), addr); 1237 1238TransitionResult result = 1239''') 1240 if self.TBEType != None and self.EntryType != None: 1241 code('doTransitionWorker(event, state, next_state, m_tbe_ptr, m_cache_entry_ptr, addr);') 1242 elif self.TBEType != None: 1243 code('doTransitionWorker(event, state, next_state, m_tbe_ptr, addr);') 1244 elif self.EntryType != None: 1245 code('doTransitionWorker(event, state, next_state, m_cache_entry_ptr, addr);') 1246 else: 1247 code('doTransitionWorker(event, state, next_state, addr);') 1248 1249 port_to_buf_map, in_msg_bufs, msg_bufs = self.getBufferMaps(ident) 1250 1251 code(''' 1252 1253if (result == TransitionResult_Valid) { 1254 DPRINTF(RubyGenerated, "next_state: %s\\n", 1255 ${ident}_State_to_string(next_state)); 1256 countTransition(state, event); 1257 1258 DPRINTFR(ProtocolTrace, "%15d %3s %10s%20s %6s>%-6s %s %s\\n", 1259 curTick(), m_version, "${ident}", 1260 ${ident}_Event_to_string(event), 1261 ${ident}_State_to_string(state), 1262 ${ident}_State_to_string(next_state), 1263 addr, GET_TRANSITION_COMMENT()); 1264 1265 CLEAR_TRANSITION_COMMENT(); 1266''') 1267 if self.TBEType != None and self.EntryType != None: 1268 code('setState(m_tbe_ptr, m_cache_entry_ptr, addr, next_state);') 1269 code('setAccessPermission(m_cache_entry_ptr, addr, next_state);') 1270 elif self.TBEType != None: 1271 code('setState(m_tbe_ptr, addr, next_state);') 1272 code('setAccessPermission(addr, next_state);') 1273 elif self.EntryType != None: 1274 code('setState(m_cache_entry_ptr, addr, next_state);') 1275 code('setAccessPermission(m_cache_entry_ptr, addr, next_state);') 1276 else: 1277 code('setState(addr, next_state);') 1278 code('setAccessPermission(addr, next_state);') 1279 1280 code(''' 1281} else if (result == TransitionResult_ResourceStall) { 1282 DPRINTFR(ProtocolTrace, "%15s %3s %10s%20s %6s>%-6s %s %s\\n", 1283 curTick(), m_version, "${ident}", 1284 ${ident}_Event_to_string(event), 1285 ${ident}_State_to_string(state), 1286 ${ident}_State_to_string(next_state), 1287 addr, "Resource Stall"); 1288} else if (result == TransitionResult_ProtocolStall) { 1289 DPRINTF(RubyGenerated, "stalling\\n"); 1290 DPRINTFR(ProtocolTrace, "%15s %3s %10s%20s %6s>%-6s %s %s\\n", 1291 curTick(), m_version, "${ident}", 1292 ${ident}_Event_to_string(event), 1293 ${ident}_State_to_string(state), 1294 ${ident}_State_to_string(next_state), 1295 addr, "Protocol Stall"); 1296} 1297 1298return result; 1299''') 1300 code.dedent() 1301 code(''' 1302} 1303 1304TransitionResult 1305${ident}_Controller::doTransitionWorker(${ident}_Event event, 1306 ${ident}_State state, 1307 ${ident}_State& next_state, 1308''') 1309 1310 if self.TBEType != None: 1311 code(''' 1312 ${{self.TBEType.c_ident}}*& m_tbe_ptr, 1313''') 1314 if self.EntryType != None: 1315 code(''' 1316 ${{self.EntryType.c_ident}}*& m_cache_entry_ptr, 1317''') 1318 code(''' 1319 const Address& addr) 1320{ 1321 switch(HASH_FUN(state, event)) { 1322''') 1323 1324 # This map will allow suppress generating duplicate code 1325 cases = orderdict() 1326 1327 for trans in self.transitions: 1328 case_string = "%s_State_%s, %s_Event_%s" % \ 1329 (self.ident, trans.state.ident, self.ident, trans.event.ident) 1330 1331 case = self.symtab.codeFormatter() 1332 # Only set next_state if it changes 1333 if trans.state != trans.nextState: 1334 if trans.nextState.isWildcard(): 1335 # When * is encountered as an end state of a transition, 1336 # the next state is determined by calling the 1337 # machine-specific getNextState function. The next state 1338 # is determined before any actions of the transition 1339 # execute, and therefore the next state calculation cannot 1340 # depend on any of the transitionactions. 1341 case('next_state = getNextState(addr);') 1342 else: 1343 ns_ident = trans.nextState.ident 1344 case('next_state = ${ident}_State_${ns_ident};') 1345 1346 actions = trans.actions 1347 request_types = trans.request_types 1348 1349 # Check for resources 1350 case_sorter = [] 1351 res = trans.resources 1352 for key,val in res.iteritems(): 1353 val = ''' 1354if (!%s.areNSlotsAvailable(%s)) 1355 return TransitionResult_ResourceStall; 1356''' % (key.code, val) 1357 case_sorter.append(val) 1358 1359 # Check all of the request_types for resource constraints 1360 for request_type in request_types: 1361 val = ''' 1362if (!checkResourceAvailable(%s_RequestType_%s, addr)) { 1363 return TransitionResult_ResourceStall; 1364} 1365''' % (self.ident, request_type.ident) 1366 case_sorter.append(val) 1367 1368 # Emit the code sequences in a sorted order. This makes the 1369 # output deterministic (without this the output order can vary 1370 # since Map's keys() on a vector of pointers is not deterministic 1371 for c in sorted(case_sorter): 1372 case("$c") 1373 1374 # Record access types for this transition 1375 for request_type in request_types: 1376 case('recordRequestType(${ident}_RequestType_${{request_type.ident}}, addr);') 1377 1378 # Figure out if we stall 1379 stall = False 1380 for action in actions: 1381 if action.ident == "z_stall": 1382 stall = True 1383 break 1384 1385 if stall: 1386 case('return TransitionResult_ProtocolStall;') 1387 else: 1388 if self.TBEType != None and self.EntryType != None: 1389 for action in actions: 1390 case('${{action.ident}}(m_tbe_ptr, m_cache_entry_ptr, addr);') 1391 elif self.TBEType != None: 1392 for action in actions: 1393 case('${{action.ident}}(m_tbe_ptr, addr);') 1394 elif self.EntryType != None: 1395 for action in actions: 1396 case('${{action.ident}}(m_cache_entry_ptr, addr);') 1397 else: 1398 for action in actions: 1399 case('${{action.ident}}(addr);') 1400 case('return TransitionResult_Valid;') 1401 1402 case = str(case) 1403 1404 # Look to see if this transition code is unique. 1405 if case not in cases: 1406 cases[case] = [] 1407 1408 cases[case].append(case_string) 1409 1410 # Walk through all of the unique code blocks and spit out the 1411 # corresponding case statement elements 1412 for case,transitions in cases.iteritems(): 1413 # Iterative over all the multiple transitions that share 1414 # the same code 1415 for trans in transitions: 1416 code(' case HASH_FUN($trans):') 1417 code(' $case\n') 1418 1419 code(''' 1420 default: 1421 panic("Invalid transition\\n" 1422 "%s time: %d addr: %s event: %s state: %s\\n", 1423 name(), curCycle(), addr, event, state); 1424 } 1425 1426 return TransitionResult_Valid; 1427} 1428''') 1429 code.write(path, "%s_Transitions.cc" % self.ident) 1430 1431 1432 # ************************** 1433 # ******* HTML Files ******* 1434 # ************************** 1435 def frameRef(self, click_href, click_target, over_href, over_num, text): 1436 code = self.symtab.codeFormatter(fix_newlines=False) 1437 code("""<A href=\"$click_href\" target=\"$click_target\" onmouseover=\" 1438 if (parent.frames[$over_num].location != parent.location + '$over_href') { 1439 parent.frames[$over_num].location='$over_href' 1440 }\"> 1441 ${{html.formatShorthand(text)}} 1442 </A>""") 1443 return str(code) 1444 1445 def writeHTMLFiles(self, path): 1446 # Create table with no row hilighted 1447 self.printHTMLTransitions(path, None) 1448 1449 # Generate transition tables 1450 for state in self.states.itervalues(): 1451 self.printHTMLTransitions(path, state) 1452 1453 # Generate action descriptions 1454 for action in self.actions.itervalues(): 1455 name = "%s_action_%s.html" % (self.ident, action.ident) 1456 code = html.createSymbol(action, "Action") 1457 code.write(path, name) 1458 1459 # Generate state descriptions 1460 for state in self.states.itervalues(): 1461 name = "%s_State_%s.html" % (self.ident, state.ident) 1462 code = html.createSymbol(state, "State") 1463 code.write(path, name) 1464 1465 # Generate event descriptions 1466 for event in self.events.itervalues(): 1467 name = "%s_Event_%s.html" % (self.ident, event.ident) 1468 code = html.createSymbol(event, "Event") 1469 code.write(path, name) 1470 1471 def printHTMLTransitions(self, path, active_state): 1472 code = self.symtab.codeFormatter() 1473 1474 code(''' 1475<HTML> 1476<BODY link="blue" vlink="blue"> 1477 1478<H1 align="center">${{html.formatShorthand(self.short)}}: 1479''') 1480 code.indent() 1481 for i,machine in enumerate(self.symtab.getAllType(StateMachine)): 1482 mid = machine.ident 1483 if i != 0: 1484 extra = " - " 1485 else: 1486 extra = "" 1487 if machine == self: 1488 code('$extra$mid') 1489 else: 1490 code('$extra<A target="Table" href="${mid}_table.html">$mid</A>') 1491 code.dedent() 1492 1493 code(""" 1494</H1> 1495 1496<TABLE border=1> 1497<TR> 1498 <TH> </TH> 1499""") 1500 1501 for event in self.events.itervalues(): 1502 href = "%s_Event_%s.html" % (self.ident, event.ident) 1503 ref = self.frameRef(href, "Status", href, "1", event.short) 1504 code('<TH bgcolor=white>$ref</TH>') 1505 1506 code('</TR>') 1507 # -- Body of table 1508 for state in self.states.itervalues(): 1509 # -- Each row 1510 if state == active_state: 1511 color = "yellow" 1512 else: 1513 color = "white" 1514 1515 click = "%s_table_%s.html" % (self.ident, state.ident) 1516 over = "%s_State_%s.html" % (self.ident, state.ident) 1517 text = html.formatShorthand(state.short) 1518 ref = self.frameRef(click, "Table", over, "1", state.short) 1519 code(''' 1520<TR> 1521 <TH bgcolor=$color>$ref</TH> 1522''') 1523 1524 # -- One column for each event 1525 for event in self.events.itervalues(): 1526 trans = self.table.get((state,event), None) 1527 if trans is None: 1528 # This is the no transition case 1529 if state == active_state: 1530 color = "#C0C000" 1531 else: 1532 color = "lightgrey" 1533 1534 code('<TD bgcolor=$color> </TD>') 1535 continue 1536 1537 next = trans.nextState 1538 stall_action = False 1539 1540 # -- Get the actions 1541 for action in trans.actions: 1542 if action.ident == "z_stall" or \ 1543 action.ident == "zz_recycleMandatoryQueue": 1544 stall_action = True 1545 1546 # -- Print out "actions/next-state" 1547 if stall_action: 1548 if state == active_state: 1549 color = "#C0C000" 1550 else: 1551 color = "lightgrey" 1552 1553 elif active_state and next.ident == active_state.ident: 1554 color = "aqua" 1555 elif state == active_state: 1556 color = "yellow" 1557 else: 1558 color = "white" 1559 1560 code('<TD bgcolor=$color>') 1561 for action in trans.actions: 1562 href = "%s_action_%s.html" % (self.ident, action.ident) 1563 ref = self.frameRef(href, "Status", href, "1", 1564 action.short) 1565 code(' $ref') 1566 if next != state: 1567 if trans.actions: 1568 code('/') 1569 click = "%s_table_%s.html" % (self.ident, next.ident) 1570 over = "%s_State_%s.html" % (self.ident, next.ident) 1571 ref = self.frameRef(click, "Table", over, "1", next.short) 1572 code("$ref") 1573 code("</TD>") 1574 1575 # -- Each row 1576 if state == active_state: 1577 color = "yellow" 1578 else: 1579 color = "white" 1580 1581 click = "%s_table_%s.html" % (self.ident, state.ident) 1582 over = "%s_State_%s.html" % (self.ident, state.ident) 1583 ref = self.frameRef(click, "Table", over, "1", state.short) 1584 code(''' 1585 <TH bgcolor=$color>$ref</TH> 1586</TR> 1587''') 1588 code(''' 1589<!- Column footer-> 1590<TR> 1591 <TH> </TH> 1592''') 1593 1594 for event in self.events.itervalues(): 1595 href = "%s_Event_%s.html" % (self.ident, event.ident) 1596 ref = self.frameRef(href, "Status", href, "1", event.short) 1597 code('<TH bgcolor=white>$ref</TH>') 1598 code(''' 1599</TR> 1600</TABLE> 1601</BODY></HTML> 1602''') 1603 1604 1605 if active_state: 1606 name = "%s_table_%s.html" % (self.ident, active_state.ident) 1607 else: 1608 name = "%s_table.html" % self.ident 1609 code.write(path, name) 1610 1611__all__ = [ "StateMachine" ] 1612