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