StateMachine.py revision 10121:64545628f5a7
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                if key.type.ident != "DNUCAStopTable":
1257                    val = '''
1258if (!%s.areNSlotsAvailable(%s))
1259    return TransitionResult_ResourceStall;
1260''' % (key.code, val)
1261                case_sorter.append(val)
1262
1263            # Check all of the request_types for resource constraints
1264            for request_type in request_types:
1265                val = '''
1266if (!checkResourceAvailable(%s_RequestType_%s, addr)) {
1267    return TransitionResult_ResourceStall;
1268}
1269''' % (self.ident, request_type.ident)
1270                case_sorter.append(val)
1271
1272            # Emit the code sequences in a sorted order.  This makes the
1273            # output deterministic (without this the output order can vary
1274            # since Map's keys() on a vector of pointers is not deterministic
1275            for c in sorted(case_sorter):
1276                case("$c")
1277
1278            # Record access types for this transition
1279            for request_type in request_types:
1280                case('recordRequestType(${ident}_RequestType_${{request_type.ident}}, addr);')
1281
1282            # Figure out if we stall
1283            stall = False
1284            for action in actions:
1285                if action.ident == "z_stall":
1286                    stall = True
1287                    break
1288
1289            if stall:
1290                case('return TransitionResult_ProtocolStall;')
1291            else:
1292                if self.TBEType != None and self.EntryType != None:
1293                    for action in actions:
1294                        case('${{action.ident}}(m_tbe_ptr, m_cache_entry_ptr, addr);')
1295                elif self.TBEType != None:
1296                    for action in actions:
1297                        case('${{action.ident}}(m_tbe_ptr, addr);')
1298                elif self.EntryType != None:
1299                    for action in actions:
1300                        case('${{action.ident}}(m_cache_entry_ptr, addr);')
1301                else:
1302                    for action in actions:
1303                        case('${{action.ident}}(addr);')
1304                case('return TransitionResult_Valid;')
1305
1306            case = str(case)
1307
1308            # Look to see if this transition code is unique.
1309            if case not in cases:
1310                cases[case] = []
1311
1312            cases[case].append(case_string)
1313
1314        # Walk through all of the unique code blocks and spit out the
1315        # corresponding case statement elements
1316        for case,transitions in cases.iteritems():
1317            # Iterative over all the multiple transitions that share
1318            # the same code
1319            for trans in transitions:
1320                code('  case HASH_FUN($trans):')
1321            code('    $case')
1322
1323        code('''
1324      default:
1325        fatal("Invalid transition\\n"
1326              "%s time: %d addr: %s event: %s state: %s\\n",
1327              name(), curCycle(), addr, event, state);
1328    }
1329    return TransitionResult_Valid;
1330}
1331''')
1332        code.write(path, "%s_Transitions.cc" % self.ident)
1333
1334
1335    # **************************
1336    # ******* HTML Files *******
1337    # **************************
1338    def frameRef(self, click_href, click_target, over_href, over_num, text):
1339        code = self.symtab.codeFormatter(fix_newlines=False)
1340        code("""<A href=\"$click_href\" target=\"$click_target\" onmouseover=\"
1341    if (parent.frames[$over_num].location != parent.location + '$over_href') {
1342        parent.frames[$over_num].location='$over_href'
1343    }\">
1344    ${{html.formatShorthand(text)}}
1345    </A>""")
1346        return str(code)
1347
1348    def writeHTMLFiles(self, path):
1349        # Create table with no row hilighted
1350        self.printHTMLTransitions(path, None)
1351
1352        # Generate transition tables
1353        for state in self.states.itervalues():
1354            self.printHTMLTransitions(path, state)
1355
1356        # Generate action descriptions
1357        for action in self.actions.itervalues():
1358            name = "%s_action_%s.html" % (self.ident, action.ident)
1359            code = html.createSymbol(action, "Action")
1360            code.write(path, name)
1361
1362        # Generate state descriptions
1363        for state in self.states.itervalues():
1364            name = "%s_State_%s.html" % (self.ident, state.ident)
1365            code = html.createSymbol(state, "State")
1366            code.write(path, name)
1367
1368        # Generate event descriptions
1369        for event in self.events.itervalues():
1370            name = "%s_Event_%s.html" % (self.ident, event.ident)
1371            code = html.createSymbol(event, "Event")
1372            code.write(path, name)
1373
1374    def printHTMLTransitions(self, path, active_state):
1375        code = self.symtab.codeFormatter()
1376
1377        code('''
1378<HTML>
1379<BODY link="blue" vlink="blue">
1380
1381<H1 align="center">${{html.formatShorthand(self.short)}}:
1382''')
1383        code.indent()
1384        for i,machine in enumerate(self.symtab.getAllType(StateMachine)):
1385            mid = machine.ident
1386            if i != 0:
1387                extra = " - "
1388            else:
1389                extra = ""
1390            if machine == self:
1391                code('$extra$mid')
1392            else:
1393                code('$extra<A target="Table" href="${mid}_table.html">$mid</A>')
1394        code.dedent()
1395
1396        code("""
1397</H1>
1398
1399<TABLE border=1>
1400<TR>
1401  <TH> </TH>
1402""")
1403
1404        for event in self.events.itervalues():
1405            href = "%s_Event_%s.html" % (self.ident, event.ident)
1406            ref = self.frameRef(href, "Status", href, "1", event.short)
1407            code('<TH bgcolor=white>$ref</TH>')
1408
1409        code('</TR>')
1410        # -- Body of table
1411        for state in self.states.itervalues():
1412            # -- Each row
1413            if state == active_state:
1414                color = "yellow"
1415            else:
1416                color = "white"
1417
1418            click = "%s_table_%s.html" % (self.ident, state.ident)
1419            over = "%s_State_%s.html" % (self.ident, state.ident)
1420            text = html.formatShorthand(state.short)
1421            ref = self.frameRef(click, "Table", over, "1", state.short)
1422            code('''
1423<TR>
1424  <TH bgcolor=$color>$ref</TH>
1425''')
1426
1427            # -- One column for each event
1428            for event in self.events.itervalues():
1429                trans = self.table.get((state,event), None)
1430                if trans is None:
1431                    # This is the no transition case
1432                    if state == active_state:
1433                        color = "#C0C000"
1434                    else:
1435                        color = "lightgrey"
1436
1437                    code('<TD bgcolor=$color>&nbsp;</TD>')
1438                    continue
1439
1440                next = trans.nextState
1441                stall_action = False
1442
1443                # -- Get the actions
1444                for action in trans.actions:
1445                    if action.ident == "z_stall" or \
1446                       action.ident == "zz_recycleMandatoryQueue":
1447                        stall_action = True
1448
1449                # -- Print out "actions/next-state"
1450                if stall_action:
1451                    if state == active_state:
1452                        color = "#C0C000"
1453                    else:
1454                        color = "lightgrey"
1455
1456                elif active_state and next.ident == active_state.ident:
1457                    color = "aqua"
1458                elif state == active_state:
1459                    color = "yellow"
1460                else:
1461                    color = "white"
1462
1463                code('<TD bgcolor=$color>')
1464                for action in trans.actions:
1465                    href = "%s_action_%s.html" % (self.ident, action.ident)
1466                    ref = self.frameRef(href, "Status", href, "1",
1467                                        action.short)
1468                    code('  $ref')
1469                if next != state:
1470                    if trans.actions:
1471                        code('/')
1472                    click = "%s_table_%s.html" % (self.ident, next.ident)
1473                    over = "%s_State_%s.html" % (self.ident, next.ident)
1474                    ref = self.frameRef(click, "Table", over, "1", next.short)
1475                    code("$ref")
1476                code("</TD>")
1477
1478            # -- Each row
1479            if state == active_state:
1480                color = "yellow"
1481            else:
1482                color = "white"
1483
1484            click = "%s_table_%s.html" % (self.ident, state.ident)
1485            over = "%s_State_%s.html" % (self.ident, state.ident)
1486            ref = self.frameRef(click, "Table", over, "1", state.short)
1487            code('''
1488  <TH bgcolor=$color>$ref</TH>
1489</TR>
1490''')
1491        code('''
1492<!- Column footer->
1493<TR>
1494  <TH> </TH>
1495''')
1496
1497        for event in self.events.itervalues():
1498            href = "%s_Event_%s.html" % (self.ident, event.ident)
1499            ref = self.frameRef(href, "Status", href, "1", event.short)
1500            code('<TH bgcolor=white>$ref</TH>')
1501        code('''
1502</TR>
1503</TABLE>
1504</BODY></HTML>
1505''')
1506
1507
1508        if active_state:
1509            name = "%s_table_%s.html" % (self.ident, active_state.ident)
1510        else:
1511            name = "%s_table.html" % self.ident
1512        code.write(path, name)
1513
1514__all__ = [ "StateMachine" ]
1515