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