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