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