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