StateMachine.py revision 11113:5a2e1b1b5c43
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''')
596
597        code.indent()
598
599        for var in self.objects:
600            vtype = var.type
601            vid = "m_%s_ptr" % var.ident
602            if "network" not in var:
603                # Not a network port object
604                if "primitive" in vtype:
605                    code('$vid = new ${{vtype.c_ident}};')
606                    if "default" in var:
607                        code('(*$vid) = ${{var["default"]}};')
608                else:
609                    # Normal Object
610                    th = var.get("template", "")
611                    expr = "%s  = new %s%s" % (vid, vtype.c_ident, th)
612                    args = ""
613                    if "non_obj" not in vtype and not vtype.isEnumeration:
614                        args = var.get("constructor", "")
615
616                    code('$expr($args);')
617                    code('assert($vid != NULL);')
618
619                    if "default" in var:
620                        code('*$vid = ${{var["default"]}}; // Object default')
621                    elif "default" in vtype:
622                        comment = "Type %s default" % vtype.ident
623                        code('*$vid = ${{vtype["default"]}}; // $comment')
624
625        # Set the prefetchers
626        code()
627        for prefetcher in self.prefetchers:
628            code('${{prefetcher.code}}.setController(this);')
629
630        code()
631        for port in self.in_ports:
632            # Set the queue consumers
633            code('${{port.code}}.setConsumer(this);')
634
635        # Initialize the transition profiling
636        code()
637        for trans in self.transitions:
638            # Figure out if we stall
639            stall = False
640            for action in trans.actions:
641                if action.ident == "z_stall":
642                    stall = True
643
644            # Only possible if it is not a 'z' case
645            if not stall:
646                state = "%s_State_%s" % (self.ident, trans.state.ident)
647                event = "%s_Event_%s" % (self.ident, trans.event.ident)
648                code('possibleTransition($state, $event);')
649
650        code.dedent()
651        code('''
652    AbstractController::init();
653    resetStats();
654}
655''')
656
657        mq_ident = "NULL"
658        for port in self.in_ports:
659            if port.code.find("mandatoryQueue_ptr") >= 0:
660                mq_ident = "m_mandatoryQueue_ptr"
661
662        memq_ident = "NULL"
663        for port in self.in_ports:
664            if port.code.find("responseFromMemory_ptr") >= 0:
665                memq_ident = "m_responseFromMemory_ptr"
666
667        seq_ident = "NULL"
668        for param in self.config_parameters:
669            if param.ident == "sequencer":
670                assert(param.pointer)
671                seq_ident = "m_%s_ptr" % param.ident
672
673        code('''
674
675void
676$c_ident::regStats()
677{
678    AbstractController::regStats();
679
680    if (m_version == 0) {
681        for (${ident}_Event event = ${ident}_Event_FIRST;
682             event < ${ident}_Event_NUM; ++event) {
683            Stats::Vector *t = new Stats::Vector();
684            t->init(m_num_controllers);
685            t->name(params()->ruby_system->name() + ".${c_ident}." +
686                ${ident}_Event_to_string(event));
687            t->flags(Stats::pdf | Stats::total | Stats::oneline |
688                     Stats::nozero);
689
690            eventVec.push_back(t);
691        }
692
693        for (${ident}_State state = ${ident}_State_FIRST;
694             state < ${ident}_State_NUM; ++state) {
695
696            transVec.push_back(std::vector<Stats::Vector *>());
697
698            for (${ident}_Event event = ${ident}_Event_FIRST;
699                 event < ${ident}_Event_NUM; ++event) {
700
701                Stats::Vector *t = new Stats::Vector();
702                t->init(m_num_controllers);
703                t->name(params()->ruby_system->name() + ".${c_ident}." +
704                        ${ident}_State_to_string(state) +
705                        "." + ${ident}_Event_to_string(event));
706
707                t->flags(Stats::pdf | Stats::total | Stats::oneline |
708                         Stats::nozero);
709                transVec[state].push_back(t);
710            }
711        }
712    }
713}
714
715void
716$c_ident::collateStats()
717{
718    for (${ident}_Event event = ${ident}_Event_FIRST;
719         event < ${ident}_Event_NUM; ++event) {
720        for (unsigned int i = 0; i < m_num_controllers; ++i) {
721            RubySystem *rs = params()->ruby_system;
722            std::map<uint32_t, AbstractController *>::iterator it =
723                     rs->m_abstract_controls[MachineType_${ident}].find(i);
724            assert(it != rs->m_abstract_controls[MachineType_${ident}].end());
725            (*eventVec[event])[i] =
726                (($c_ident *)(*it).second)->getEventCount(event);
727        }
728    }
729
730    for (${ident}_State state = ${ident}_State_FIRST;
731         state < ${ident}_State_NUM; ++state) {
732
733        for (${ident}_Event event = ${ident}_Event_FIRST;
734             event < ${ident}_Event_NUM; ++event) {
735
736            for (unsigned int i = 0; i < m_num_controllers; ++i) {
737                RubySystem *rs = params()->ruby_system;
738                std::map<uint32_t, AbstractController *>::iterator it =
739                         rs->m_abstract_controls[MachineType_${ident}].find(i);
740                assert(it != rs->m_abstract_controls[MachineType_${ident}].end());
741                (*transVec[state][event])[i] =
742                    (($c_ident *)(*it).second)->getTransitionCount(state, event);
743            }
744        }
745    }
746}
747
748void
749$c_ident::countTransition(${ident}_State state, ${ident}_Event event)
750{
751    assert(m_possible[state][event]);
752    m_counters[state][event]++;
753    m_event_counters[event]++;
754}
755void
756$c_ident::possibleTransition(${ident}_State state,
757                             ${ident}_Event event)
758{
759    m_possible[state][event] = true;
760}
761
762uint64_t
763$c_ident::getEventCount(${ident}_Event event)
764{
765    return m_event_counters[event];
766}
767
768bool
769$c_ident::isPossible(${ident}_State state, ${ident}_Event event)
770{
771    return m_possible[state][event];
772}
773
774uint64_t
775$c_ident::getTransitionCount(${ident}_State state,
776                             ${ident}_Event event)
777{
778    return m_counters[state][event];
779}
780
781int
782$c_ident::getNumControllers()
783{
784    return m_num_controllers;
785}
786
787MessageBuffer*
788$c_ident::getMandatoryQueue() const
789{
790    return $mq_ident;
791}
792
793MessageBuffer*
794$c_ident::getMemoryQueue() const
795{
796    return $memq_ident;
797}
798
799Sequencer*
800$c_ident::getSequencer() const
801{
802    return $seq_ident;
803}
804
805void
806$c_ident::print(ostream& out) const
807{
808    out << "[$c_ident " << m_version << "]";
809}
810
811void $c_ident::resetStats()
812{
813    for (int state = 0; state < ${ident}_State_NUM; state++) {
814        for (int event = 0; event < ${ident}_Event_NUM; event++) {
815            m_counters[state][event] = 0;
816        }
817    }
818
819    for (int event = 0; event < ${ident}_Event_NUM; event++) {
820        m_event_counters[event] = 0;
821    }
822
823    AbstractController::resetStats();
824}
825''')
826
827        if self.EntryType != None:
828            code('''
829
830// Set and Reset for cache_entry variable
831void
832$c_ident::set_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, AbstractCacheEntry* m_new_cache_entry)
833{
834  m_cache_entry_ptr = (${{self.EntryType.c_ident}}*)m_new_cache_entry;
835}
836
837void
838$c_ident::unset_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr)
839{
840  m_cache_entry_ptr = 0;
841}
842''')
843
844        if self.TBEType != None:
845            code('''
846
847// Set and Reset for tbe variable
848void
849$c_ident::set_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.TBEType.c_ident}}* m_new_tbe)
850{
851  m_tbe_ptr = m_new_tbe;
852}
853
854void
855$c_ident::unset_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr)
856{
857  m_tbe_ptr = NULL;
858}
859''')
860
861        code('''
862
863void
864$c_ident::recordCacheTrace(int cntrl, CacheRecorder* tr)
865{
866''')
867        #
868        # Record cache contents for all associated caches.
869        #
870        code.indent()
871        for param in self.config_parameters:
872            if param.type_ast.type.ident == "CacheMemory":
873                assert(param.pointer)
874                code('m_${{param.ident}}_ptr->recordCacheContents(cntrl, tr);')
875
876        code.dedent()
877        code('''
878}
879
880// Actions
881''')
882        if self.TBEType != None and self.EntryType != None:
883            for action in self.actions.itervalues():
884                if "c_code" not in action:
885                 continue
886
887                code('''
888/** \\brief ${{action.desc}} */
889void
890$c_ident::${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.EntryType.c_ident}}*& m_cache_entry_ptr, Addr addr)
891{
892    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
893    try {
894       ${{action["c_code"]}}
895    } catch (const RejectException & e) {
896       fatal("Error in action ${{ident}}:${{action.ident}}: "
897             "executed a peek statement with the wrong message "
898             "type specified. ");
899    }
900}
901
902''')
903        elif self.TBEType != None:
904            for action in self.actions.itervalues():
905                if "c_code" not in action:
906                 continue
907
908                code('''
909/** \\brief ${{action.desc}} */
910void
911$c_ident::${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, Addr addr)
912{
913    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
914    ${{action["c_code"]}}
915}
916
917''')
918        elif self.EntryType != None:
919            for action in self.actions.itervalues():
920                if "c_code" not in action:
921                 continue
922
923                code('''
924/** \\brief ${{action.desc}} */
925void
926$c_ident::${{action.ident}}(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, Addr addr)
927{
928    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
929    ${{action["c_code"]}}
930}
931
932''')
933        else:
934            for action in self.actions.itervalues():
935                if "c_code" not in action:
936                 continue
937
938                code('''
939/** \\brief ${{action.desc}} */
940void
941$c_ident::${{action.ident}}(Addr addr)
942{
943    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
944    ${{action["c_code"]}}
945}
946
947''')
948        for func in self.functions:
949            code(func.generateCode())
950
951        # Function for functional writes to messages buffered in the controller
952        code('''
953int
954$c_ident::functionalWriteBuffers(PacketPtr& pkt)
955{
956    int num_functional_writes = 0;
957''')
958        for var in self.objects:
959            vtype = var.type
960            if vtype.isBuffer:
961                vid = "m_%s_ptr" % var.ident
962                code('num_functional_writes += $vid->functionalWrite(pkt);')
963
964        for var in self.config_parameters:
965            vtype = var.type_ast.type
966            if vtype.isBuffer:
967                vid = "m_%s_ptr" % var.ident
968                code('num_functional_writes += $vid->functionalWrite(pkt);')
969
970        code('''
971    return num_functional_writes;
972}
973''')
974
975        code.write(path, "%s.cc" % c_ident)
976
977    def printCWakeup(self, path, includes):
978        '''Output the wakeup loop for the events'''
979
980        code = self.symtab.codeFormatter()
981        ident = self.ident
982
983        outputRequest_types = True
984        if len(self.request_types) == 0:
985            outputRequest_types = False
986
987        code('''
988// Auto generated C++ code started by $__file__:$__line__
989// ${ident}: ${{self.short}}
990
991#include <sys/types.h>
992#include <unistd.h>
993
994#include <cassert>
995#include <typeinfo>
996
997#include "base/misc.hh"
998
999''')
1000        for f in self.debug_flags:
1001            code('#include "debug/${{f}}.hh"')
1002        code('''
1003#include "mem/protocol/${ident}_Controller.hh"
1004#include "mem/protocol/${ident}_Event.hh"
1005#include "mem/protocol/${ident}_State.hh"
1006
1007''')
1008
1009        if outputRequest_types:
1010            code('''#include "mem/protocol/${ident}_RequestType.hh"''')
1011
1012        code('''
1013#include "mem/protocol/Types.hh"
1014#include "mem/ruby/system/RubySystem.hh"
1015
1016''')
1017
1018
1019        for include_path in includes:
1020            code('#include "${{include_path}}"')
1021
1022        port_to_buf_map, in_msg_bufs, msg_bufs = self.getBufferMaps(ident)
1023
1024        code('''
1025
1026using namespace std;
1027
1028void
1029${ident}_Controller::wakeup()
1030{
1031    int counter = 0;
1032    while (true) {
1033        unsigned char rejected[${{len(msg_bufs)}}];
1034        memset(rejected, 0, sizeof(unsigned char)*${{len(msg_bufs)}});
1035        // Some cases will put us into an infinite loop without this limit
1036        assert(counter <= m_transitions_per_cycle);
1037        if (counter == m_transitions_per_cycle) {
1038            // Count how often we are fully utilized
1039            m_fully_busy_cycles++;
1040
1041            // Wakeup in another cycle and try again
1042            scheduleEvent(Cycles(1));
1043            break;
1044        }
1045''')
1046
1047        code.indent()
1048        code.indent()
1049
1050        # InPorts
1051        #
1052        for port in self.in_ports:
1053            code.indent()
1054            code('// ${ident}InPort $port')
1055            if port.pairs.has_key("rank"):
1056                code('m_cur_in_port = ${{port.pairs["rank"]}};')
1057            else:
1058                code('m_cur_in_port = 0;')
1059            if port in port_to_buf_map:
1060                code('try {')
1061                code.indent()
1062            code('${{port["c_code_in_port"]}}')
1063
1064            if port in port_to_buf_map:
1065                code.dedent()
1066                code('''
1067            } catch (const RejectException & e) {
1068                rejected[${{port_to_buf_map[port]}}]++;
1069            }
1070''')
1071            code.dedent()
1072            code('')
1073
1074        code.dedent()
1075        code.dedent()
1076        code('''
1077        // If we got this far, we have nothing left todo or something went
1078        // wrong''')
1079        for buf_name, ports in in_msg_bufs.items():
1080            if len(ports) > 1:
1081                # only produce checks when a buffer is shared by multiple ports
1082                code('''
1083        if (${{buf_name}}->isReady() && rejected[${{port_to_buf_map[ports[0]]}}] == ${{len(ports)}})
1084        {
1085            // no port claimed the message on the top of this buffer
1086            panic("Runtime Error at Ruby Time: %d. "
1087                  "All ports rejected a message. "
1088                  "You are probably sending a message type to this controller "
1089                  "over a virtual network that do not define an in_port for "
1090                  "the incoming message type.\\n",
1091                  Cycles(1));
1092        }
1093''')
1094        code('''
1095        break;
1096    }
1097}
1098''')
1099
1100        code.write(path, "%s_Wakeup.cc" % self.ident)
1101
1102    def printCSwitch(self, path):
1103        '''Output switch statement for transition table'''
1104
1105        code = self.symtab.codeFormatter()
1106        ident = self.ident
1107
1108        code('''
1109// Auto generated C++ code started by $__file__:$__line__
1110// ${ident}: ${{self.short}}
1111
1112#include <cassert>
1113
1114#include "base/misc.hh"
1115#include "base/trace.hh"
1116#include "debug/ProtocolTrace.hh"
1117#include "debug/RubyGenerated.hh"
1118#include "mem/protocol/${ident}_Controller.hh"
1119#include "mem/protocol/${ident}_Event.hh"
1120#include "mem/protocol/${ident}_State.hh"
1121#include "mem/protocol/Types.hh"
1122#include "mem/ruby/system/RubySystem.hh"
1123
1124#define HASH_FUN(state, event)  ((int(state)*${ident}_Event_NUM)+int(event))
1125
1126#define GET_TRANSITION_COMMENT() (${ident}_transitionComment.str())
1127#define CLEAR_TRANSITION_COMMENT() (${ident}_transitionComment.str(""))
1128
1129TransitionResult
1130${ident}_Controller::doTransition(${ident}_Event event,
1131''')
1132        if self.EntryType != None:
1133            code('''
1134                                  ${{self.EntryType.c_ident}}* m_cache_entry_ptr,
1135''')
1136        if self.TBEType != None:
1137            code('''
1138                                  ${{self.TBEType.c_ident}}* m_tbe_ptr,
1139''')
1140        code('''
1141                                  Addr addr)
1142{
1143''')
1144        code.indent()
1145
1146        if self.TBEType != None and self.EntryType != None:
1147            code('${ident}_State state = getState(m_tbe_ptr, m_cache_entry_ptr, addr);')
1148        elif self.TBEType != None:
1149            code('${ident}_State state = getState(m_tbe_ptr, addr);')
1150        elif self.EntryType != None:
1151            code('${ident}_State state = getState(m_cache_entry_ptr, addr);')
1152        else:
1153            code('${ident}_State state = getState(addr);')
1154
1155        code('''
1156${ident}_State next_state = state;
1157
1158DPRINTF(RubyGenerated, "%s, Time: %lld, state: %s, event: %s, addr: %s\\n",
1159        *this, curCycle(), ${ident}_State_to_string(state),
1160        ${ident}_Event_to_string(event), addr);
1161
1162TransitionResult result =
1163''')
1164        if self.TBEType != None and self.EntryType != None:
1165            code('doTransitionWorker(event, state, next_state, m_tbe_ptr, m_cache_entry_ptr, addr);')
1166        elif self.TBEType != None:
1167            code('doTransitionWorker(event, state, next_state, m_tbe_ptr, addr);')
1168        elif self.EntryType != None:
1169            code('doTransitionWorker(event, state, next_state, m_cache_entry_ptr, addr);')
1170        else:
1171            code('doTransitionWorker(event, state, next_state, addr);')
1172
1173        port_to_buf_map, in_msg_bufs, msg_bufs = self.getBufferMaps(ident)
1174
1175        code('''
1176
1177if (result == TransitionResult_Valid) {
1178    DPRINTF(RubyGenerated, "next_state: %s\\n",
1179            ${ident}_State_to_string(next_state));
1180    countTransition(state, event);
1181
1182    DPRINTFR(ProtocolTrace, "%15d %3s %10s%20s %6s>%-6s %#x %s\\n",
1183             curTick(), m_version, "${ident}",
1184             ${ident}_Event_to_string(event),
1185             ${ident}_State_to_string(state),
1186             ${ident}_State_to_string(next_state),
1187             addr, GET_TRANSITION_COMMENT());
1188
1189    CLEAR_TRANSITION_COMMENT();
1190''')
1191        if self.TBEType != None and self.EntryType != None:
1192            code('setState(m_tbe_ptr, m_cache_entry_ptr, addr, next_state);')
1193            code('setAccessPermission(m_cache_entry_ptr, addr, next_state);')
1194        elif self.TBEType != None:
1195            code('setState(m_tbe_ptr, addr, next_state);')
1196            code('setAccessPermission(addr, next_state);')
1197        elif self.EntryType != None:
1198            code('setState(m_cache_entry_ptr, addr, next_state);')
1199            code('setAccessPermission(m_cache_entry_ptr, addr, next_state);')
1200        else:
1201            code('setState(addr, next_state);')
1202            code('setAccessPermission(addr, next_state);')
1203
1204        code('''
1205} else if (result == TransitionResult_ResourceStall) {
1206    DPRINTFR(ProtocolTrace, "%15s %3s %10s%20s %6s>%-6s %#x %s\\n",
1207             curTick(), m_version, "${ident}",
1208             ${ident}_Event_to_string(event),
1209             ${ident}_State_to_string(state),
1210             ${ident}_State_to_string(next_state),
1211             addr, "Resource Stall");
1212} else if (result == TransitionResult_ProtocolStall) {
1213    DPRINTF(RubyGenerated, "stalling\\n");
1214    DPRINTFR(ProtocolTrace, "%15s %3s %10s%20s %6s>%-6s %#x %s\\n",
1215             curTick(), m_version, "${ident}",
1216             ${ident}_Event_to_string(event),
1217             ${ident}_State_to_string(state),
1218             ${ident}_State_to_string(next_state),
1219             addr, "Protocol Stall");
1220}
1221
1222return result;
1223''')
1224        code.dedent()
1225        code('''
1226}
1227
1228TransitionResult
1229${ident}_Controller::doTransitionWorker(${ident}_Event event,
1230                                        ${ident}_State state,
1231                                        ${ident}_State& next_state,
1232''')
1233
1234        if self.TBEType != None:
1235            code('''
1236                                        ${{self.TBEType.c_ident}}*& m_tbe_ptr,
1237''')
1238        if self.EntryType != None:
1239                  code('''
1240                                        ${{self.EntryType.c_ident}}*& m_cache_entry_ptr,
1241''')
1242        code('''
1243                                        Addr addr)
1244{
1245    switch(HASH_FUN(state, event)) {
1246''')
1247
1248        # This map will allow suppress generating duplicate code
1249        cases = orderdict()
1250
1251        for trans in self.transitions:
1252            case_string = "%s_State_%s, %s_Event_%s" % \
1253                (self.ident, trans.state.ident, self.ident, trans.event.ident)
1254
1255            case = self.symtab.codeFormatter()
1256            # Only set next_state if it changes
1257            if trans.state != trans.nextState:
1258                if trans.nextState.isWildcard():
1259                    # When * is encountered as an end state of a transition,
1260                    # the next state is determined by calling the
1261                    # machine-specific getNextState function. The next state
1262                    # is determined before any actions of the transition
1263                    # execute, and therefore the next state calculation cannot
1264                    # depend on any of the transitionactions.
1265                    case('next_state = getNextState(addr);')
1266                else:
1267                    ns_ident = trans.nextState.ident
1268                    case('next_state = ${ident}_State_${ns_ident};')
1269
1270            actions = trans.actions
1271            request_types = trans.request_types
1272
1273            # Check for resources
1274            case_sorter = []
1275            res = trans.resources
1276            for key,val in res.iteritems():
1277                val = '''
1278if (!%s.areNSlotsAvailable(%s, clockEdge()))
1279    return TransitionResult_ResourceStall;
1280''' % (key.code, val)
1281                case_sorter.append(val)
1282
1283            # Check all of the request_types for resource constraints
1284            for request_type in request_types:
1285                val = '''
1286if (!checkResourceAvailable(%s_RequestType_%s, addr)) {
1287    return TransitionResult_ResourceStall;
1288}
1289''' % (self.ident, request_type.ident)
1290                case_sorter.append(val)
1291
1292            # Emit the code sequences in a sorted order.  This makes the
1293            # output deterministic (without this the output order can vary
1294            # since Map's keys() on a vector of pointers is not deterministic
1295            for c in sorted(case_sorter):
1296                case("$c")
1297
1298            # Record access types for this transition
1299            for request_type in request_types:
1300                case('recordRequestType(${ident}_RequestType_${{request_type.ident}}, addr);')
1301
1302            # Figure out if we stall
1303            stall = False
1304            for action in actions:
1305                if action.ident == "z_stall":
1306                    stall = True
1307                    break
1308
1309            if stall:
1310                case('return TransitionResult_ProtocolStall;')
1311            else:
1312                if self.TBEType != None and self.EntryType != None:
1313                    for action in actions:
1314                        case('${{action.ident}}(m_tbe_ptr, m_cache_entry_ptr, addr);')
1315                elif self.TBEType != None:
1316                    for action in actions:
1317                        case('${{action.ident}}(m_tbe_ptr, addr);')
1318                elif self.EntryType != None:
1319                    for action in actions:
1320                        case('${{action.ident}}(m_cache_entry_ptr, addr);')
1321                else:
1322                    for action in actions:
1323                        case('${{action.ident}}(addr);')
1324                case('return TransitionResult_Valid;')
1325
1326            case = str(case)
1327
1328            # Look to see if this transition code is unique.
1329            if case not in cases:
1330                cases[case] = []
1331
1332            cases[case].append(case_string)
1333
1334        # Walk through all of the unique code blocks and spit out the
1335        # corresponding case statement elements
1336        for case,transitions in cases.iteritems():
1337            # Iterative over all the multiple transitions that share
1338            # the same code
1339            for trans in transitions:
1340                code('  case HASH_FUN($trans):')
1341            code('    $case\n')
1342
1343        code('''
1344      default:
1345        panic("Invalid transition\\n"
1346              "%s time: %d addr: %s event: %s state: %s\\n",
1347              name(), curCycle(), addr, event, state);
1348    }
1349
1350    return TransitionResult_Valid;
1351}
1352''')
1353        code.write(path, "%s_Transitions.cc" % self.ident)
1354
1355
1356    # **************************
1357    # ******* HTML Files *******
1358    # **************************
1359    def frameRef(self, click_href, click_target, over_href, over_num, text):
1360        code = self.symtab.codeFormatter(fix_newlines=False)
1361        code("""<A href=\"$click_href\" target=\"$click_target\" onmouseover=\"
1362    if (parent.frames[$over_num].location != parent.location + '$over_href') {
1363        parent.frames[$over_num].location='$over_href'
1364    }\">
1365    ${{html.formatShorthand(text)}}
1366    </A>""")
1367        return str(code)
1368
1369    def writeHTMLFiles(self, path):
1370        # Create table with no row hilighted
1371        self.printHTMLTransitions(path, None)
1372
1373        # Generate transition tables
1374        for state in self.states.itervalues():
1375            self.printHTMLTransitions(path, state)
1376
1377        # Generate action descriptions
1378        for action in self.actions.itervalues():
1379            name = "%s_action_%s.html" % (self.ident, action.ident)
1380            code = html.createSymbol(action, "Action")
1381            code.write(path, name)
1382
1383        # Generate state descriptions
1384        for state in self.states.itervalues():
1385            name = "%s_State_%s.html" % (self.ident, state.ident)
1386            code = html.createSymbol(state, "State")
1387            code.write(path, name)
1388
1389        # Generate event descriptions
1390        for event in self.events.itervalues():
1391            name = "%s_Event_%s.html" % (self.ident, event.ident)
1392            code = html.createSymbol(event, "Event")
1393            code.write(path, name)
1394
1395    def printHTMLTransitions(self, path, active_state):
1396        code = self.symtab.codeFormatter()
1397
1398        code('''
1399<HTML>
1400<BODY link="blue" vlink="blue">
1401
1402<H1 align="center">${{html.formatShorthand(self.short)}}:
1403''')
1404        code.indent()
1405        for i,machine in enumerate(self.symtab.getAllType(StateMachine)):
1406            mid = machine.ident
1407            if i != 0:
1408                extra = " - "
1409            else:
1410                extra = ""
1411            if machine == self:
1412                code('$extra$mid')
1413            else:
1414                code('$extra<A target="Table" href="${mid}_table.html">$mid</A>')
1415        code.dedent()
1416
1417        code("""
1418</H1>
1419
1420<TABLE border=1>
1421<TR>
1422  <TH> </TH>
1423""")
1424
1425        for event in self.events.itervalues():
1426            href = "%s_Event_%s.html" % (self.ident, event.ident)
1427            ref = self.frameRef(href, "Status", href, "1", event.short)
1428            code('<TH bgcolor=white>$ref</TH>')
1429
1430        code('</TR>')
1431        # -- Body of table
1432        for state in self.states.itervalues():
1433            # -- Each row
1434            if state == active_state:
1435                color = "yellow"
1436            else:
1437                color = "white"
1438
1439            click = "%s_table_%s.html" % (self.ident, state.ident)
1440            over = "%s_State_%s.html" % (self.ident, state.ident)
1441            text = html.formatShorthand(state.short)
1442            ref = self.frameRef(click, "Table", over, "1", state.short)
1443            code('''
1444<TR>
1445  <TH bgcolor=$color>$ref</TH>
1446''')
1447
1448            # -- One column for each event
1449            for event in self.events.itervalues():
1450                trans = self.table.get((state,event), None)
1451                if trans is None:
1452                    # This is the no transition case
1453                    if state == active_state:
1454                        color = "#C0C000"
1455                    else:
1456                        color = "lightgrey"
1457
1458                    code('<TD bgcolor=$color>&nbsp;</TD>')
1459                    continue
1460
1461                next = trans.nextState
1462                stall_action = False
1463
1464                # -- Get the actions
1465                for action in trans.actions:
1466                    if action.ident == "z_stall" or \
1467                       action.ident == "zz_recycleMandatoryQueue":
1468                        stall_action = True
1469
1470                # -- Print out "actions/next-state"
1471                if stall_action:
1472                    if state == active_state:
1473                        color = "#C0C000"
1474                    else:
1475                        color = "lightgrey"
1476
1477                elif active_state and next.ident == active_state.ident:
1478                    color = "aqua"
1479                elif state == active_state:
1480                    color = "yellow"
1481                else:
1482                    color = "white"
1483
1484                code('<TD bgcolor=$color>')
1485                for action in trans.actions:
1486                    href = "%s_action_%s.html" % (self.ident, action.ident)
1487                    ref = self.frameRef(href, "Status", href, "1",
1488                                        action.short)
1489                    code('  $ref')
1490                if next != state:
1491                    if trans.actions:
1492                        code('/')
1493                    click = "%s_table_%s.html" % (self.ident, next.ident)
1494                    over = "%s_State_%s.html" % (self.ident, next.ident)
1495                    ref = self.frameRef(click, "Table", over, "1", next.short)
1496                    code("$ref")
1497                code("</TD>")
1498
1499            # -- Each row
1500            if state == active_state:
1501                color = "yellow"
1502            else:
1503                color = "white"
1504
1505            click = "%s_table_%s.html" % (self.ident, state.ident)
1506            over = "%s_State_%s.html" % (self.ident, state.ident)
1507            ref = self.frameRef(click, "Table", over, "1", state.short)
1508            code('''
1509  <TH bgcolor=$color>$ref</TH>
1510</TR>
1511''')
1512        code('''
1513<!- Column footer->
1514<TR>
1515  <TH> </TH>
1516''')
1517
1518        for event in self.events.itervalues():
1519            href = "%s_Event_%s.html" % (self.ident, event.ident)
1520            ref = self.frameRef(href, "Status", href, "1", event.short)
1521            code('<TH bgcolor=white>$ref</TH>')
1522        code('''
1523</TR>
1524</TABLE>
1525</BODY></HTML>
1526''')
1527
1528
1529        if active_state:
1530            name = "%s_table_%s.html" % (self.ident, active_state.ident)
1531        else:
1532            name = "%s_table.html" % self.ident
1533        code.write(path, name)
1534
1535__all__ = [ "StateMachine" ]
1536