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