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