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