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