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