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