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