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