StateMachine.py revision 10307:6df951dcd7d9
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.symtab.registerSym(str(obj), obj)
124        self.objects.append(obj)
125
126    def addType(self, type):
127        type_ident = '%s' % type.c_ident
128
129        if type_ident == "%s_TBE" %self.ident:
130            if self.TBEType != None:
131                self.error("Multiple Transaction Buffer types in a " \
132                           "single machine.");
133            self.TBEType = type
134
135        elif "interface" in type and "AbstractCacheEntry" == type["interface"]:
136            if self.EntryType != None:
137                self.error("Multiple AbstractCacheEntry types in a " \
138                           "single machine.");
139            self.EntryType = type
140
141    # Needs to be called before accessing the table
142    def buildTable(self):
143        assert self.table is None
144
145        table = {}
146
147        for trans in self.transitions:
148            # Track which actions we touch so we know if we use them
149            # all -- really this should be done for all symbols as
150            # part of the symbol table, then only trigger it for
151            # Actions, States, Events, etc.
152
153            for action in trans.actions:
154                action.used = True
155
156            index = (trans.state, trans.event)
157            if index in table:
158                table[index].warning("Duplicate transition: %s" % table[index])
159                trans.error("Duplicate transition: %s" % trans)
160            table[index] = trans
161
162        # Look at all actions to make sure we used them all
163        for action in self.actions.itervalues():
164            if not action.used:
165                error_msg = "Unused action: %s" % action.ident
166                if "desc" in action:
167                    error_msg += ", "  + action.desc
168                action.warning(error_msg)
169        self.table = table
170
171    def writeCodeFiles(self, path, includes):
172        self.printControllerPython(path)
173        self.printControllerHH(path)
174        self.printControllerCC(path, includes)
175        self.printCSwitch(path)
176        self.printCWakeup(path, includes)
177
178    def printControllerPython(self, path):
179        code = self.symtab.codeFormatter()
180        ident = self.ident
181        py_ident = "%s_Controller" % ident
182        c_ident = "%s_Controller" % self.ident
183        code('''
184from m5.params import *
185from m5.SimObject import SimObject
186from Controller import RubyController
187
188class $py_ident(RubyController):
189    type = '$py_ident'
190    cxx_header = 'mem/protocol/${c_ident}.hh'
191''')
192        code.indent()
193        for param in self.config_parameters:
194            dflt_str = ''
195            if param.default is not None:
196                dflt_str = str(param.default) + ', '
197            if python_class_map.has_key(param.type_ast.type.c_ident):
198                python_type = python_class_map[param.type_ast.type.c_ident]
199                code('${{param.name}} = Param.${{python_type}}(${dflt_str}"")')
200            else:
201                self.error("Unknown c++ to python class conversion for c++ " \
202                           "type: '%s'. Please update the python_class_map " \
203                           "in StateMachine.py", param.type_ast.type.c_ident)
204        code.dedent()
205        code.write(path, '%s.py' % py_ident)
206
207
208    def printControllerHH(self, path):
209        '''Output the method declarations for the class declaration'''
210        code = self.symtab.codeFormatter()
211        ident = self.ident
212        c_ident = "%s_Controller" % self.ident
213
214        code('''
215/** \\file $c_ident.hh
216 *
217 * Auto generated C++ code started by $__file__:$__line__
218 * Created by slicc definition of Module "${{self.short}}"
219 */
220
221#ifndef __${ident}_CONTROLLER_HH__
222#define __${ident}_CONTROLLER_HH__
223
224#include <iostream>
225#include <sstream>
226#include <string>
227
228#include "mem/protocol/TransitionResult.hh"
229#include "mem/protocol/Types.hh"
230#include "mem/ruby/common/Consumer.hh"
231#include "mem/ruby/common/Global.hh"
232#include "mem/ruby/slicc_interface/AbstractController.hh"
233#include "params/$c_ident.hh"
234''')
235
236        seen_types = set()
237        has_peer = False
238        for var in self.objects:
239            if var.type.ident not in seen_types and not var.type.isPrimitive:
240                code('#include "mem/protocol/${{var.type.c_ident}}.hh"')
241            if "network" in var and "physical_network" in var:
242                has_peer = True
243            seen_types.add(var.type.ident)
244
245        # for adding information to the protocol debug trace
246        code('''
247extern std::stringstream ${ident}_transitionComment;
248
249class $c_ident : public AbstractController
250{
251  public:
252    typedef ${c_ident}Params Params;
253    $c_ident(const Params *p);
254    static int getNumControllers();
255    void init();
256    MessageBuffer* getMandatoryQueue() 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.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.ident}}_ptr = new ${{var.type.c_ident}}();
493m_${{var.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.ident}}_ptr = new ${{var.type.c_ident}}();
501peerQueueMap[${{var["physical_network"]}}] = m_${{var.ident}}_ptr;
502m_${{var.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.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.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_mandatoryQueue_ptr"
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
810void
811$c_ident::print(ostream& out) const
812{
813    out << "[$c_ident " << m_version << "]";
814}
815
816void $c_ident::resetStats()
817{
818    for (int state = 0; state < ${ident}_State_NUM; state++) {
819        for (int event = 0; event < ${ident}_Event_NUM; event++) {
820            m_counters[state][event] = 0;
821        }
822    }
823
824    for (int event = 0; event < ${ident}_Event_NUM; event++) {
825        m_event_counters[event] = 0;
826    }
827
828    AbstractController::resetStats();
829}
830''')
831
832        if self.EntryType != None:
833            code('''
834
835// Set and Reset for cache_entry variable
836void
837$c_ident::set_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, AbstractCacheEntry* m_new_cache_entry)
838{
839  m_cache_entry_ptr = (${{self.EntryType.c_ident}}*)m_new_cache_entry;
840}
841
842void
843$c_ident::unset_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr)
844{
845  m_cache_entry_ptr = 0;
846}
847''')
848
849        if self.TBEType != None:
850            code('''
851
852// Set and Reset for tbe variable
853void
854$c_ident::set_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.TBEType.c_ident}}* m_new_tbe)
855{
856  m_tbe_ptr = m_new_tbe;
857}
858
859void
860$c_ident::unset_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr)
861{
862  m_tbe_ptr = NULL;
863}
864''')
865
866        code('''
867
868void
869$c_ident::recordCacheTrace(int cntrl, CacheRecorder* tr)
870{
871''')
872        #
873        # Record cache contents for all associated caches.
874        #
875        code.indent()
876        for param in self.config_parameters:
877            if param.type_ast.type.ident == "CacheMemory":
878                assert(param.pointer)
879                code('m_${{param.ident}}_ptr->recordCacheContents(cntrl, tr);')
880
881        code.dedent()
882        code('''
883}
884
885// Actions
886''')
887        if self.TBEType != None and self.EntryType != None:
888            for action in self.actions.itervalues():
889                if "c_code" not in action:
890                 continue
891
892                code('''
893/** \\brief ${{action.desc}} */
894void
895$c_ident::${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr)
896{
897    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
898    ${{action["c_code"]}}
899}
900
901''')
902        elif self.TBEType != None:
903            for action in self.actions.itervalues():
904                if "c_code" not in action:
905                 continue
906
907                code('''
908/** \\brief ${{action.desc}} */
909void
910$c_ident::${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, const Address& addr)
911{
912    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
913    ${{action["c_code"]}}
914}
915
916''')
917        elif self.EntryType != None:
918            for action in self.actions.itervalues():
919                if "c_code" not in action:
920                 continue
921
922                code('''
923/** \\brief ${{action.desc}} */
924void
925$c_ident::${{action.ident}}(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr)
926{
927    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
928    ${{action["c_code"]}}
929}
930
931''')
932        else:
933            for action in self.actions.itervalues():
934                if "c_code" not in action:
935                 continue
936
937                code('''
938/** \\brief ${{action.desc}} */
939void
940$c_ident::${{action.ident}}(const Address& addr)
941{
942    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
943    ${{action["c_code"]}}
944}
945
946''')
947        for func in self.functions:
948            code(func.generateCode())
949
950        # Function for functional reads from messages buffered in the controller
951        code('''
952bool
953$c_ident::functionalReadBuffers(PacketPtr& pkt)
954{
955''')
956        for var in self.objects:
957            vtype = var.type
958            if vtype.isBuffer:
959                vid = "m_%s_ptr" % var.ident
960                code('if ($vid->functionalRead(pkt)) { return true; }')
961        code('''
962                return false;
963}
964''')
965
966        # Function for functional writes to messages buffered in the controller
967        code('''
968uint32_t
969$c_ident::functionalWriteBuffers(PacketPtr& pkt)
970{
971    uint32_t num_functional_writes = 0;
972''')
973        for var in self.objects:
974            vtype = var.type
975            if vtype.isBuffer:
976                vid = "m_%s_ptr" % var.ident
977                code('num_functional_writes += $vid->functionalWrite(pkt);')
978        code('''
979    return num_functional_writes;
980}
981''')
982
983        # Check if this controller has a peer, if yes then write the
984        # function for connecting to the peer.
985        if has_peer:
986            code('''
987
988void
989$c_ident::getQueuesFromPeer(AbstractController *peer)
990{
991''')
992            for var in self.objects:
993                if "network" in var and "physical_network" in var and \
994                   var["network"] == "From":
995                    code('''
996m_${{var.ident}}_ptr = peer->getPeerQueue(${{var["physical_network"]}});
997assert(m_${{var.ident}}_ptr != NULL);
998m_${{var.ident}}_ptr->setReceiver(this);
999
1000''')
1001            code('}')
1002
1003        code.write(path, "%s.cc" % c_ident)
1004
1005    def printCWakeup(self, path, includes):
1006        '''Output the wakeup loop for the events'''
1007
1008        code = self.symtab.codeFormatter()
1009        ident = self.ident
1010
1011        outputRequest_types = True
1012        if len(self.request_types) == 0:
1013            outputRequest_types = False
1014
1015        code('''
1016// Auto generated C++ code started by $__file__:$__line__
1017// ${ident}: ${{self.short}}
1018
1019#include <sys/types.h>
1020#include <unistd.h>
1021
1022#include <cassert>
1023
1024#include "base/misc.hh"
1025#include "debug/RubySlicc.hh"
1026#include "mem/protocol/${ident}_Controller.hh"
1027#include "mem/protocol/${ident}_Event.hh"
1028#include "mem/protocol/${ident}_State.hh"
1029''')
1030
1031        if outputRequest_types:
1032            code('''#include "mem/protocol/${ident}_RequestType.hh"''')
1033
1034        code('''
1035#include "mem/protocol/Types.hh"
1036#include "mem/ruby/common/Global.hh"
1037#include "mem/ruby/system/System.hh"
1038''')
1039
1040
1041        for include_path in includes:
1042            code('#include "${{include_path}}"')
1043
1044        code('''
1045
1046using namespace std;
1047
1048void
1049${ident}_Controller::wakeup()
1050{
1051    int counter = 0;
1052    while (true) {
1053        // Some cases will put us into an infinite loop without this limit
1054        assert(counter <= m_transitions_per_cycle);
1055        if (counter == m_transitions_per_cycle) {
1056            // Count how often we are fully utilized
1057            m_fully_busy_cycles++;
1058
1059            // Wakeup in another cycle and try again
1060            scheduleEvent(Cycles(1));
1061            break;
1062        }
1063''')
1064
1065        code.indent()
1066        code.indent()
1067
1068        # InPorts
1069        #
1070        for port in self.in_ports:
1071            code.indent()
1072            code('// ${ident}InPort $port')
1073            if port.pairs.has_key("rank"):
1074                code('m_cur_in_port = ${{port.pairs["rank"]}};')
1075            else:
1076                code('m_cur_in_port = 0;')
1077            code('${{port["c_code_in_port"]}}')
1078            code.dedent()
1079
1080            code('')
1081
1082        code.dedent()
1083        code.dedent()
1084        code('''
1085        break;  // If we got this far, we have nothing left todo
1086    }
1087}
1088''')
1089
1090        code.write(path, "%s_Wakeup.cc" % self.ident)
1091
1092    def printCSwitch(self, path):
1093        '''Output switch statement for transition table'''
1094
1095        code = self.symtab.codeFormatter()
1096        ident = self.ident
1097
1098        code('''
1099// Auto generated C++ code started by $__file__:$__line__
1100// ${ident}: ${{self.short}}
1101
1102#include <cassert>
1103
1104#include "base/misc.hh"
1105#include "base/trace.hh"
1106#include "debug/ProtocolTrace.hh"
1107#include "debug/RubyGenerated.hh"
1108#include "mem/protocol/${ident}_Controller.hh"
1109#include "mem/protocol/${ident}_Event.hh"
1110#include "mem/protocol/${ident}_State.hh"
1111#include "mem/protocol/Types.hh"
1112#include "mem/ruby/common/Global.hh"
1113#include "mem/ruby/system/System.hh"
1114
1115#define HASH_FUN(state, event)  ((int(state)*${ident}_Event_NUM)+int(event))
1116
1117#define GET_TRANSITION_COMMENT() (${ident}_transitionComment.str())
1118#define CLEAR_TRANSITION_COMMENT() (${ident}_transitionComment.str(""))
1119
1120TransitionResult
1121${ident}_Controller::doTransition(${ident}_Event event,
1122''')
1123        if self.EntryType != None:
1124            code('''
1125                                  ${{self.EntryType.c_ident}}* m_cache_entry_ptr,
1126''')
1127        if self.TBEType != None:
1128            code('''
1129                                  ${{self.TBEType.c_ident}}* m_tbe_ptr,
1130''')
1131        code('''
1132                                  const Address addr)
1133{
1134''')
1135        code.indent()
1136
1137        if self.TBEType != None and self.EntryType != None:
1138            code('${ident}_State state = getState(m_tbe_ptr, m_cache_entry_ptr, addr);')
1139        elif self.TBEType != None:
1140            code('${ident}_State state = getState(m_tbe_ptr, addr);')
1141        elif self.EntryType != None:
1142            code('${ident}_State state = getState(m_cache_entry_ptr, addr);')
1143        else:
1144            code('${ident}_State state = getState(addr);')
1145
1146        code('''
1147${ident}_State next_state = state;
1148
1149DPRINTF(RubyGenerated, "%s, Time: %lld, state: %s, event: %s, addr: %s\\n",
1150        *this, curCycle(), ${ident}_State_to_string(state),
1151        ${ident}_Event_to_string(event), addr);
1152
1153TransitionResult result =
1154''')
1155        if self.TBEType != None and self.EntryType != None:
1156            code('doTransitionWorker(event, state, next_state, m_tbe_ptr, m_cache_entry_ptr, addr);')
1157        elif self.TBEType != None:
1158            code('doTransitionWorker(event, state, next_state, m_tbe_ptr, addr);')
1159        elif self.EntryType != None:
1160            code('doTransitionWorker(event, state, next_state, m_cache_entry_ptr, addr);')
1161        else:
1162            code('doTransitionWorker(event, state, next_state, addr);')
1163
1164        code('''
1165
1166if (result == TransitionResult_Valid) {
1167    DPRINTF(RubyGenerated, "next_state: %s\\n",
1168            ${ident}_State_to_string(next_state));
1169    countTransition(state, event);
1170
1171    DPRINTFR(ProtocolTrace, "%15d %3s %10s%20s %6s>%-6s %s %s\\n",
1172             curTick(), m_version, "${ident}",
1173             ${ident}_Event_to_string(event),
1174             ${ident}_State_to_string(state),
1175             ${ident}_State_to_string(next_state),
1176             addr, GET_TRANSITION_COMMENT());
1177
1178    CLEAR_TRANSITION_COMMENT();
1179''')
1180        if self.TBEType != None and self.EntryType != None:
1181            code('setState(m_tbe_ptr, m_cache_entry_ptr, addr, next_state);')
1182            code('setAccessPermission(m_cache_entry_ptr, addr, next_state);')
1183        elif self.TBEType != None:
1184            code('setState(m_tbe_ptr, addr, next_state);')
1185            code('setAccessPermission(addr, next_state);')
1186        elif self.EntryType != None:
1187            code('setState(m_cache_entry_ptr, addr, next_state);')
1188            code('setAccessPermission(m_cache_entry_ptr, addr, next_state);')
1189        else:
1190            code('setState(addr, next_state);')
1191            code('setAccessPermission(addr, next_state);')
1192
1193        code('''
1194} else if (result == TransitionResult_ResourceStall) {
1195    DPRINTFR(ProtocolTrace, "%15s %3s %10s%20s %6s>%-6s %s %s\\n",
1196             curTick(), m_version, "${ident}",
1197             ${ident}_Event_to_string(event),
1198             ${ident}_State_to_string(state),
1199             ${ident}_State_to_string(next_state),
1200             addr, "Resource Stall");
1201} else if (result == TransitionResult_ProtocolStall) {
1202    DPRINTF(RubyGenerated, "stalling\\n");
1203    DPRINTFR(ProtocolTrace, "%15s %3s %10s%20s %6s>%-6s %s %s\\n",
1204             curTick(), m_version, "${ident}",
1205             ${ident}_Event_to_string(event),
1206             ${ident}_State_to_string(state),
1207             ${ident}_State_to_string(next_state),
1208             addr, "Protocol Stall");
1209}
1210
1211return result;
1212''')
1213        code.dedent()
1214        code('''
1215}
1216
1217TransitionResult
1218${ident}_Controller::doTransitionWorker(${ident}_Event event,
1219                                        ${ident}_State state,
1220                                        ${ident}_State& next_state,
1221''')
1222
1223        if self.TBEType != None:
1224            code('''
1225                                        ${{self.TBEType.c_ident}}*& m_tbe_ptr,
1226''')
1227        if self.EntryType != None:
1228                  code('''
1229                                        ${{self.EntryType.c_ident}}*& m_cache_entry_ptr,
1230''')
1231        code('''
1232                                        const Address& addr)
1233{
1234    switch(HASH_FUN(state, event)) {
1235''')
1236
1237        # This map will allow suppress generating duplicate code
1238        cases = orderdict()
1239
1240        for trans in self.transitions:
1241            case_string = "%s_State_%s, %s_Event_%s" % \
1242                (self.ident, trans.state.ident, self.ident, trans.event.ident)
1243
1244            case = self.symtab.codeFormatter()
1245            # Only set next_state if it changes
1246            if trans.state != trans.nextState:
1247                ns_ident = trans.nextState.ident
1248                case('next_state = ${ident}_State_${ns_ident};')
1249
1250            actions = trans.actions
1251            request_types = trans.request_types
1252
1253            # Check for resources
1254            case_sorter = []
1255            res = trans.resources
1256            for key,val in res.iteritems():
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\n')
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
1330    return TransitionResult_Valid;
1331}
1332''')
1333        code.write(path, "%s_Transitions.cc" % self.ident)
1334
1335
1336    # **************************
1337    # ******* HTML Files *******
1338    # **************************
1339    def frameRef(self, click_href, click_target, over_href, over_num, text):
1340        code = self.symtab.codeFormatter(fix_newlines=False)
1341        code("""<A href=\"$click_href\" target=\"$click_target\" onmouseover=\"
1342    if (parent.frames[$over_num].location != parent.location + '$over_href') {
1343        parent.frames[$over_num].location='$over_href'
1344    }\">
1345    ${{html.formatShorthand(text)}}
1346    </A>""")
1347        return str(code)
1348
1349    def writeHTMLFiles(self, path):
1350        # Create table with no row hilighted
1351        self.printHTMLTransitions(path, None)
1352
1353        # Generate transition tables
1354        for state in self.states.itervalues():
1355            self.printHTMLTransitions(path, state)
1356
1357        # Generate action descriptions
1358        for action in self.actions.itervalues():
1359            name = "%s_action_%s.html" % (self.ident, action.ident)
1360            code = html.createSymbol(action, "Action")
1361            code.write(path, name)
1362
1363        # Generate state descriptions
1364        for state in self.states.itervalues():
1365            name = "%s_State_%s.html" % (self.ident, state.ident)
1366            code = html.createSymbol(state, "State")
1367            code.write(path, name)
1368
1369        # Generate event descriptions
1370        for event in self.events.itervalues():
1371            name = "%s_Event_%s.html" % (self.ident, event.ident)
1372            code = html.createSymbol(event, "Event")
1373            code.write(path, name)
1374
1375    def printHTMLTransitions(self, path, active_state):
1376        code = self.symtab.codeFormatter()
1377
1378        code('''
1379<HTML>
1380<BODY link="blue" vlink="blue">
1381
1382<H1 align="center">${{html.formatShorthand(self.short)}}:
1383''')
1384        code.indent()
1385        for i,machine in enumerate(self.symtab.getAllType(StateMachine)):
1386            mid = machine.ident
1387            if i != 0:
1388                extra = " - "
1389            else:
1390                extra = ""
1391            if machine == self:
1392                code('$extra$mid')
1393            else:
1394                code('$extra<A target="Table" href="${mid}_table.html">$mid</A>')
1395        code.dedent()
1396
1397        code("""
1398</H1>
1399
1400<TABLE border=1>
1401<TR>
1402  <TH> </TH>
1403""")
1404
1405        for event in self.events.itervalues():
1406            href = "%s_Event_%s.html" % (self.ident, event.ident)
1407            ref = self.frameRef(href, "Status", href, "1", event.short)
1408            code('<TH bgcolor=white>$ref</TH>')
1409
1410        code('</TR>')
1411        # -- Body of table
1412        for state in self.states.itervalues():
1413            # -- Each row
1414            if state == active_state:
1415                color = "yellow"
1416            else:
1417                color = "white"
1418
1419            click = "%s_table_%s.html" % (self.ident, state.ident)
1420            over = "%s_State_%s.html" % (self.ident, state.ident)
1421            text = html.formatShorthand(state.short)
1422            ref = self.frameRef(click, "Table", over, "1", state.short)
1423            code('''
1424<TR>
1425  <TH bgcolor=$color>$ref</TH>
1426''')
1427
1428            # -- One column for each event
1429            for event in self.events.itervalues():
1430                trans = self.table.get((state,event), None)
1431                if trans is None:
1432                    # This is the no transition case
1433                    if state == active_state:
1434                        color = "#C0C000"
1435                    else:
1436                        color = "lightgrey"
1437
1438                    code('<TD bgcolor=$color>&nbsp;</TD>')
1439                    continue
1440
1441                next = trans.nextState
1442                stall_action = False
1443
1444                # -- Get the actions
1445                for action in trans.actions:
1446                    if action.ident == "z_stall" or \
1447                       action.ident == "zz_recycleMandatoryQueue":
1448                        stall_action = True
1449
1450                # -- Print out "actions/next-state"
1451                if stall_action:
1452                    if state == active_state:
1453                        color = "#C0C000"
1454                    else:
1455                        color = "lightgrey"
1456
1457                elif active_state and next.ident == active_state.ident:
1458                    color = "aqua"
1459                elif state == active_state:
1460                    color = "yellow"
1461                else:
1462                    color = "white"
1463
1464                code('<TD bgcolor=$color>')
1465                for action in trans.actions:
1466                    href = "%s_action_%s.html" % (self.ident, action.ident)
1467                    ref = self.frameRef(href, "Status", href, "1",
1468                                        action.short)
1469                    code('  $ref')
1470                if next != state:
1471                    if trans.actions:
1472                        code('/')
1473                    click = "%s_table_%s.html" % (self.ident, next.ident)
1474                    over = "%s_State_%s.html" % (self.ident, next.ident)
1475                    ref = self.frameRef(click, "Table", over, "1", next.short)
1476                    code("$ref")
1477                code("</TD>")
1478
1479            # -- Each row
1480            if state == active_state:
1481                color = "yellow"
1482            else:
1483                color = "white"
1484
1485            click = "%s_table_%s.html" % (self.ident, state.ident)
1486            over = "%s_State_%s.html" % (self.ident, state.ident)
1487            ref = self.frameRef(click, "Table", over, "1", state.short)
1488            code('''
1489  <TH bgcolor=$color>$ref</TH>
1490</TR>
1491''')
1492        code('''
1493<!- Column footer->
1494<TR>
1495  <TH> </TH>
1496''')
1497
1498        for event in self.events.itervalues():
1499            href = "%s_Event_%s.html" % (self.ident, event.ident)
1500            ref = self.frameRef(href, "Status", href, "1", event.short)
1501            code('<TH bgcolor=white>$ref</TH>')
1502        code('''
1503</TR>
1504</TABLE>
1505</BODY></HTML>
1506''')
1507
1508
1509        if active_state:
1510            name = "%s_table_%s.html" % (self.ident, active_state.ident)
1511        else:
1512            name = "%s_table.html" % self.ident
1513        code.write(path, name)
1514
1515__all__ = [ "StateMachine" ]
1516