StateMachine.py revision 10228:1a85c4fc805c
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    const std::string toString() const;
257
258    void print(std::ostream& out) const;
259    void wakeup();
260    void resetStats();
261    void regStats();
262    void collateStats();
263
264    void recordCacheTrace(int cntrl, CacheRecorder* tr);
265    Sequencer* getSequencer() const;
266
267    bool functionalReadBuffers(PacketPtr&);
268    uint32_t functionalWriteBuffers(PacketPtr&);
269
270    void countTransition(${ident}_State state, ${ident}_Event event);
271    void possibleTransition(${ident}_State state, ${ident}_Event event);
272    uint64 getEventCount(${ident}_Event event);
273    bool isPossible(${ident}_State state, ${ident}_Event event);
274    uint64 getTransitionCount(${ident}_State state, ${ident}_Event event);
275
276private:
277''')
278
279        code.indent()
280        # added by SS
281        for param in self.config_parameters:
282            if param.pointer:
283                code('${{param.type_ast.type}}* m_${{param.ident}}_ptr;')
284            else:
285                code('${{param.type_ast.type}} m_${{param.ident}};')
286
287        code('''
288TransitionResult doTransition(${ident}_Event event,
289''')
290
291        if self.EntryType != None:
292            code('''
293                              ${{self.EntryType.c_ident}}* m_cache_entry_ptr,
294''')
295        if self.TBEType != None:
296            code('''
297                              ${{self.TBEType.c_ident}}* m_tbe_ptr,
298''')
299
300        code('''
301                              const Address addr);
302
303TransitionResult doTransitionWorker(${ident}_Event event,
304                                    ${ident}_State state,
305                                    ${ident}_State& next_state,
306''')
307
308        if self.TBEType != None:
309            code('''
310                                    ${{self.TBEType.c_ident}}*& m_tbe_ptr,
311''')
312        if self.EntryType != None:
313            code('''
314                                    ${{self.EntryType.c_ident}}*& m_cache_entry_ptr,
315''')
316
317        code('''
318                                    const Address& addr);
319
320int m_counters[${ident}_State_NUM][${ident}_Event_NUM];
321int m_event_counters[${ident}_Event_NUM];
322bool m_possible[${ident}_State_NUM][${ident}_Event_NUM];
323
324static std::vector<Stats::Vector *> eventVec;
325static std::vector<std::vector<Stats::Vector *> > transVec;
326static int m_num_controllers;
327
328// Internal functions
329''')
330
331        for func in self.functions:
332            proto = func.prototype
333            if proto:
334                code('$proto')
335
336        if has_peer:
337            code('void getQueuesFromPeer(AbstractController *);')
338        if self.EntryType != None:
339            code('''
340
341// Set and Reset for cache_entry variable
342void set_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, AbstractCacheEntry* m_new_cache_entry);
343void unset_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr);
344''')
345
346        if self.TBEType != None:
347            code('''
348
349// Set and Reset for tbe variable
350void set_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${ident}_TBE* m_new_tbe);
351void unset_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr);
352''')
353
354        # Prototype the actions that the controller can take
355        code('''
356
357// Actions
358''')
359        if self.TBEType != None and self.EntryType != None:
360            for action in self.actions.itervalues():
361                code('/** \\brief ${{action.desc}} */')
362                code('void ${{action.ident}}(${{self.TBEType.c_ident}}*& '
363                     'm_tbe_ptr, ${{self.EntryType.c_ident}}*& '
364                     'm_cache_entry_ptr, const Address& addr);')
365        elif self.TBEType != None:
366            for action in self.actions.itervalues():
367                code('/** \\brief ${{action.desc}} */')
368                code('void ${{action.ident}}(${{self.TBEType.c_ident}}*& '
369                     'm_tbe_ptr, const Address& addr);')
370        elif self.EntryType != None:
371            for action in self.actions.itervalues():
372                code('/** \\brief ${{action.desc}} */')
373                code('void ${{action.ident}}(${{self.EntryType.c_ident}}*& '
374                     'm_cache_entry_ptr, const Address& addr);')
375        else:
376            for action in self.actions.itervalues():
377                code('/** \\brief ${{action.desc}} */')
378                code('void ${{action.ident}}(const Address& addr);')
379
380        # the controller internal variables
381        code('''
382
383// Objects
384''')
385        for var in self.objects:
386            th = var.get("template", "")
387            code('${{var.type.c_ident}}$th* m_${{var.c_ident}}_ptr;')
388
389        code.dedent()
390        code('};')
391        code('#endif // __${ident}_CONTROLLER_H__')
392        code.write(path, '%s.hh' % c_ident)
393
394    def printControllerCC(self, path, includes):
395        '''Output the actions for performing the actions'''
396
397        code = self.symtab.codeFormatter()
398        ident = self.ident
399        c_ident = "%s_Controller" % self.ident
400        has_peer = False
401
402        code('''
403/** \\file $c_ident.cc
404 *
405 * Auto generated C++ code started by $__file__:$__line__
406 * Created by slicc definition of Module "${{self.short}}"
407 */
408
409#include <sys/types.h>
410#include <unistd.h>
411
412#include <cassert>
413#include <sstream>
414#include <string>
415
416#include "base/compiler.hh"
417#include "base/cprintf.hh"
418#include "debug/RubyGenerated.hh"
419#include "debug/RubySlicc.hh"
420#include "mem/protocol/${ident}_Controller.hh"
421#include "mem/protocol/${ident}_Event.hh"
422#include "mem/protocol/${ident}_State.hh"
423#include "mem/protocol/Types.hh"
424#include "mem/ruby/common/Global.hh"
425#include "mem/ruby/system/System.hh"
426''')
427        for include_path in includes:
428            code('#include "${{include_path}}"')
429
430        code('''
431
432using namespace std;
433''')
434
435        # include object classes
436        seen_types = set()
437        for var in self.objects:
438            if var.type.ident not in seen_types and not var.type.isPrimitive:
439                code('#include "mem/protocol/${{var.type.c_ident}}.hh"')
440            seen_types.add(var.type.ident)
441
442        num_in_ports = len(self.in_ports)
443
444        code('''
445$c_ident *
446${c_ident}Params::create()
447{
448    return new $c_ident(this);
449}
450
451int $c_ident::m_num_controllers = 0;
452std::vector<Stats::Vector *>  $c_ident::eventVec;
453std::vector<std::vector<Stats::Vector *> >  $c_ident::transVec;
454
455// for adding information to the protocol debug trace
456stringstream ${ident}_transitionComment;
457
458#ifndef NDEBUG
459#define APPEND_TRANSITION_COMMENT(str) (${ident}_transitionComment << str)
460#else
461#define APPEND_TRANSITION_COMMENT(str) do {} while (0)
462#endif
463
464/** \\brief constructor */
465$c_ident::$c_ident(const Params *p)
466    : AbstractController(p)
467{
468    m_machineID.type = MachineType_${ident};
469    m_machineID.num = m_version;
470    m_num_controllers++;
471
472    m_in_ports = $num_in_ports;
473''')
474        code.indent()
475
476        #
477        # After initializing the universal machine parameters, initialize the
478        # this machines config parameters.  Also if these configuration params
479        # include a sequencer, connect the it to the controller.
480        #
481        for param in self.config_parameters:
482            if param.pointer:
483                code('m_${{param.name}}_ptr = p->${{param.name}};')
484            else:
485                code('m_${{param.name}} = p->${{param.name}};')
486            if re.compile("sequencer").search(param.name):
487                code('m_${{param.name}}_ptr->setController(this);')
488
489        for var in self.objects:
490            if var.ident.find("mandatoryQueue") >= 0:
491                code('''
492m_${{var.c_ident}}_ptr = new ${{var.type.c_ident}}();
493m_${{var.c_ident}}_ptr->setReceiver(this);
494''')
495            else:
496                if "network" in var and "physical_network" in var and \
497                   var["network"] == "To":
498                    has_peer = True
499                    code('''
500m_${{var.c_ident}}_ptr = new ${{var.type.c_ident}}();
501peerQueueMap[${{var["physical_network"]}}] = m_${{var.c_ident}}_ptr;
502m_${{var.c_ident}}_ptr->setSender(this);
503''')
504
505        code('''
506if (p->peer != NULL)
507    connectWithPeer(p->peer);
508
509for (int state = 0; state < ${ident}_State_NUM; state++) {
510    for (int event = 0; event < ${ident}_Event_NUM; event++) {
511        m_possible[state][event] = false;
512        m_counters[state][event] = 0;
513    }
514}
515for (int event = 0; event < ${ident}_Event_NUM; event++) {
516    m_event_counters[event] = 0;
517}
518''')
519        code.dedent()
520        code('''
521}
522
523void
524$c_ident::init()
525{
526    MachineType machine_type = string_to_MachineType("${{var.machine.ident}}");
527    int base M5_VAR_USED = MachineType_base_number(machine_type);
528
529    // initialize objects
530
531''')
532
533        code.indent()
534        for var in self.objects:
535            vtype = var.type
536            vid = "m_%s_ptr" % var.c_ident
537            if "network" not in var:
538                # Not a network port object
539                if "primitive" in vtype:
540                    code('$vid = new ${{vtype.c_ident}};')
541                    if "default" in var:
542                        code('(*$vid) = ${{var["default"]}};')
543                else:
544                    # Normal Object
545                    if var.ident.find("mandatoryQueue") < 0:
546                        th = var.get("template", "")
547                        expr = "%s  = new %s%s" % (vid, vtype.c_ident, th)
548                        args = ""
549                        if "non_obj" not in vtype and not vtype.isEnumeration:
550                            args = var.get("constructor", "")
551                        code('$expr($args);')
552
553                    code('assert($vid != NULL);')
554
555                    if "default" in var:
556                        code('*$vid = ${{var["default"]}}; // Object default')
557                    elif "default" in vtype:
558                        comment = "Type %s default" % vtype.ident
559                        code('*$vid = ${{vtype["default"]}}; // $comment')
560
561                    # Set ordering
562                    if "ordered" in var:
563                        # A buffer
564                        code('$vid->setOrdering(${{var["ordered"]}});')
565
566                    # Set randomization
567                    if "random" in var:
568                        # A buffer
569                        code('$vid->setRandomization(${{var["random"]}});')
570
571                    # Set Priority
572                    if vtype.isBuffer and "rank" in var:
573                        code('$vid->setPriority(${{var["rank"]}});')
574
575                    # Set sender and receiver for trigger queue
576                    if var.ident.find("triggerQueue") >= 0:
577                        code('$vid->setSender(this);')
578                        code('$vid->setReceiver(this);')
579                    elif vtype.c_ident == "TimerTable":
580                        code('$vid->setClockObj(this);')
581                    elif var.ident.find("optionalQueue") >= 0:
582                        code('$vid->setSender(this);')
583                        code('$vid->setReceiver(this);')
584
585            else:
586                # Network port object
587                network = var["network"]
588                ordered =  var["ordered"]
589
590                if "virtual_network" in var:
591                    vnet = var["virtual_network"]
592                    vnet_type = var["vnet_type"]
593
594                    assert var.machine is not None
595                    code('''
596$vid = m_net_ptr->get${network}NetQueue(m_version + base, $ordered, $vnet, "$vnet_type");
597assert($vid != NULL);
598''')
599
600                    # Set the end
601                    if network == "To":
602                        code('$vid->setSender(this);')
603                    else:
604                        code('$vid->setReceiver(this);')
605
606                # Set ordering
607                if "ordered" in var:
608                    # A buffer
609                    code('$vid->setOrdering(${{var["ordered"]}});')
610
611                # Set randomization
612                if "random" in var:
613                    # A buffer
614                    code('$vid->setRandomization(${{var["random"]}});')
615
616                # Set Priority
617                if "rank" in var:
618                    code('$vid->setPriority(${{var["rank"]}})')
619
620                # Set buffer size
621                if vtype.isBuffer:
622                    code('''
623if (m_buffer_size > 0) {
624    $vid->resize(m_buffer_size);
625}
626''')
627
628                # set description (may be overriden later by port def)
629                code('''
630$vid->setDescription("[Version " + to_string(m_version) + ", ${ident}, name=${{var.c_ident}}]");
631
632''')
633
634            if vtype.isBuffer:
635                if "recycle_latency" in var:
636                    code('$vid->setRecycleLatency( ' \
637                         'Cycles(${{var["recycle_latency"]}}));')
638                else:
639                    code('$vid->setRecycleLatency(m_recycle_latency);')
640
641        # Set the prefetchers
642        code()
643        for prefetcher in self.prefetchers:
644            code('${{prefetcher.code}}.setController(this);')
645
646        code()
647        for port in self.in_ports:
648            # Set the queue consumers
649            code('${{port.code}}.setConsumer(this);')
650            # Set the queue descriptions
651            code('${{port.code}}.setDescription("[Version " + to_string(m_version) + ", $ident, $port]");')
652
653        # Initialize the transition profiling
654        code()
655        for trans in self.transitions:
656            # Figure out if we stall
657            stall = False
658            for action in trans.actions:
659                if action.ident == "z_stall":
660                    stall = True
661
662            # Only possible if it is not a 'z' case
663            if not stall:
664                state = "%s_State_%s" % (self.ident, trans.state.ident)
665                event = "%s_Event_%s" % (self.ident, trans.event.ident)
666                code('possibleTransition($state, $event);')
667
668        code.dedent()
669        code('''
670    AbstractController::init();
671    resetStats();
672}
673''')
674
675        mq_ident = "NULL"
676        for port in self.in_ports:
677            if port.code.find("mandatoryQueue_ptr") >= 0:
678                mq_ident = "m_%s_mandatoryQueue_ptr" % self.ident
679
680        seq_ident = "NULL"
681        for param in self.config_parameters:
682            if param.name == "sequencer":
683                assert(param.pointer)
684                seq_ident = "m_%s_ptr" % param.name
685
686        code('''
687
688void
689$c_ident::regStats()
690{
691    AbstractController::regStats();
692
693    if (m_version == 0) {
694        for (${ident}_Event event = ${ident}_Event_FIRST;
695             event < ${ident}_Event_NUM; ++event) {
696            Stats::Vector *t = new Stats::Vector();
697            t->init(m_num_controllers);
698            t->name(g_system_ptr->name() + ".${c_ident}." +
699                ${ident}_Event_to_string(event));
700            t->flags(Stats::pdf | Stats::total | Stats::oneline |
701                     Stats::nozero);
702
703            eventVec.push_back(t);
704        }
705
706        for (${ident}_State state = ${ident}_State_FIRST;
707             state < ${ident}_State_NUM; ++state) {
708
709            transVec.push_back(std::vector<Stats::Vector *>());
710
711            for (${ident}_Event event = ${ident}_Event_FIRST;
712                 event < ${ident}_Event_NUM; ++event) {
713
714                Stats::Vector *t = new Stats::Vector();
715                t->init(m_num_controllers);
716                t->name(g_system_ptr->name() + ".${c_ident}." +
717                        ${ident}_State_to_string(state) +
718                        "." + ${ident}_Event_to_string(event));
719
720                t->flags(Stats::pdf | Stats::total | Stats::oneline |
721                         Stats::nozero);
722                transVec[state].push_back(t);
723            }
724        }
725    }
726}
727
728void
729$c_ident::collateStats()
730{
731    for (${ident}_Event event = ${ident}_Event_FIRST;
732         event < ${ident}_Event_NUM; ++event) {
733        for (unsigned int i = 0; i < m_num_controllers; ++i) {
734            std::map<uint32_t, AbstractController *>::iterator it =
735                                g_abs_controls[MachineType_${ident}].find(i);
736            assert(it != g_abs_controls[MachineType_${ident}].end());
737            (*eventVec[event])[i] =
738                (($c_ident *)(*it).second)->getEventCount(event);
739        }
740    }
741
742    for (${ident}_State state = ${ident}_State_FIRST;
743         state < ${ident}_State_NUM; ++state) {
744
745        for (${ident}_Event event = ${ident}_Event_FIRST;
746             event < ${ident}_Event_NUM; ++event) {
747
748            for (unsigned int i = 0; i < m_num_controllers; ++i) {
749                std::map<uint32_t, AbstractController *>::iterator it =
750                                g_abs_controls[MachineType_${ident}].find(i);
751                assert(it != g_abs_controls[MachineType_${ident}].end());
752                (*transVec[state][event])[i] =
753                    (($c_ident *)(*it).second)->getTransitionCount(state, event);
754            }
755        }
756    }
757}
758
759void
760$c_ident::countTransition(${ident}_State state, ${ident}_Event event)
761{
762    assert(m_possible[state][event]);
763    m_counters[state][event]++;
764    m_event_counters[event]++;
765}
766void
767$c_ident::possibleTransition(${ident}_State state,
768                             ${ident}_Event event)
769{
770    m_possible[state][event] = true;
771}
772
773uint64
774$c_ident::getEventCount(${ident}_Event event)
775{
776    return m_event_counters[event];
777}
778
779bool
780$c_ident::isPossible(${ident}_State state, ${ident}_Event event)
781{
782    return m_possible[state][event];
783}
784
785uint64
786$c_ident::getTransitionCount(${ident}_State state,
787                             ${ident}_Event event)
788{
789    return m_counters[state][event];
790}
791
792int
793$c_ident::getNumControllers()
794{
795    return m_num_controllers;
796}
797
798MessageBuffer*
799$c_ident::getMandatoryQueue() const
800{
801    return $mq_ident;
802}
803
804Sequencer*
805$c_ident::getSequencer() const
806{
807    return $seq_ident;
808}
809
810const string
811$c_ident::toString() const
812{
813    return "$c_ident";
814}
815
816void
817$c_ident::print(ostream& out) const
818{
819    out << "[$c_ident " << m_version << "]";
820}
821
822void $c_ident::resetStats()
823{
824    for (int state = 0; state < ${ident}_State_NUM; state++) {
825        for (int event = 0; event < ${ident}_Event_NUM; event++) {
826            m_counters[state][event] = 0;
827        }
828    }
829
830    for (int event = 0; event < ${ident}_Event_NUM; event++) {
831        m_event_counters[event] = 0;
832    }
833
834    AbstractController::resetStats();
835}
836''')
837
838        if self.EntryType != None:
839            code('''
840
841// Set and Reset for cache_entry variable
842void
843$c_ident::set_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, AbstractCacheEntry* m_new_cache_entry)
844{
845  m_cache_entry_ptr = (${{self.EntryType.c_ident}}*)m_new_cache_entry;
846}
847
848void
849$c_ident::unset_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr)
850{
851  m_cache_entry_ptr = 0;
852}
853''')
854
855        if self.TBEType != None:
856            code('''
857
858// Set and Reset for tbe variable
859void
860$c_ident::set_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.TBEType.c_ident}}* m_new_tbe)
861{
862  m_tbe_ptr = m_new_tbe;
863}
864
865void
866$c_ident::unset_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr)
867{
868  m_tbe_ptr = NULL;
869}
870''')
871
872        code('''
873
874void
875$c_ident::recordCacheTrace(int cntrl, CacheRecorder* tr)
876{
877''')
878        #
879        # Record cache contents for all associated caches.
880        #
881        code.indent()
882        for param in self.config_parameters:
883            if param.type_ast.type.ident == "CacheMemory":
884                assert(param.pointer)
885                code('m_${{param.ident}}_ptr->recordCacheContents(cntrl, tr);')
886
887        code.dedent()
888        code('''
889}
890
891// Actions
892''')
893        if self.TBEType != None and self.EntryType != None:
894            for action in self.actions.itervalues():
895                if "c_code" not in action:
896                 continue
897
898                code('''
899/** \\brief ${{action.desc}} */
900void
901$c_ident::${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr)
902{
903    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
904    ${{action["c_code"]}}
905}
906
907''')
908        elif self.TBEType != None:
909            for action in self.actions.itervalues():
910                if "c_code" not in action:
911                 continue
912
913                code('''
914/** \\brief ${{action.desc}} */
915void
916$c_ident::${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, const Address& addr)
917{
918    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
919    ${{action["c_code"]}}
920}
921
922''')
923        elif self.EntryType != None:
924            for action in self.actions.itervalues():
925                if "c_code" not in action:
926                 continue
927
928                code('''
929/** \\brief ${{action.desc}} */
930void
931$c_ident::${{action.ident}}(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr)
932{
933    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
934    ${{action["c_code"]}}
935}
936
937''')
938        else:
939            for action in self.actions.itervalues():
940                if "c_code" not in action:
941                 continue
942
943                code('''
944/** \\brief ${{action.desc}} */
945void
946$c_ident::${{action.ident}}(const Address& addr)
947{
948    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
949    ${{action["c_code"]}}
950}
951
952''')
953        for func in self.functions:
954            code(func.generateCode())
955
956        # Function for functional reads from messages buffered in the controller
957        code('''
958bool
959$c_ident::functionalReadBuffers(PacketPtr& pkt)
960{
961''')
962        for var in self.objects:
963            vtype = var.type
964            if vtype.isBuffer:
965                vid = "m_%s_ptr" % var.c_ident
966                code('if ($vid->functionalRead(pkt)) { return true; }')
967        code('''
968                return false;
969}
970''')
971
972        # Function for functional writes to messages buffered in the controller
973        code('''
974uint32_t
975$c_ident::functionalWriteBuffers(PacketPtr& pkt)
976{
977    uint32_t num_functional_writes = 0;
978''')
979        for var in self.objects:
980            vtype = var.type
981            if vtype.isBuffer:
982                vid = "m_%s_ptr" % var.c_ident
983                code('num_functional_writes += $vid->functionalWrite(pkt);')
984        code('''
985    return num_functional_writes;
986}
987''')
988
989        # Check if this controller has a peer, if yes then write the
990        # function for connecting to the peer.
991        if has_peer:
992            code('''
993
994void
995$c_ident::getQueuesFromPeer(AbstractController *peer)
996{
997''')
998            for var in self.objects:
999                if "network" in var and "physical_network" in var and \
1000                   var["network"] == "From":
1001                    code('''
1002m_${{var.c_ident}}_ptr = peer->getPeerQueue(${{var["physical_network"]}});
1003assert(m_${{var.c_ident}}_ptr != NULL);
1004m_${{var.c_ident}}_ptr->setReceiver(this);
1005
1006''')
1007            code('}')
1008
1009        code.write(path, "%s.cc" % c_ident)
1010
1011    def printCWakeup(self, path, includes):
1012        '''Output the wakeup loop for the events'''
1013
1014        code = self.symtab.codeFormatter()
1015        ident = self.ident
1016
1017        outputRequest_types = True
1018        if len(self.request_types) == 0:
1019            outputRequest_types = False
1020
1021        code('''
1022// Auto generated C++ code started by $__file__:$__line__
1023// ${ident}: ${{self.short}}
1024
1025#include <sys/types.h>
1026#include <unistd.h>
1027
1028#include <cassert>
1029
1030#include "base/misc.hh"
1031#include "debug/RubySlicc.hh"
1032#include "mem/protocol/${ident}_Controller.hh"
1033#include "mem/protocol/${ident}_Event.hh"
1034#include "mem/protocol/${ident}_State.hh"
1035''')
1036
1037        if outputRequest_types:
1038            code('''#include "mem/protocol/${ident}_RequestType.hh"''')
1039
1040        code('''
1041#include "mem/protocol/Types.hh"
1042#include "mem/ruby/common/Global.hh"
1043#include "mem/ruby/system/System.hh"
1044''')
1045
1046
1047        for include_path in includes:
1048            code('#include "${{include_path}}"')
1049
1050        code('''
1051
1052using namespace std;
1053
1054void
1055${ident}_Controller::wakeup()
1056{
1057    int counter = 0;
1058    while (true) {
1059        // Some cases will put us into an infinite loop without this limit
1060        assert(counter <= m_transitions_per_cycle);
1061        if (counter == m_transitions_per_cycle) {
1062            // Count how often we are fully utilized
1063            m_fully_busy_cycles++;
1064
1065            // Wakeup in another cycle and try again
1066            scheduleEvent(Cycles(1));
1067            break;
1068        }
1069''')
1070
1071        code.indent()
1072        code.indent()
1073
1074        # InPorts
1075        #
1076        for port in self.in_ports:
1077            code.indent()
1078            code('// ${ident}InPort $port')
1079            if port.pairs.has_key("rank"):
1080                code('m_cur_in_port = ${{port.pairs["rank"]}};')
1081            else:
1082                code('m_cur_in_port = 0;')
1083            code('${{port["c_code_in_port"]}}')
1084            code.dedent()
1085
1086            code('')
1087
1088        code.dedent()
1089        code.dedent()
1090        code('''
1091        break;  // If we got this far, we have nothing left todo
1092    }
1093}
1094''')
1095
1096        code.write(path, "%s_Wakeup.cc" % self.ident)
1097
1098    def printCSwitch(self, path):
1099        '''Output switch statement for transition table'''
1100
1101        code = self.symtab.codeFormatter()
1102        ident = self.ident
1103
1104        code('''
1105// Auto generated C++ code started by $__file__:$__line__
1106// ${ident}: ${{self.short}}
1107
1108#include <cassert>
1109
1110#include "base/misc.hh"
1111#include "base/trace.hh"
1112#include "debug/ProtocolTrace.hh"
1113#include "debug/RubyGenerated.hh"
1114#include "mem/protocol/${ident}_Controller.hh"
1115#include "mem/protocol/${ident}_Event.hh"
1116#include "mem/protocol/${ident}_State.hh"
1117#include "mem/protocol/Types.hh"
1118#include "mem/ruby/common/Global.hh"
1119#include "mem/ruby/system/System.hh"
1120
1121#define HASH_FUN(state, event)  ((int(state)*${ident}_Event_NUM)+int(event))
1122
1123#define GET_TRANSITION_COMMENT() (${ident}_transitionComment.str())
1124#define CLEAR_TRANSITION_COMMENT() (${ident}_transitionComment.str(""))
1125
1126TransitionResult
1127${ident}_Controller::doTransition(${ident}_Event event,
1128''')
1129        if self.EntryType != None:
1130            code('''
1131                                  ${{self.EntryType.c_ident}}* m_cache_entry_ptr,
1132''')
1133        if self.TBEType != None:
1134            code('''
1135                                  ${{self.TBEType.c_ident}}* m_tbe_ptr,
1136''')
1137        code('''
1138                                  const Address addr)
1139{
1140''')
1141        if self.TBEType != None and self.EntryType != None:
1142            code('${ident}_State state = getState(m_tbe_ptr, m_cache_entry_ptr, addr);')
1143        elif self.TBEType != None:
1144            code('${ident}_State state = getState(m_tbe_ptr, addr);')
1145        elif self.EntryType != None:
1146            code('${ident}_State state = getState(m_cache_entry_ptr, addr);')
1147        else:
1148            code('${ident}_State state = getState(addr);')
1149
1150        code('''
1151    ${ident}_State next_state = state;
1152
1153    DPRINTF(RubyGenerated, "%s, Time: %lld, state: %s, event: %s, addr: %s\\n",
1154            *this, curCycle(), ${ident}_State_to_string(state),
1155            ${ident}_Event_to_string(event), addr);
1156
1157    TransitionResult result =
1158''')
1159        if self.TBEType != None and self.EntryType != None:
1160            code('doTransitionWorker(event, state, next_state, m_tbe_ptr, m_cache_entry_ptr, addr);')
1161        elif self.TBEType != None:
1162            code('doTransitionWorker(event, state, next_state, m_tbe_ptr, addr);')
1163        elif self.EntryType != None:
1164            code('doTransitionWorker(event, state, next_state, m_cache_entry_ptr, addr);')
1165        else:
1166            code('doTransitionWorker(event, state, next_state, addr);')
1167
1168        code('''
1169    if (result == TransitionResult_Valid) {
1170        DPRINTF(RubyGenerated, "next_state: %s\\n",
1171                ${ident}_State_to_string(next_state));
1172        countTransition(state, event);
1173        DPRINTFR(ProtocolTrace, "%15d %3s %10s%20s %6s>%-6s %s %s\\n",
1174                 curTick(), m_version, "${ident}",
1175                 ${ident}_Event_to_string(event),
1176                 ${ident}_State_to_string(state),
1177                 ${ident}_State_to_string(next_state),
1178                 addr, GET_TRANSITION_COMMENT());
1179
1180        CLEAR_TRANSITION_COMMENT();
1181''')
1182        if self.TBEType != None and self.EntryType != None:
1183            code('setState(m_tbe_ptr, m_cache_entry_ptr, addr, next_state);')
1184            code('setAccessPermission(m_cache_entry_ptr, addr, next_state);')
1185        elif self.TBEType != None:
1186            code('setState(m_tbe_ptr, addr, next_state);')
1187            code('setAccessPermission(addr, next_state);')
1188        elif self.EntryType != None:
1189            code('setState(m_cache_entry_ptr, addr, next_state);')
1190            code('setAccessPermission(m_cache_entry_ptr, addr, next_state);')
1191        else:
1192            code('setState(addr, next_state);')
1193            code('setAccessPermission(addr, next_state);')
1194
1195        code('''
1196    } else if (result == TransitionResult_ResourceStall) {
1197        DPRINTFR(ProtocolTrace, "%15s %3s %10s%20s %6s>%-6s %s %s\\n",
1198                 curTick(), m_version, "${ident}",
1199                 ${ident}_Event_to_string(event),
1200                 ${ident}_State_to_string(state),
1201                 ${ident}_State_to_string(next_state),
1202                 addr, "Resource Stall");
1203    } else if (result == TransitionResult_ProtocolStall) {
1204        DPRINTF(RubyGenerated, "stalling\\n");
1205        DPRINTFR(ProtocolTrace, "%15s %3s %10s%20s %6s>%-6s %s %s\\n",
1206                 curTick(), m_version, "${ident}",
1207                 ${ident}_Event_to_string(event),
1208                 ${ident}_State_to_string(state),
1209                 ${ident}_State_to_string(next_state),
1210                 addr, "Protocol Stall");
1211    }
1212
1213    return result;
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')
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    return TransitionResult_Valid;
1329}
1330''')
1331        code.write(path, "%s_Transitions.cc" % self.ident)
1332
1333
1334    # **************************
1335    # ******* HTML Files *******
1336    # **************************
1337    def frameRef(self, click_href, click_target, over_href, over_num, text):
1338        code = self.symtab.codeFormatter(fix_newlines=False)
1339        code("""<A href=\"$click_href\" target=\"$click_target\" onmouseover=\"
1340    if (parent.frames[$over_num].location != parent.location + '$over_href') {
1341        parent.frames[$over_num].location='$over_href'
1342    }\">
1343    ${{html.formatShorthand(text)}}
1344    </A>""")
1345        return str(code)
1346
1347    def writeHTMLFiles(self, path):
1348        # Create table with no row hilighted
1349        self.printHTMLTransitions(path, None)
1350
1351        # Generate transition tables
1352        for state in self.states.itervalues():
1353            self.printHTMLTransitions(path, state)
1354
1355        # Generate action descriptions
1356        for action in self.actions.itervalues():
1357            name = "%s_action_%s.html" % (self.ident, action.ident)
1358            code = html.createSymbol(action, "Action")
1359            code.write(path, name)
1360
1361        # Generate state descriptions
1362        for state in self.states.itervalues():
1363            name = "%s_State_%s.html" % (self.ident, state.ident)
1364            code = html.createSymbol(state, "State")
1365            code.write(path, name)
1366
1367        # Generate event descriptions
1368        for event in self.events.itervalues():
1369            name = "%s_Event_%s.html" % (self.ident, event.ident)
1370            code = html.createSymbol(event, "Event")
1371            code.write(path, name)
1372
1373    def printHTMLTransitions(self, path, active_state):
1374        code = self.symtab.codeFormatter()
1375
1376        code('''
1377<HTML>
1378<BODY link="blue" vlink="blue">
1379
1380<H1 align="center">${{html.formatShorthand(self.short)}}:
1381''')
1382        code.indent()
1383        for i,machine in enumerate(self.symtab.getAllType(StateMachine)):
1384            mid = machine.ident
1385            if i != 0:
1386                extra = " - "
1387            else:
1388                extra = ""
1389            if machine == self:
1390                code('$extra$mid')
1391            else:
1392                code('$extra<A target="Table" href="${mid}_table.html">$mid</A>')
1393        code.dedent()
1394
1395        code("""
1396</H1>
1397
1398<TABLE border=1>
1399<TR>
1400  <TH> </TH>
1401""")
1402
1403        for event in self.events.itervalues():
1404            href = "%s_Event_%s.html" % (self.ident, event.ident)
1405            ref = self.frameRef(href, "Status", href, "1", event.short)
1406            code('<TH bgcolor=white>$ref</TH>')
1407
1408        code('</TR>')
1409        # -- Body of table
1410        for state in self.states.itervalues():
1411            # -- Each row
1412            if state == active_state:
1413                color = "yellow"
1414            else:
1415                color = "white"
1416
1417            click = "%s_table_%s.html" % (self.ident, state.ident)
1418            over = "%s_State_%s.html" % (self.ident, state.ident)
1419            text = html.formatShorthand(state.short)
1420            ref = self.frameRef(click, "Table", over, "1", state.short)
1421            code('''
1422<TR>
1423  <TH bgcolor=$color>$ref</TH>
1424''')
1425
1426            # -- One column for each event
1427            for event in self.events.itervalues():
1428                trans = self.table.get((state,event), None)
1429                if trans is None:
1430                    # This is the no transition case
1431                    if state == active_state:
1432                        color = "#C0C000"
1433                    else:
1434                        color = "lightgrey"
1435
1436                    code('<TD bgcolor=$color>&nbsp;</TD>')
1437                    continue
1438
1439                next = trans.nextState
1440                stall_action = False
1441
1442                # -- Get the actions
1443                for action in trans.actions:
1444                    if action.ident == "z_stall" or \
1445                       action.ident == "zz_recycleMandatoryQueue":
1446                        stall_action = True
1447
1448                # -- Print out "actions/next-state"
1449                if stall_action:
1450                    if state == active_state:
1451                        color = "#C0C000"
1452                    else:
1453                        color = "lightgrey"
1454
1455                elif active_state and next.ident == active_state.ident:
1456                    color = "aqua"
1457                elif state == active_state:
1458                    color = "yellow"
1459                else:
1460                    color = "white"
1461
1462                code('<TD bgcolor=$color>')
1463                for action in trans.actions:
1464                    href = "%s_action_%s.html" % (self.ident, action.ident)
1465                    ref = self.frameRef(href, "Status", href, "1",
1466                                        action.short)
1467                    code('  $ref')
1468                if next != state:
1469                    if trans.actions:
1470                        code('/')
1471                    click = "%s_table_%s.html" % (self.ident, next.ident)
1472                    over = "%s_State_%s.html" % (self.ident, next.ident)
1473                    ref = self.frameRef(click, "Table", over, "1", next.short)
1474                    code("$ref")
1475                code("</TD>")
1476
1477            # -- Each row
1478            if state == active_state:
1479                color = "yellow"
1480            else:
1481                color = "white"
1482
1483            click = "%s_table_%s.html" % (self.ident, state.ident)
1484            over = "%s_State_%s.html" % (self.ident, state.ident)
1485            ref = self.frameRef(click, "Table", over, "1", state.short)
1486            code('''
1487  <TH bgcolor=$color>$ref</TH>
1488</TR>
1489''')
1490        code('''
1491<!- Column footer->
1492<TR>
1493  <TH> </TH>
1494''')
1495
1496        for event in self.events.itervalues():
1497            href = "%s_Event_%s.html" % (self.ident, event.ident)
1498            ref = self.frameRef(href, "Status", href, "1", event.short)
1499            code('<TH bgcolor=white>$ref</TH>')
1500        code('''
1501</TR>
1502</TABLE>
1503</BODY></HTML>
1504''')
1505
1506
1507        if active_state:
1508            name = "%s_table_%s.html" % (self.ident, active_state.ident)
1509        else:
1510            name = "%s_table.html" % self.ident
1511        code.write(path, name)
1512
1513__all__ = [ "StateMachine" ]
1514