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