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