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