StateMachine.py revision 10307:6df951dcd7d9
12600SN/A# Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
22600SN/A# Copyright (c) 2009 The Hewlett-Packard Development Company
32600SN/A# All rights reserved.
42600SN/A#
52600SN/A# Redistribution and use in source and binary forms, with or without
62600SN/A# modification, are permitted provided that the following conditions are
72600SN/A# met: redistributions of source code must retain the above copyright
82600SN/A# notice, this list of conditions and the following disclaimer;
92600SN/A# redistributions in binary form must reproduce the above copyright
102600SN/A# notice, this list of conditions and the following disclaimer in the
112600SN/A# documentation and/or other materials provided with the distribution;
122600SN/A# neither the name of the copyright holders nor the names of its
132600SN/A# contributors may be used to endorse or promote products derived from
142600SN/A# this software without specific prior written permission.
152600SN/A#
162600SN/A# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
172600SN/A# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
182600SN/A# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
192600SN/A# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
202600SN/A# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
212600SN/A# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
222600SN/A# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
232600SN/A# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
242600SN/A# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
252600SN/A# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
262600SN/A# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
272665Ssaidi@eecs.umich.edu
282665Ssaidi@eecs.umich.edufrom m5.util import orderdict
292600SN/A
302600SN/Afrom slicc.symbols.Symbol import Symbol
312600SN/Afrom slicc.symbols.Var import Var
322600SN/Aimport slicc.generate.html as html
332600SN/Aimport re
342600SN/A
352600SN/Apython_class_map = {
362680Sktlim@umich.edu                    "int": "Int",
372600SN/A                    "uint32_t" : "UInt32",
382600SN/A                    "std::string": "String",
392600SN/A                    "bool": "Bool",
402600SN/A                    "CacheMemory": "RubyCache",
412600SN/A                    "WireBuffer": "RubyWireBuffer",
422600SN/A                    "Sequencer": "RubySequencer",
432600SN/A                    "DirectoryMemory": "RubyDirectoryMemory",
442600SN/A                    "MemoryControl": "MemoryControl",
452600SN/A                    "DMASequencer": "DMASequencer",
462600SN/A                    "Prefetcher":"Prefetcher",
472600SN/A                    "Cycles":"Cycles",
483114Sgblack@eecs.umich.edu                   }
492680Sktlim@umich.edu
502600SN/Aclass StateMachine(Symbol):
512680Sktlim@umich.edu    def __init__(self, symtab, ident, location, pairs, config_parameters):
522600SN/A        super(StateMachine, self).__init__(symtab, ident, location, pairs)
532600SN/A        self.table = None
542600SN/A        self.config_parameters = config_parameters
552600SN/A        self.prefetchers = []
562600SN/A
572600SN/A        for param in config_parameters:
582600SN/A            if param.pointer:
592680Sktlim@umich.edu                var = Var(symtab, param.name, location, param.type_ast.type,
602600SN/A                          "(*m_%s_ptr)" % param.name, {}, self)
612600SN/A            else:
622600SN/A                var = Var(symtab, param.name, location, param.type_ast.type,
632600SN/A                          "m_%s" % param.name, {}, self)
642600SN/A            self.symtab.registerSym(param.name, var)
652600SN/A            if str(param.type_ast.type) == "Prefetcher":
662600SN/A                self.prefetchers.append(var)
672600SN/A
682600SN/A        self.states = orderdict()
692600SN/A        self.events = orderdict()
702600SN/A        self.actions = orderdict()
712600SN/A        self.request_types = orderdict()
722600SN/A        self.transitions = []
732600SN/A        self.in_ports = []
742600SN/A        self.functions = []
752600SN/A        self.objects = []
762600SN/A        self.TBEType   = None
772600SN/A        self.EntryType = None
782600SN/A
792600SN/A    def __repr__(self):
802600SN/A        return "[StateMachine: %s]" % self.ident
812600SN/A
822600SN/A    def addState(self, state):
832600SN/A        assert self.table is None
842600SN/A        self.states[state.ident] = state
852600SN/A
862600SN/A    def addEvent(self, event):
872600SN/A        assert self.table is None
882600SN/A        self.events[event.ident] = event
892600SN/A
902600SN/A    def addAction(self, action):
912600SN/A        assert self.table is None
922600SN/A
932600SN/A        # Check for duplicate action
942600SN/A        for other in self.actions.itervalues():
952600SN/A            if action.ident == other.ident:
962600SN/A                action.warning("Duplicate action definition: %s" % action.ident)
972600SN/A                action.error("Duplicate action definition: %s" % action.ident)
982600SN/A            if action.short == other.short:
992600SN/A                other.warning("Duplicate action shorthand: %s" % other.ident)
1002600SN/A                other.warning("    shorthand = %s" % other.short)
1012600SN/A                action.warning("Duplicate action shorthand: %s" % action.ident)
1022600SN/A                action.error("    shorthand = %s" % action.short)
1032600SN/A
1042600SN/A        self.actions[action.ident] = action
1052600SN/A
1062600SN/A    def addRequestType(self, request_type):
1072600SN/A        assert self.table is None
1082600SN/A        self.request_types[request_type.ident] = request_type
1092600SN/A
1102600SN/A    def addTransition(self, trans):
1112600SN/A        assert self.table is None
1122600SN/A        self.transitions.append(trans)
1132600SN/A
1142600SN/A    def addInPort(self, var):
1152600SN/A        self.in_ports.append(var)
1162600SN/A
1172600SN/A    def addFunc(self, func):
1182600SN/A        # register func in the symbol table
1192600SN/A        self.symtab.registerSym(str(func), func)
1202600SN/A        self.functions.append(func)
1212600SN/A
1222600SN/A    def addObject(self, obj):
1232600SN/A        self.symtab.registerSym(str(obj), obj)
1242600SN/A        self.objects.append(obj)
1252600SN/A
1262600SN/A    def addType(self, type):
1272600SN/A        type_ident = '%s' % type.c_ident
1282600SN/A
1292600SN/A        if type_ident == "%s_TBE" %self.ident:
1302600SN/A            if self.TBEType != None:
1312600SN/A                self.error("Multiple Transaction Buffer types in a " \
1322600SN/A                           "single machine.");
1332600SN/A            self.TBEType = type
1342600SN/A
1352600SN/A        elif "interface" in type and "AbstractCacheEntry" == type["interface"]:
1362600SN/A            if self.EntryType != None:
1372600SN/A                self.error("Multiple AbstractCacheEntry types in a " \
1382600SN/A                           "single machine.");
1392600SN/A            self.EntryType = type
1402600SN/A
1412600SN/A    # Needs to be called before accessing the table
1422600SN/A    def buildTable(self):
1432600SN/A        assert self.table is None
1442600SN/A
1452600SN/A        table = {}
1462600SN/A
1472600SN/A        for trans in self.transitions:
1482600SN/A            # Track which actions we touch so we know if we use them
1492600SN/A            # all -- really this should be done for all symbols as
1502600SN/A            # part of the symbol table, then only trigger it for
1512600SN/A            # Actions, States, Events, etc.
1522600SN/A
1532600SN/A            for action in trans.actions:
1542600SN/A                action.used = True
1552600SN/A
1562600SN/A            index = (trans.state, trans.event)
1572600SN/A            if index in table:
1582600SN/A                table[index].warning("Duplicate transition: %s" % table[index])
1592600SN/A                trans.error("Duplicate transition: %s" % trans)
1602600SN/A            table[index] = trans
1612600SN/A
1622600SN/A        # Look at all actions to make sure we used them all
1632600SN/A        for action in self.actions.itervalues():
1642600SN/A            if not action.used:
1652600SN/A                error_msg = "Unused action: %s" % action.ident
1662600SN/A                if "desc" in action:
1672600SN/A                    error_msg += ", "  + action.desc
1682600SN/A                action.warning(error_msg)
1692600SN/A        self.table = table
1702600SN/A
1712600SN/A    def writeCodeFiles(self, path, includes):
1722600SN/A        self.printControllerPython(path)
1732600SN/A        self.printControllerHH(path)
1742600SN/A        self.printControllerCC(path, includes)
1752600SN/A        self.printCSwitch(path)
1762600SN/A        self.printCWakeup(path, includes)
1772600SN/A
1782600SN/A    def printControllerPython(self, path):
1792600SN/A        code = self.symtab.codeFormatter()
1802600SN/A        ident = self.ident
1812600SN/A        py_ident = "%s_Controller" % ident
1822600SN/A        c_ident = "%s_Controller" % self.ident
1832600SN/A        code('''
1842600SN/Afrom m5.params import *
1852600SN/Afrom m5.SimObject import SimObject
1862600SN/Afrom Controller import RubyController
1872600SN/A
1882600SN/Aclass $py_ident(RubyController):
1892600SN/A    type = '$py_ident'
1902600SN/A    cxx_header = 'mem/protocol/${c_ident}.hh'
1912600SN/A''')
1922600SN/A        code.indent()
1932600SN/A        for param in self.config_parameters:
1942600SN/A            dflt_str = ''
1952600SN/A            if param.default is not None:
1962600SN/A                dflt_str = str(param.default) + ', '
1972600SN/A            if python_class_map.has_key(param.type_ast.type.c_ident):
1982600SN/A                python_type = python_class_map[param.type_ast.type.c_ident]
1992600SN/A                code('${{param.name}} = Param.${{python_type}}(${dflt_str}"")')
2002600SN/A            else:
2012600SN/A                self.error("Unknown c++ to python class conversion for c++ " \
2022600SN/A                           "type: '%s'. Please update the python_class_map " \
2032600SN/A                           "in StateMachine.py", param.type_ast.type.c_ident)
2042600SN/A        code.dedent()
2052600SN/A        code.write(path, '%s.py' % py_ident)
2062600SN/A
2072600SN/A
2082600SN/A    def printControllerHH(self, path):
2092600SN/A        '''Output the method declarations for the class declaration'''
2102600SN/A        code = self.symtab.codeFormatter()
2112600SN/A        ident = self.ident
2122600SN/A        c_ident = "%s_Controller" % self.ident
2132600SN/A
2142600SN/A        code('''
2152600SN/A/** \\file $c_ident.hh
2162600SN/A *
2172600SN/A * Auto generated C++ code started by $__file__:$__line__
2182600SN/A * Created by slicc definition of Module "${{self.short}}"
2192600SN/A */
2202600SN/A
2212600SN/A#ifndef __${ident}_CONTROLLER_HH__
2222600SN/A#define __${ident}_CONTROLLER_HH__
2232600SN/A
2242600SN/A#include <iostream>
2252600SN/A#include <sstream>
2262600SN/A#include <string>
2272600SN/A
2282600SN/A#include "mem/protocol/TransitionResult.hh"
2292600SN/A#include "mem/protocol/Types.hh"
2302600SN/A#include "mem/ruby/common/Consumer.hh"
2312600SN/A#include "mem/ruby/common/Global.hh"
2322600SN/A#include "mem/ruby/slicc_interface/AbstractController.hh"
2332600SN/A#include "params/$c_ident.hh"
2342600SN/A''')
2352600SN/A
2362600SN/A        seen_types = set()
2372600SN/A        has_peer = False
2382600SN/A        for var in self.objects:
2392600SN/A            if var.type.ident not in seen_types and not var.type.isPrimitive:
2402600SN/A                code('#include "mem/protocol/${{var.type.c_ident}}.hh"')
2412600SN/A            if "network" in var and "physical_network" in var:
2422600SN/A                has_peer = True
2432600SN/A            seen_types.add(var.type.ident)
2442600SN/A
2452600SN/A        # for adding information to the protocol debug trace
2462600SN/A        code('''
2472600SN/Aextern std::stringstream ${ident}_transitionComment;
2482600SN/A
2492600SN/Aclass $c_ident : public AbstractController
2502600SN/A{
2512600SN/A  public:
2522600SN/A    typedef ${c_ident}Params Params;
2532600SN/A    $c_ident(const Params *p);
2542600SN/A    static int getNumControllers();
2552600SN/A    void init();
2562600SN/A    MessageBuffer* getMandatoryQueue() const;
2572600SN/A
2582600SN/A    void print(std::ostream& out) const;
2592600SN/A    void wakeup();
2602600SN/A    void resetStats();
2612600SN/A    void regStats();
2622600SN/A    void collateStats();
2632600SN/A
2642600SN/A    void recordCacheTrace(int cntrl, CacheRecorder* tr);
2652600SN/A    Sequencer* getSequencer() const;
2662600SN/A
2672600SN/A    bool functionalReadBuffers(PacketPtr&);
2682600SN/A    uint32_t functionalWriteBuffers(PacketPtr&);
2692600SN/A
2702600SN/A    void countTransition(${ident}_State state, ${ident}_Event event);
2712600SN/A    void possibleTransition(${ident}_State state, ${ident}_Event event);
2722600SN/A    uint64 getEventCount(${ident}_Event event);
2732600SN/A    bool isPossible(${ident}_State state, ${ident}_Event event);
2742600SN/A    uint64 getTransitionCount(${ident}_State state, ${ident}_Event event);
2752600SN/A
2762600SN/Aprivate:
2772600SN/A''')
2782600SN/A
2792600SN/A        code.indent()
2802600SN/A        # added by SS
2812600SN/A        for param in self.config_parameters:
2822600SN/A            if param.pointer:
2832600SN/A                code('${{param.type_ast.type}}* m_${{param.ident}}_ptr;')
2842600SN/A            else:
2852600SN/A                code('${{param.type_ast.type}} m_${{param.ident}};')
2862600SN/A
2872600SN/A        code('''
2882600SN/ATransitionResult doTransition(${ident}_Event event,
2892600SN/A''')
2902600SN/A
2912600SN/A        if self.EntryType != None:
2922600SN/A            code('''
2932600SN/A                              ${{self.EntryType.c_ident}}* m_cache_entry_ptr,
2942600SN/A''')
2952600SN/A        if self.TBEType != None:
2962600SN/A            code('''
2972600SN/A                              ${{self.TBEType.c_ident}}* m_tbe_ptr,
2982600SN/A''')
2992600SN/A
3002600SN/A        code('''
3012600SN/A                              const Address addr);
3022600SN/A
3032600SN/ATransitionResult doTransitionWorker(${ident}_Event event,
3042600SN/A                                    ${ident}_State state,
3052600SN/A                                    ${ident}_State& next_state,
3062600SN/A''')
3072600SN/A
3082600SN/A        if self.TBEType != None:
3092600SN/A            code('''
3102600SN/A                                    ${{self.TBEType.c_ident}}*& m_tbe_ptr,
3112600SN/A''')
3122600SN/A        if self.EntryType != None:
3132600SN/A            code('''
3142600SN/A                                    ${{self.EntryType.c_ident}}*& m_cache_entry_ptr,
3152600SN/A''')
3162600SN/A
3172600SN/A        code('''
3182600SN/A                                    const Address& addr);
3192600SN/A
3202600SN/Aint m_counters[${ident}_State_NUM][${ident}_Event_NUM];
3212600SN/Aint m_event_counters[${ident}_Event_NUM];
3222600SN/Abool m_possible[${ident}_State_NUM][${ident}_Event_NUM];
3232600SN/A
3242600SN/Astatic std::vector<Stats::Vector *> eventVec;
3252600SN/Astatic std::vector<std::vector<Stats::Vector *> > transVec;
3262600SN/Astatic int m_num_controllers;
3272600SN/A
3282600SN/A// Internal functions
3292600SN/A''')
3302600SN/A
3313114Sgblack@eecs.umich.edu        for func in self.functions:
3323114Sgblack@eecs.umich.edu            proto = func.prototype
3333114Sgblack@eecs.umich.edu            if proto:
3343114Sgblack@eecs.umich.edu                code('$proto')
3353114Sgblack@eecs.umich.edu
3363114Sgblack@eecs.umich.edu        if has_peer:
3373114Sgblack@eecs.umich.edu            code('void getQueuesFromPeer(AbstractController *);')
3382600SN/A        if self.EntryType != None:
3393114Sgblack@eecs.umich.edu            code('''
3403114Sgblack@eecs.umich.edu
3412600SN/A// Set and Reset for cache_entry variable
3422600SN/Avoid set_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, AbstractCacheEntry* m_new_cache_entry);
3432600SN/Avoid unset_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr);
3442600SN/A''')
3452600SN/A
3462600SN/A        if self.TBEType != None:
3472600SN/A            code('''
3482600SN/A
3492600SN/A// Set and Reset for tbe variable
3502600SN/Avoid set_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${ident}_TBE* m_new_tbe);
3512600SN/Avoid unset_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr);
3522600SN/A''')
3532600SN/A
3542600SN/A        # Prototype the actions that the controller can take
3552600SN/A        code('''
3562600SN/A
357// Actions
358''')
359        if self.TBEType != None and self.EntryType != None:
360            for action in self.actions.itervalues():
361                code('/** \\brief ${{action.desc}} */')
362                code('void ${{action.ident}}(${{self.TBEType.c_ident}}*& '
363                     'm_tbe_ptr, ${{self.EntryType.c_ident}}*& '
364                     'm_cache_entry_ptr, const Address& addr);')
365        elif self.TBEType != None:
366            for action in self.actions.itervalues():
367                code('/** \\brief ${{action.desc}} */')
368                code('void ${{action.ident}}(${{self.TBEType.c_ident}}*& '
369                     '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}}*& '
374                     'm_cache_entry_ptr, const Address& addr);')
375        else:
376            for action in self.actions.itervalues():
377                code('/** \\brief ${{action.desc}} */')
378                code('void ${{action.ident}}(const Address& addr);')
379
380        # the controller internal variables
381        code('''
382
383// Objects
384''')
385        for var in self.objects:
386            th = var.get("template", "")
387            code('${{var.type.c_ident}}$th* m_${{var.ident}}_ptr;')
388
389        code.dedent()
390        code('};')
391        code('#endif // __${ident}_CONTROLLER_H__')
392        code.write(path, '%s.hh' % c_ident)
393
394    def printControllerCC(self, path, includes):
395        '''Output the actions for performing the actions'''
396
397        code = self.symtab.codeFormatter()
398        ident = self.ident
399        c_ident = "%s_Controller" % self.ident
400        has_peer = False
401
402        code('''
403/** \\file $c_ident.cc
404 *
405 * Auto generated C++ code started by $__file__:$__line__
406 * Created by slicc definition of Module "${{self.short}}"
407 */
408
409#include <sys/types.h>
410#include <unistd.h>
411
412#include <cassert>
413#include <sstream>
414#include <string>
415
416#include "base/compiler.hh"
417#include "base/cprintf.hh"
418#include "debug/RubyGenerated.hh"
419#include "debug/RubySlicc.hh"
420#include "mem/protocol/${ident}_Controller.hh"
421#include "mem/protocol/${ident}_Event.hh"
422#include "mem/protocol/${ident}_State.hh"
423#include "mem/protocol/Types.hh"
424#include "mem/ruby/common/Global.hh"
425#include "mem/ruby/system/System.hh"
426''')
427        for include_path in includes:
428            code('#include "${{include_path}}"')
429
430        code('''
431
432using namespace std;
433''')
434
435        # include object classes
436        seen_types = set()
437        for var in self.objects:
438            if var.type.ident not in seen_types and not var.type.isPrimitive:
439                code('#include "mem/protocol/${{var.type.c_ident}}.hh"')
440            seen_types.add(var.type.ident)
441
442        num_in_ports = len(self.in_ports)
443
444        code('''
445$c_ident *
446${c_ident}Params::create()
447{
448    return new $c_ident(this);
449}
450
451int $c_ident::m_num_controllers = 0;
452std::vector<Stats::Vector *>  $c_ident::eventVec;
453std::vector<std::vector<Stats::Vector *> >  $c_ident::transVec;
454
455// for adding information to the protocol debug trace
456stringstream ${ident}_transitionComment;
457
458#ifndef NDEBUG
459#define APPEND_TRANSITION_COMMENT(str) (${ident}_transitionComment << str)
460#else
461#define APPEND_TRANSITION_COMMENT(str) do {} while (0)
462#endif
463
464/** \\brief constructor */
465$c_ident::$c_ident(const Params *p)
466    : AbstractController(p)
467{
468    m_machineID.type = MachineType_${ident};
469    m_machineID.num = m_version;
470    m_num_controllers++;
471
472    m_in_ports = $num_in_ports;
473''')
474        code.indent()
475
476        #
477        # After initializing the universal machine parameters, initialize the
478        # this machines config parameters.  Also if these configuration params
479        # include a sequencer, connect the it to the controller.
480        #
481        for param in self.config_parameters:
482            if param.pointer:
483                code('m_${{param.name}}_ptr = p->${{param.name}};')
484            else:
485                code('m_${{param.name}} = p->${{param.name}};')
486            if re.compile("sequencer").search(param.name):
487                code('m_${{param.name}}_ptr->setController(this);')
488
489        for var in self.objects:
490            if var.ident.find("mandatoryQueue") >= 0:
491                code('''
492m_${{var.ident}}_ptr = new ${{var.type.c_ident}}();
493m_${{var.ident}}_ptr->setReceiver(this);
494''')
495            else:
496                if "network" in var and "physical_network" in var and \
497                   var["network"] == "To":
498                    has_peer = True
499                    code('''
500m_${{var.ident}}_ptr = new ${{var.type.c_ident}}();
501peerQueueMap[${{var["physical_network"]}}] = m_${{var.ident}}_ptr;
502m_${{var.ident}}_ptr->setSender(this);
503''')
504
505        code('''
506if (p->peer != NULL)
507    connectWithPeer(p->peer);
508
509for (int state = 0; state < ${ident}_State_NUM; state++) {
510    for (int event = 0; event < ${ident}_Event_NUM; event++) {
511        m_possible[state][event] = false;
512        m_counters[state][event] = 0;
513    }
514}
515for (int event = 0; event < ${ident}_Event_NUM; event++) {
516    m_event_counters[event] = 0;
517}
518''')
519        code.dedent()
520        code('''
521}
522
523void
524$c_ident::init()
525{
526    MachineType machine_type = string_to_MachineType("${{var.machine.ident}}");
527    int base M5_VAR_USED = MachineType_base_number(machine_type);
528
529    // initialize objects
530
531''')
532
533        code.indent()
534        for var in self.objects:
535            vtype = var.type
536            vid = "m_%s_ptr" % var.ident
537            if "network" not in var:
538                # Not a network port object
539                if "primitive" in vtype:
540                    code('$vid = new ${{vtype.c_ident}};')
541                    if "default" in var:
542                        code('(*$vid) = ${{var["default"]}};')
543                else:
544                    # Normal Object
545                    if var.ident.find("mandatoryQueue") < 0:
546                        th = var.get("template", "")
547                        expr = "%s  = new %s%s" % (vid, vtype.c_ident, th)
548                        args = ""
549                        if "non_obj" not in vtype and not vtype.isEnumeration:
550                            args = var.get("constructor", "")
551                        code('$expr($args);')
552
553                    code('assert($vid != NULL);')
554
555                    if "default" in var:
556                        code('*$vid = ${{var["default"]}}; // Object default')
557                    elif "default" in vtype:
558                        comment = "Type %s default" % vtype.ident
559                        code('*$vid = ${{vtype["default"]}}; // $comment')
560
561                    # Set ordering
562                    if "ordered" in var:
563                        # A buffer
564                        code('$vid->setOrdering(${{var["ordered"]}});')
565
566                    # Set randomization
567                    if "random" in var:
568                        # A buffer
569                        code('$vid->setRandomization(${{var["random"]}});')
570
571                    # Set Priority
572                    if vtype.isBuffer and "rank" in var:
573                        code('$vid->setPriority(${{var["rank"]}});')
574
575                    # Set sender and receiver for trigger queue
576                    if var.ident.find("triggerQueue") >= 0:
577                        code('$vid->setSender(this);')
578                        code('$vid->setReceiver(this);')
579                    elif vtype.c_ident == "TimerTable":
580                        code('$vid->setClockObj(this);')
581                    elif var.ident.find("optionalQueue") >= 0:
582                        code('$vid->setSender(this);')
583                        code('$vid->setReceiver(this);')
584
585            else:
586                # Network port object
587                network = var["network"]
588                ordered =  var["ordered"]
589
590                if "virtual_network" in var:
591                    vnet = var["virtual_network"]
592                    vnet_type = var["vnet_type"]
593
594                    assert var.machine is not None
595                    code('''
596$vid = m_net_ptr->get${network}NetQueue(m_version + base, $ordered, $vnet, "$vnet_type");
597assert($vid != NULL);
598''')
599
600                    # Set the end
601                    if network == "To":
602                        code('$vid->setSender(this);')
603                    else:
604                        code('$vid->setReceiver(this);')
605
606                # Set ordering
607                if "ordered" in var:
608                    # A buffer
609                    code('$vid->setOrdering(${{var["ordered"]}});')
610
611                # Set randomization
612                if "random" in var:
613                    # A buffer
614                    code('$vid->setRandomization(${{var["random"]}});')
615
616                # Set Priority
617                if "rank" in var:
618                    code('$vid->setPriority(${{var["rank"]}})')
619
620                # Set buffer size
621                if vtype.isBuffer:
622                    code('''
623if (m_buffer_size > 0) {
624    $vid->resize(m_buffer_size);
625}
626''')
627
628                # set description (may be overriden later by port def)
629                code('''
630$vid->setDescription("[Version " + to_string(m_version) + ", ${ident}, name=${{var.ident}}]");
631
632''')
633
634            if vtype.isBuffer:
635                if "recycle_latency" in var:
636                    code('$vid->setRecycleLatency( ' \
637                         'Cycles(${{var["recycle_latency"]}}));')
638                else:
639                    code('$vid->setRecycleLatency(m_recycle_latency);')
640
641        # Set the prefetchers
642        code()
643        for prefetcher in self.prefetchers:
644            code('${{prefetcher.code}}.setController(this);')
645
646        code()
647        for port in self.in_ports:
648            # Set the queue consumers
649            code('${{port.code}}.setConsumer(this);')
650            # Set the queue descriptions
651            code('${{port.code}}.setDescription("[Version " + to_string(m_version) + ", $ident, $port]");')
652
653        # Initialize the transition profiling
654        code()
655        for trans in self.transitions:
656            # Figure out if we stall
657            stall = False
658            for action in trans.actions:
659                if action.ident == "z_stall":
660                    stall = True
661
662            # Only possible if it is not a 'z' case
663            if not stall:
664                state = "%s_State_%s" % (self.ident, trans.state.ident)
665                event = "%s_Event_%s" % (self.ident, trans.event.ident)
666                code('possibleTransition($state, $event);')
667
668        code.dedent()
669        code('''
670    AbstractController::init();
671    resetStats();
672}
673''')
674
675        mq_ident = "NULL"
676        for port in self.in_ports:
677            if port.code.find("mandatoryQueue_ptr") >= 0:
678                mq_ident = "m_mandatoryQueue_ptr"
679
680        seq_ident = "NULL"
681        for param in self.config_parameters:
682            if param.name == "sequencer":
683                assert(param.pointer)
684                seq_ident = "m_%s_ptr" % param.name
685
686        code('''
687
688void
689$c_ident::regStats()
690{
691    AbstractController::regStats();
692
693    if (m_version == 0) {
694        for (${ident}_Event event = ${ident}_Event_FIRST;
695             event < ${ident}_Event_NUM; ++event) {
696            Stats::Vector *t = new Stats::Vector();
697            t->init(m_num_controllers);
698            t->name(g_system_ptr->name() + ".${c_ident}." +
699                ${ident}_Event_to_string(event));
700            t->flags(Stats::pdf | Stats::total | Stats::oneline |
701                     Stats::nozero);
702
703            eventVec.push_back(t);
704        }
705
706        for (${ident}_State state = ${ident}_State_FIRST;
707             state < ${ident}_State_NUM; ++state) {
708
709            transVec.push_back(std::vector<Stats::Vector *>());
710
711            for (${ident}_Event event = ${ident}_Event_FIRST;
712                 event < ${ident}_Event_NUM; ++event) {
713
714                Stats::Vector *t = new Stats::Vector();
715                t->init(m_num_controllers);
716                t->name(g_system_ptr->name() + ".${c_ident}." +
717                        ${ident}_State_to_string(state) +
718                        "." + ${ident}_Event_to_string(event));
719
720                t->flags(Stats::pdf | Stats::total | Stats::oneline |
721                         Stats::nozero);
722                transVec[state].push_back(t);
723            }
724        }
725    }
726}
727
728void
729$c_ident::collateStats()
730{
731    for (${ident}_Event event = ${ident}_Event_FIRST;
732         event < ${ident}_Event_NUM; ++event) {
733        for (unsigned int i = 0; i < m_num_controllers; ++i) {
734            std::map<uint32_t, AbstractController *>::iterator it =
735                                g_abs_controls[MachineType_${ident}].find(i);
736            assert(it != g_abs_controls[MachineType_${ident}].end());
737            (*eventVec[event])[i] =
738                (($c_ident *)(*it).second)->getEventCount(event);
739        }
740    }
741
742    for (${ident}_State state = ${ident}_State_FIRST;
743         state < ${ident}_State_NUM; ++state) {
744
745        for (${ident}_Event event = ${ident}_Event_FIRST;
746             event < ${ident}_Event_NUM; ++event) {
747
748            for (unsigned int i = 0; i < m_num_controllers; ++i) {
749                std::map<uint32_t, AbstractController *>::iterator it =
750                                g_abs_controls[MachineType_${ident}].find(i);
751                assert(it != g_abs_controls[MachineType_${ident}].end());
752                (*transVec[state][event])[i] =
753                    (($c_ident *)(*it).second)->getTransitionCount(state, event);
754            }
755        }
756    }
757}
758
759void
760$c_ident::countTransition(${ident}_State state, ${ident}_Event event)
761{
762    assert(m_possible[state][event]);
763    m_counters[state][event]++;
764    m_event_counters[event]++;
765}
766void
767$c_ident::possibleTransition(${ident}_State state,
768                             ${ident}_Event event)
769{
770    m_possible[state][event] = true;
771}
772
773uint64
774$c_ident::getEventCount(${ident}_Event event)
775{
776    return m_event_counters[event];
777}
778
779bool
780$c_ident::isPossible(${ident}_State state, ${ident}_Event event)
781{
782    return m_possible[state][event];
783}
784
785uint64
786$c_ident::getTransitionCount(${ident}_State state,
787                             ${ident}_Event event)
788{
789    return m_counters[state][event];
790}
791
792int
793$c_ident::getNumControllers()
794{
795    return m_num_controllers;
796}
797
798MessageBuffer*
799$c_ident::getMandatoryQueue() const
800{
801    return $mq_ident;
802}
803
804Sequencer*
805$c_ident::getSequencer() const
806{
807    return $seq_ident;
808}
809
810void
811$c_ident::print(ostream& out) const
812{
813    out << "[$c_ident " << m_version << "]";
814}
815
816void $c_ident::resetStats()
817{
818    for (int state = 0; state < ${ident}_State_NUM; state++) {
819        for (int event = 0; event < ${ident}_Event_NUM; event++) {
820            m_counters[state][event] = 0;
821        }
822    }
823
824    for (int event = 0; event < ${ident}_Event_NUM; event++) {
825        m_event_counters[event] = 0;
826    }
827
828    AbstractController::resetStats();
829}
830''')
831
832        if self.EntryType != None:
833            code('''
834
835// Set and Reset for cache_entry variable
836void
837$c_ident::set_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, AbstractCacheEntry* m_new_cache_entry)
838{
839  m_cache_entry_ptr = (${{self.EntryType.c_ident}}*)m_new_cache_entry;
840}
841
842void
843$c_ident::unset_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr)
844{
845  m_cache_entry_ptr = 0;
846}
847''')
848
849        if self.TBEType != None:
850            code('''
851
852// Set and Reset for tbe variable
853void
854$c_ident::set_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.TBEType.c_ident}}* m_new_tbe)
855{
856  m_tbe_ptr = m_new_tbe;
857}
858
859void
860$c_ident::unset_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr)
861{
862  m_tbe_ptr = NULL;
863}
864''')
865
866        code('''
867
868void
869$c_ident::recordCacheTrace(int cntrl, CacheRecorder* tr)
870{
871''')
872        #
873        # Record cache contents for all associated caches.
874        #
875        code.indent()
876        for param in self.config_parameters:
877            if param.type_ast.type.ident == "CacheMemory":
878                assert(param.pointer)
879                code('m_${{param.ident}}_ptr->recordCacheContents(cntrl, tr);')
880
881        code.dedent()
882        code('''
883}
884
885// Actions
886''')
887        if self.TBEType != None and self.EntryType != None:
888            for action in self.actions.itervalues():
889                if "c_code" not in action:
890                 continue
891
892                code('''
893/** \\brief ${{action.desc}} */
894void
895$c_ident::${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr)
896{
897    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
898    ${{action["c_code"]}}
899}
900
901''')
902        elif self.TBEType != None:
903            for action in self.actions.itervalues():
904                if "c_code" not in action:
905                 continue
906
907                code('''
908/** \\brief ${{action.desc}} */
909void
910$c_ident::${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, const Address& addr)
911{
912    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
913    ${{action["c_code"]}}
914}
915
916''')
917        elif self.EntryType != None:
918            for action in self.actions.itervalues():
919                if "c_code" not in action:
920                 continue
921
922                code('''
923/** \\brief ${{action.desc}} */
924void
925$c_ident::${{action.ident}}(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr)
926{
927    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
928    ${{action["c_code"]}}
929}
930
931''')
932        else:
933            for action in self.actions.itervalues():
934                if "c_code" not in action:
935                 continue
936
937                code('''
938/** \\brief ${{action.desc}} */
939void
940$c_ident::${{action.ident}}(const Address& addr)
941{
942    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
943    ${{action["c_code"]}}
944}
945
946''')
947        for func in self.functions:
948            code(func.generateCode())
949
950        # Function for functional reads from messages buffered in the controller
951        code('''
952bool
953$c_ident::functionalReadBuffers(PacketPtr& pkt)
954{
955''')
956        for var in self.objects:
957            vtype = var.type
958            if vtype.isBuffer:
959                vid = "m_%s_ptr" % var.ident
960                code('if ($vid->functionalRead(pkt)) { return true; }')
961        code('''
962                return false;
963}
964''')
965
966        # Function for functional writes to messages buffered in the controller
967        code('''
968uint32_t
969$c_ident::functionalWriteBuffers(PacketPtr& pkt)
970{
971    uint32_t num_functional_writes = 0;
972''')
973        for var in self.objects:
974            vtype = var.type
975            if vtype.isBuffer:
976                vid = "m_%s_ptr" % var.ident
977                code('num_functional_writes += $vid->functionalWrite(pkt);')
978        code('''
979    return num_functional_writes;
980}
981''')
982
983        # Check if this controller has a peer, if yes then write the
984        # function for connecting to the peer.
985        if has_peer:
986            code('''
987
988void
989$c_ident::getQueuesFromPeer(AbstractController *peer)
990{
991''')
992            for var in self.objects:
993                if "network" in var and "physical_network" in var and \
994                   var["network"] == "From":
995                    code('''
996m_${{var.ident}}_ptr = peer->getPeerQueue(${{var["physical_network"]}});
997assert(m_${{var.ident}}_ptr != NULL);
998m_${{var.ident}}_ptr->setReceiver(this);
999
1000''')
1001            code('}')
1002
1003        code.write(path, "%s.cc" % c_ident)
1004
1005    def printCWakeup(self, path, includes):
1006        '''Output the wakeup loop for the events'''
1007
1008        code = self.symtab.codeFormatter()
1009        ident = self.ident
1010
1011        outputRequest_types = True
1012        if len(self.request_types) == 0:
1013            outputRequest_types = False
1014
1015        code('''
1016// Auto generated C++ code started by $__file__:$__line__
1017// ${ident}: ${{self.short}}
1018
1019#include <sys/types.h>
1020#include <unistd.h>
1021
1022#include <cassert>
1023
1024#include "base/misc.hh"
1025#include "debug/RubySlicc.hh"
1026#include "mem/protocol/${ident}_Controller.hh"
1027#include "mem/protocol/${ident}_Event.hh"
1028#include "mem/protocol/${ident}_State.hh"
1029''')
1030
1031        if outputRequest_types:
1032            code('''#include "mem/protocol/${ident}_RequestType.hh"''')
1033
1034        code('''
1035#include "mem/protocol/Types.hh"
1036#include "mem/ruby/common/Global.hh"
1037#include "mem/ruby/system/System.hh"
1038''')
1039
1040
1041        for include_path in includes:
1042            code('#include "${{include_path}}"')
1043
1044        code('''
1045
1046using namespace std;
1047
1048void
1049${ident}_Controller::wakeup()
1050{
1051    int counter = 0;
1052    while (true) {
1053        // Some cases will put us into an infinite loop without this limit
1054        assert(counter <= m_transitions_per_cycle);
1055        if (counter == m_transitions_per_cycle) {
1056            // Count how often we are fully utilized
1057            m_fully_busy_cycles++;
1058
1059            // Wakeup in another cycle and try again
1060            scheduleEvent(Cycles(1));
1061            break;
1062        }
1063''')
1064
1065        code.indent()
1066        code.indent()
1067
1068        # InPorts
1069        #
1070        for port in self.in_ports:
1071            code.indent()
1072            code('// ${ident}InPort $port')
1073            if port.pairs.has_key("rank"):
1074                code('m_cur_in_port = ${{port.pairs["rank"]}};')
1075            else:
1076                code('m_cur_in_port = 0;')
1077            code('${{port["c_code_in_port"]}}')
1078            code.dedent()
1079
1080            code('')
1081
1082        code.dedent()
1083        code.dedent()
1084        code('''
1085        break;  // If we got this far, we have nothing left todo
1086    }
1087}
1088''')
1089
1090        code.write(path, "%s_Wakeup.cc" % self.ident)
1091
1092    def printCSwitch(self, path):
1093        '''Output switch statement for transition table'''
1094
1095        code = self.symtab.codeFormatter()
1096        ident = self.ident
1097
1098        code('''
1099// Auto generated C++ code started by $__file__:$__line__
1100// ${ident}: ${{self.short}}
1101
1102#include <cassert>
1103
1104#include "base/misc.hh"
1105#include "base/trace.hh"
1106#include "debug/ProtocolTrace.hh"
1107#include "debug/RubyGenerated.hh"
1108#include "mem/protocol/${ident}_Controller.hh"
1109#include "mem/protocol/${ident}_Event.hh"
1110#include "mem/protocol/${ident}_State.hh"
1111#include "mem/protocol/Types.hh"
1112#include "mem/ruby/common/Global.hh"
1113#include "mem/ruby/system/System.hh"
1114
1115#define HASH_FUN(state, event)  ((int(state)*${ident}_Event_NUM)+int(event))
1116
1117#define GET_TRANSITION_COMMENT() (${ident}_transitionComment.str())
1118#define CLEAR_TRANSITION_COMMENT() (${ident}_transitionComment.str(""))
1119
1120TransitionResult
1121${ident}_Controller::doTransition(${ident}_Event event,
1122''')
1123        if self.EntryType != None:
1124            code('''
1125                                  ${{self.EntryType.c_ident}}* m_cache_entry_ptr,
1126''')
1127        if self.TBEType != None:
1128            code('''
1129                                  ${{self.TBEType.c_ident}}* m_tbe_ptr,
1130''')
1131        code('''
1132                                  const Address addr)
1133{
1134''')
1135        code.indent()
1136
1137        if self.TBEType != None and self.EntryType != None:
1138            code('${ident}_State state = getState(m_tbe_ptr, m_cache_entry_ptr, addr);')
1139        elif self.TBEType != None:
1140            code('${ident}_State state = getState(m_tbe_ptr, addr);')
1141        elif self.EntryType != None:
1142            code('${ident}_State state = getState(m_cache_entry_ptr, addr);')
1143        else:
1144            code('${ident}_State state = getState(addr);')
1145
1146        code('''
1147${ident}_State next_state = state;
1148
1149DPRINTF(RubyGenerated, "%s, Time: %lld, state: %s, event: %s, addr: %s\\n",
1150        *this, curCycle(), ${ident}_State_to_string(state),
1151        ${ident}_Event_to_string(event), addr);
1152
1153TransitionResult result =
1154''')
1155        if self.TBEType != None and self.EntryType != None:
1156            code('doTransitionWorker(event, state, next_state, m_tbe_ptr, m_cache_entry_ptr, addr);')
1157        elif self.TBEType != None:
1158            code('doTransitionWorker(event, state, next_state, m_tbe_ptr, addr);')
1159        elif self.EntryType != None:
1160            code('doTransitionWorker(event, state, next_state, m_cache_entry_ptr, addr);')
1161        else:
1162            code('doTransitionWorker(event, state, next_state, addr);')
1163
1164        code('''
1165
1166if (result == TransitionResult_Valid) {
1167    DPRINTF(RubyGenerated, "next_state: %s\\n",
1168            ${ident}_State_to_string(next_state));
1169    countTransition(state, event);
1170
1171    DPRINTFR(ProtocolTrace, "%15d %3s %10s%20s %6s>%-6s %s %s\\n",
1172             curTick(), m_version, "${ident}",
1173             ${ident}_Event_to_string(event),
1174             ${ident}_State_to_string(state),
1175             ${ident}_State_to_string(next_state),
1176             addr, GET_TRANSITION_COMMENT());
1177
1178    CLEAR_TRANSITION_COMMENT();
1179''')
1180        if self.TBEType != None and self.EntryType != None:
1181            code('setState(m_tbe_ptr, m_cache_entry_ptr, addr, next_state);')
1182            code('setAccessPermission(m_cache_entry_ptr, addr, next_state);')
1183        elif self.TBEType != None:
1184            code('setState(m_tbe_ptr, addr, next_state);')
1185            code('setAccessPermission(addr, next_state);')
1186        elif self.EntryType != None:
1187            code('setState(m_cache_entry_ptr, addr, next_state);')
1188            code('setAccessPermission(m_cache_entry_ptr, addr, next_state);')
1189        else:
1190            code('setState(addr, next_state);')
1191            code('setAccessPermission(addr, next_state);')
1192
1193        code('''
1194} else if (result == TransitionResult_ResourceStall) {
1195    DPRINTFR(ProtocolTrace, "%15s %3s %10s%20s %6s>%-6s %s %s\\n",
1196             curTick(), m_version, "${ident}",
1197             ${ident}_Event_to_string(event),
1198             ${ident}_State_to_string(state),
1199             ${ident}_State_to_string(next_state),
1200             addr, "Resource Stall");
1201} else if (result == TransitionResult_ProtocolStall) {
1202    DPRINTF(RubyGenerated, "stalling\\n");
1203    DPRINTFR(ProtocolTrace, "%15s %3s %10s%20s %6s>%-6s %s %s\\n",
1204             curTick(), m_version, "${ident}",
1205             ${ident}_Event_to_string(event),
1206             ${ident}_State_to_string(state),
1207             ${ident}_State_to_string(next_state),
1208             addr, "Protocol Stall");
1209}
1210
1211return result;
1212''')
1213        code.dedent()
1214        code('''
1215}
1216
1217TransitionResult
1218${ident}_Controller::doTransitionWorker(${ident}_Event event,
1219                                        ${ident}_State state,
1220                                        ${ident}_State& next_state,
1221''')
1222
1223        if self.TBEType != None:
1224            code('''
1225                                        ${{self.TBEType.c_ident}}*& m_tbe_ptr,
1226''')
1227        if self.EntryType != None:
1228                  code('''
1229                                        ${{self.EntryType.c_ident}}*& m_cache_entry_ptr,
1230''')
1231        code('''
1232                                        const Address& addr)
1233{
1234    switch(HASH_FUN(state, event)) {
1235''')
1236
1237        # This map will allow suppress generating duplicate code
1238        cases = orderdict()
1239
1240        for trans in self.transitions:
1241            case_string = "%s_State_%s, %s_Event_%s" % \
1242                (self.ident, trans.state.ident, self.ident, trans.event.ident)
1243
1244            case = self.symtab.codeFormatter()
1245            # Only set next_state if it changes
1246            if trans.state != trans.nextState:
1247                ns_ident = trans.nextState.ident
1248                case('next_state = ${ident}_State_${ns_ident};')
1249
1250            actions = trans.actions
1251            request_types = trans.request_types
1252
1253            # Check for resources
1254            case_sorter = []
1255            res = trans.resources
1256            for key,val in res.iteritems():
1257                val = '''
1258if (!%s.areNSlotsAvailable(%s))
1259    return TransitionResult_ResourceStall;
1260''' % (key.code, val)
1261                case_sorter.append(val)
1262
1263            # Check all of the request_types for resource constraints
1264            for request_type in request_types:
1265                val = '''
1266if (!checkResourceAvailable(%s_RequestType_%s, addr)) {
1267    return TransitionResult_ResourceStall;
1268}
1269''' % (self.ident, request_type.ident)
1270                case_sorter.append(val)
1271
1272            # Emit the code sequences in a sorted order.  This makes the
1273            # output deterministic (without this the output order can vary
1274            # since Map's keys() on a vector of pointers is not deterministic
1275            for c in sorted(case_sorter):
1276                case("$c")
1277
1278            # Record access types for this transition
1279            for request_type in request_types:
1280                case('recordRequestType(${ident}_RequestType_${{request_type.ident}}, addr);')
1281
1282            # Figure out if we stall
1283            stall = False
1284            for action in actions:
1285                if action.ident == "z_stall":
1286                    stall = True
1287                    break
1288
1289            if stall:
1290                case('return TransitionResult_ProtocolStall;')
1291            else:
1292                if self.TBEType != None and self.EntryType != None:
1293                    for action in actions:
1294                        case('${{action.ident}}(m_tbe_ptr, m_cache_entry_ptr, addr);')
1295                elif self.TBEType != None:
1296                    for action in actions:
1297                        case('${{action.ident}}(m_tbe_ptr, addr);')
1298                elif self.EntryType != None:
1299                    for action in actions:
1300                        case('${{action.ident}}(m_cache_entry_ptr, addr);')
1301                else:
1302                    for action in actions:
1303                        case('${{action.ident}}(addr);')
1304                case('return TransitionResult_Valid;')
1305
1306            case = str(case)
1307
1308            # Look to see if this transition code is unique.
1309            if case not in cases:
1310                cases[case] = []
1311
1312            cases[case].append(case_string)
1313
1314        # Walk through all of the unique code blocks and spit out the
1315        # corresponding case statement elements
1316        for case,transitions in cases.iteritems():
1317            # Iterative over all the multiple transitions that share
1318            # the same code
1319            for trans in transitions:
1320                code('  case HASH_FUN($trans):')
1321            code('    $case\n')
1322
1323        code('''
1324      default:
1325        fatal("Invalid transition\\n"
1326              "%s time: %d addr: %s event: %s state: %s\\n",
1327              name(), curCycle(), addr, event, state);
1328    }
1329
1330    return TransitionResult_Valid;
1331}
1332''')
1333        code.write(path, "%s_Transitions.cc" % self.ident)
1334
1335
1336    # **************************
1337    # ******* HTML Files *******
1338    # **************************
1339    def frameRef(self, click_href, click_target, over_href, over_num, text):
1340        code = self.symtab.codeFormatter(fix_newlines=False)
1341        code("""<A href=\"$click_href\" target=\"$click_target\" onmouseover=\"
1342    if (parent.frames[$over_num].location != parent.location + '$over_href') {
1343        parent.frames[$over_num].location='$over_href'
1344    }\">
1345    ${{html.formatShorthand(text)}}
1346    </A>""")
1347        return str(code)
1348
1349    def writeHTMLFiles(self, path):
1350        # Create table with no row hilighted
1351        self.printHTMLTransitions(path, None)
1352
1353        # Generate transition tables
1354        for state in self.states.itervalues():
1355            self.printHTMLTransitions(path, state)
1356
1357        # Generate action descriptions
1358        for action in self.actions.itervalues():
1359            name = "%s_action_%s.html" % (self.ident, action.ident)
1360            code = html.createSymbol(action, "Action")
1361            code.write(path, name)
1362
1363        # Generate state descriptions
1364        for state in self.states.itervalues():
1365            name = "%s_State_%s.html" % (self.ident, state.ident)
1366            code = html.createSymbol(state, "State")
1367            code.write(path, name)
1368
1369        # Generate event descriptions
1370        for event in self.events.itervalues():
1371            name = "%s_Event_%s.html" % (self.ident, event.ident)
1372            code = html.createSymbol(event, "Event")
1373            code.write(path, name)
1374
1375    def printHTMLTransitions(self, path, active_state):
1376        code = self.symtab.codeFormatter()
1377
1378        code('''
1379<HTML>
1380<BODY link="blue" vlink="blue">
1381
1382<H1 align="center">${{html.formatShorthand(self.short)}}:
1383''')
1384        code.indent()
1385        for i,machine in enumerate(self.symtab.getAllType(StateMachine)):
1386            mid = machine.ident
1387            if i != 0:
1388                extra = " - "
1389            else:
1390                extra = ""
1391            if machine == self:
1392                code('$extra$mid')
1393            else:
1394                code('$extra<A target="Table" href="${mid}_table.html">$mid</A>')
1395        code.dedent()
1396
1397        code("""
1398</H1>
1399
1400<TABLE border=1>
1401<TR>
1402  <TH> </TH>
1403""")
1404
1405        for event in self.events.itervalues():
1406            href = "%s_Event_%s.html" % (self.ident, event.ident)
1407            ref = self.frameRef(href, "Status", href, "1", event.short)
1408            code('<TH bgcolor=white>$ref</TH>')
1409
1410        code('</TR>')
1411        # -- Body of table
1412        for state in self.states.itervalues():
1413            # -- Each row
1414            if state == active_state:
1415                color = "yellow"
1416            else:
1417                color = "white"
1418
1419            click = "%s_table_%s.html" % (self.ident, state.ident)
1420            over = "%s_State_%s.html" % (self.ident, state.ident)
1421            text = html.formatShorthand(state.short)
1422            ref = self.frameRef(click, "Table", over, "1", state.short)
1423            code('''
1424<TR>
1425  <TH bgcolor=$color>$ref</TH>
1426''')
1427
1428            # -- One column for each event
1429            for event in self.events.itervalues():
1430                trans = self.table.get((state,event), None)
1431                if trans is None:
1432                    # This is the no transition case
1433                    if state == active_state:
1434                        color = "#C0C000"
1435                    else:
1436                        color = "lightgrey"
1437
1438                    code('<TD bgcolor=$color>&nbsp;</TD>')
1439                    continue
1440
1441                next = trans.nextState
1442                stall_action = False
1443
1444                # -- Get the actions
1445                for action in trans.actions:
1446                    if action.ident == "z_stall" or \
1447                       action.ident == "zz_recycleMandatoryQueue":
1448                        stall_action = True
1449
1450                # -- Print out "actions/next-state"
1451                if stall_action:
1452                    if state == active_state:
1453                        color = "#C0C000"
1454                    else:
1455                        color = "lightgrey"
1456
1457                elif active_state and next.ident == active_state.ident:
1458                    color = "aqua"
1459                elif state == active_state:
1460                    color = "yellow"
1461                else:
1462                    color = "white"
1463
1464                code('<TD bgcolor=$color>')
1465                for action in trans.actions:
1466                    href = "%s_action_%s.html" % (self.ident, action.ident)
1467                    ref = self.frameRef(href, "Status", href, "1",
1468                                        action.short)
1469                    code('  $ref')
1470                if next != state:
1471                    if trans.actions:
1472                        code('/')
1473                    click = "%s_table_%s.html" % (self.ident, next.ident)
1474                    over = "%s_State_%s.html" % (self.ident, next.ident)
1475                    ref = self.frameRef(click, "Table", over, "1", next.short)
1476                    code("$ref")
1477                code("</TD>")
1478
1479            # -- Each row
1480            if state == active_state:
1481                color = "yellow"
1482            else:
1483                color = "white"
1484
1485            click = "%s_table_%s.html" % (self.ident, state.ident)
1486            over = "%s_State_%s.html" % (self.ident, state.ident)
1487            ref = self.frameRef(click, "Table", over, "1", state.short)
1488            code('''
1489  <TH bgcolor=$color>$ref</TH>
1490</TR>
1491''')
1492        code('''
1493<!- Column footer->
1494<TR>
1495  <TH> </TH>
1496''')
1497
1498        for event in self.events.itervalues():
1499            href = "%s_Event_%s.html" % (self.ident, event.ident)
1500            ref = self.frameRef(href, "Status", href, "1", event.short)
1501            code('<TH bgcolor=white>$ref</TH>')
1502        code('''
1503</TR>
1504</TABLE>
1505</BODY></HTML>
1506''')
1507
1508
1509        if active_state:
1510            name = "%s_table_%s.html" % (self.ident, active_state.ident)
1511        else:
1512            name = "%s_table.html" % self.ident
1513        code.write(path, name)
1514
1515__all__ = [ "StateMachine" ]
1516