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