StateMachine.py revision 9856:69bb50791e25
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        #
464        # max_port_rank is used to size vectors and thus should be one plus the
465        # largest port rank
466        #
467        max_port_rank = self.in_ports[0].pairs["max_port_rank"] + 1
468        code('    m_max_in_port_rank = $max_port_rank;')
469        code.indent()
470
471        #
472        # After initializing the universal machine parameters, initialize the
473        # this machines config parameters.  Also detemine if these configuration
474        # params include a sequencer.  This information will be used later for
475        # contecting the sequencer back to the L1 cache controller.
476        #
477        contains_dma_sequencer = False
478        sequencers = []
479        for param in self.config_parameters:
480            if param.name == "dma_sequencer":
481                contains_dma_sequencer = True
482            elif re.compile("sequencer").search(param.name):
483                sequencers.append(param.name)
484            if param.pointer:
485                code('m_${{param.name}}_ptr = p->${{param.name}};')
486            else:
487                code('m_${{param.name}} = p->${{param.name}};')
488
489        #
490        # For the l1 cache controller, add the special atomic support which
491        # includes passing the sequencer a pointer to the controller.
492        #
493        for seq in sequencers:
494            code('''
495m_${{seq}}_ptr->setController(this);
496    ''')
497
498        #
499        # For the DMA controller, pass the sequencer a pointer to the
500        # controller.
501        #
502        if self.ident == "DMA":
503            if not contains_dma_sequencer:
504                self.error("The DMA controller must include the sequencer " \
505                           "configuration parameter")
506
507            code('''
508m_dma_sequencer_ptr->setController(this);
509''')
510
511        code('m_num_controllers++;')
512        for var in self.objects:
513            if var.ident.find("mandatoryQueue") >= 0:
514                code('''
515m_${{var.c_ident}}_ptr = new ${{var.type.c_ident}}();
516m_${{var.c_ident}}_ptr->setReceiver(this);
517''')
518            else:
519                if "network" in var and "physical_network" in var and \
520                   var["network"] == "To":
521                    has_peer = True
522                    code('''
523m_${{var.c_ident}}_ptr = new ${{var.type.c_ident}}();
524peerQueueMap[${{var["physical_network"]}}] = m_${{var.c_ident}}_ptr;
525m_${{var.c_ident}}_ptr->setSender(this);
526''')
527
528        code('''
529if (p->peer != NULL)
530    connectWithPeer(p->peer);
531
532for (int state = 0; state < ${ident}_State_NUM; state++) {
533    for (int event = 0; event < ${ident}_Event_NUM; event++) {
534        m_possible[state][event] = false;
535        m_counters[state][event] = 0;
536    }
537}
538for (int event = 0; event < ${ident}_Event_NUM; event++) {
539    m_event_counters[event] = 0;
540}
541''')
542        code.dedent()
543        code('''
544}
545
546void
547$c_ident::init()
548{
549    MachineType machine_type = string_to_MachineType("${{var.machine.ident}}");
550    int base = MachineType_base_number(machine_type);
551
552    m_machineID.type = MachineType_${ident};
553    m_machineID.num = m_version;
554
555    // initialize objects
556
557''')
558
559        code.indent()
560        for var in self.objects:
561            vtype = var.type
562            vid = "m_%s_ptr" % var.c_ident
563            if "network" not in var:
564                # Not a network port object
565                if "primitive" in vtype:
566                    code('$vid = new ${{vtype.c_ident}};')
567                    if "default" in var:
568                        code('(*$vid) = ${{var["default"]}};')
569                else:
570                    # Normal Object
571                    if var.ident.find("mandatoryQueue") < 0:
572                        th = var.get("template", "")
573                        expr = "%s  = new %s%s" % (vid, vtype.c_ident, th)
574                        args = ""
575                        if "non_obj" not in vtype and not vtype.isEnumeration:
576                            args = var.get("constructor", "")
577                        code('$expr($args);')
578
579                    code('assert($vid != NULL);')
580
581                    if "default" in var:
582                        code('*$vid = ${{var["default"]}}; // Object default')
583                    elif "default" in vtype:
584                        comment = "Type %s default" % vtype.ident
585                        code('*$vid = ${{vtype["default"]}}; // $comment')
586
587                    # Set ordering
588                    if "ordered" in var:
589                        # A buffer
590                        code('$vid->setOrdering(${{var["ordered"]}});')
591
592                    # Set randomization
593                    if "random" in var:
594                        # A buffer
595                        code('$vid->setRandomization(${{var["random"]}});')
596
597                    # Set Priority
598                    if vtype.isBuffer and "rank" in var:
599                        code('$vid->setPriority(${{var["rank"]}});')
600
601                    # Set sender and receiver for trigger queue
602                    if var.ident.find("triggerQueue") >= 0:
603                        code('$vid->setSender(this);')
604                        code('$vid->setReceiver(this);')
605                    elif vtype.c_ident == "TimerTable":
606                        code('$vid->setClockObj(this);')
607                    elif var.ident.find("optionalQueue") >= 0:
608                        code('$vid->setSender(this);')
609                        code('$vid->setReceiver(this);')
610
611            else:
612                # Network port object
613                network = var["network"]
614                ordered =  var["ordered"]
615
616                if "virtual_network" in var:
617                    vnet = var["virtual_network"]
618                    vnet_type = var["vnet_type"]
619
620                    assert var.machine is not None
621                    code('''
622$vid = m_net_ptr->get${network}NetQueue(m_version + base, $ordered, $vnet, "$vnet_type");
623assert($vid != NULL);
624''')
625
626                    # Set the end
627                    if network == "To":
628                        code('$vid->setSender(this);')
629                    else:
630                        code('$vid->setReceiver(this);')
631
632                # Set ordering
633                if "ordered" in var:
634                    # A buffer
635                    code('$vid->setOrdering(${{var["ordered"]}});')
636
637                # Set randomization
638                if "random" in var:
639                    # A buffer
640                    code('$vid->setRandomization(${{var["random"]}});')
641
642                # Set Priority
643                if "rank" in var:
644                    code('$vid->setPriority(${{var["rank"]}})')
645
646                # Set buffer size
647                if vtype.isBuffer:
648                    code('''
649if (m_buffer_size > 0) {
650    $vid->resize(m_buffer_size);
651}
652''')
653
654                # set description (may be overriden later by port def)
655                code('''
656$vid->setDescription("[Version " + to_string(m_version) + ", ${ident}, name=${{var.c_ident}}]");
657
658''')
659
660            if vtype.isBuffer:
661                if "recycle_latency" in var:
662                    code('$vid->setRecycleLatency( ' \
663                         'Cycles(${{var["recycle_latency"]}}));')
664                else:
665                    code('$vid->setRecycleLatency(m_recycle_latency);')
666
667        # Set the prefetchers
668        code()
669        for prefetcher in self.prefetchers:
670            code('${{prefetcher.code}}.setController(this);')
671
672        code()
673        for port in self.in_ports:
674            # Set the queue consumers
675            code('${{port.code}}.setConsumer(this);')
676            # Set the queue descriptions
677            code('${{port.code}}.setDescription("[Version " + to_string(m_version) + ", $ident, $port]");')
678
679        # Initialize the transition profiling
680        code()
681        for trans in self.transitions:
682            # Figure out if we stall
683            stall = False
684            for action in trans.actions:
685                if action.ident == "z_stall":
686                    stall = True
687
688            # Only possible if it is not a 'z' case
689            if not stall:
690                state = "%s_State_%s" % (self.ident, trans.state.ident)
691                event = "%s_Event_%s" % (self.ident, trans.event.ident)
692                code('possibleTransition($state, $event);')
693
694        code.dedent()
695        code('''
696    AbstractController::init();
697    clearStats();
698}
699''')
700
701        has_mandatory_q = False
702        for port in self.in_ports:
703            if port.code.find("mandatoryQueue_ptr") >= 0:
704                has_mandatory_q = True
705
706        if has_mandatory_q:
707            mq_ident = "m_%s_mandatoryQueue_ptr" % self.ident
708        else:
709            mq_ident = "NULL"
710
711        seq_ident = "NULL"
712        for param in self.config_parameters:
713            if param.name == "sequencer":
714                assert(param.pointer)
715                seq_ident = "m_%s_ptr" % param.name
716
717        code('''
718
719void
720$c_ident::regStats()
721{
722    if (m_version == 0) {
723        for (${ident}_Event event = ${ident}_Event_FIRST;
724             event < ${ident}_Event_NUM; ++event) {
725            Stats::Vector *t = new Stats::Vector();
726            t->init(m_num_controllers);
727            t->name(name() + "." + ${ident}_Event_to_string(event));
728            t->flags(Stats::pdf | Stats::total | Stats::oneline |
729                     Stats::nozero);
730
731            eventVec.push_back(t);
732        }
733
734        for (${ident}_State state = ${ident}_State_FIRST;
735             state < ${ident}_State_NUM; ++state) {
736
737            transVec.push_back(std::vector<Stats::Vector *>());
738
739            for (${ident}_Event event = ${ident}_Event_FIRST;
740                 event < ${ident}_Event_NUM; ++event) {
741
742                Stats::Vector *t = new Stats::Vector();
743                t->init(m_num_controllers);
744                t->name(name() + "." + ${ident}_State_to_string(state) +
745                        "." + ${ident}_Event_to_string(event));
746
747                t->flags(Stats::pdf | Stats::total | Stats::oneline |
748                         Stats::nozero);
749                transVec[state].push_back(t);
750            }
751        }
752    }
753}
754
755void
756$c_ident::collateStats()
757{
758    for (${ident}_Event event = ${ident}_Event_FIRST;
759         event < ${ident}_Event_NUM; ++event) {
760        for (unsigned int i = 0; i < m_num_controllers; ++i) {
761            std::map<uint32_t, AbstractController *>::iterator it =
762                                g_abs_controls[MachineType_${ident}].find(i);
763            assert(it != g_abs_controls[MachineType_${ident}].end());
764            (*eventVec[event])[i] =
765                (($c_ident *)(*it).second)->getEventCount(event);
766        }
767    }
768
769    for (${ident}_State state = ${ident}_State_FIRST;
770         state < ${ident}_State_NUM; ++state) {
771
772        for (${ident}_Event event = ${ident}_Event_FIRST;
773             event < ${ident}_Event_NUM; ++event) {
774
775            for (unsigned int i = 0; i < m_num_controllers; ++i) {
776                std::map<uint32_t, AbstractController *>::iterator it =
777                                g_abs_controls[MachineType_${ident}].find(i);
778                assert(it != g_abs_controls[MachineType_${ident}].end());
779                (*transVec[state][event])[i] =
780                    (($c_ident *)(*it).second)->getTransitionCount(state, event);
781            }
782        }
783    }
784}
785
786void
787$c_ident::countTransition(${ident}_State state, ${ident}_Event event)
788{
789    assert(m_possible[state][event]);
790    m_counters[state][event]++;
791    m_event_counters[event]++;
792}
793void
794$c_ident::possibleTransition(${ident}_State state,
795                             ${ident}_Event event)
796{
797    m_possible[state][event] = true;
798}
799
800uint64
801$c_ident::getEventCount(${ident}_Event event)
802{
803    return m_event_counters[event];
804}
805
806bool
807$c_ident::isPossible(${ident}_State state, ${ident}_Event event)
808{
809    return m_possible[state][event];
810}
811
812uint64
813$c_ident::getTransitionCount(${ident}_State state,
814                             ${ident}_Event event)
815{
816    return m_counters[state][event];
817}
818
819int
820$c_ident::getNumControllers()
821{
822    return m_num_controllers;
823}
824
825MessageBuffer*
826$c_ident::getMandatoryQueue() const
827{
828    return $mq_ident;
829}
830
831Sequencer*
832$c_ident::getSequencer() const
833{
834    return $seq_ident;
835}
836
837const string
838$c_ident::toString() const
839{
840    return "$c_ident";
841}
842
843void
844$c_ident::print(ostream& out) const
845{
846    out << "[$c_ident " << m_version << "]";
847}
848
849void $c_ident::clearStats()
850{
851    for (int state = 0; state < ${ident}_State_NUM; state++) {
852        for (int event = 0; event < ${ident}_Event_NUM; event++) {
853            m_counters[state][event] = 0;
854        }
855    }
856
857    for (int event = 0; event < ${ident}_Event_NUM; event++) {
858        m_event_counters[event] = 0;
859    }
860
861    AbstractController::clearStats();
862}
863''')
864
865        if self.EntryType != None:
866            code('''
867
868// Set and Reset for cache_entry variable
869void
870$c_ident::set_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, AbstractCacheEntry* m_new_cache_entry)
871{
872  m_cache_entry_ptr = (${{self.EntryType.c_ident}}*)m_new_cache_entry;
873}
874
875void
876$c_ident::unset_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr)
877{
878  m_cache_entry_ptr = 0;
879}
880''')
881
882        if self.TBEType != None:
883            code('''
884
885// Set and Reset for tbe variable
886void
887$c_ident::set_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.TBEType.c_ident}}* m_new_tbe)
888{
889  m_tbe_ptr = m_new_tbe;
890}
891
892void
893$c_ident::unset_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr)
894{
895  m_tbe_ptr = NULL;
896}
897''')
898
899        code('''
900
901void
902$c_ident::recordCacheTrace(int cntrl, CacheRecorder* tr)
903{
904''')
905        #
906        # Record cache contents for all associated caches.
907        #
908        code.indent()
909        for param in self.config_parameters:
910            if param.type_ast.type.ident == "CacheMemory":
911                assert(param.pointer)
912                code('m_${{param.ident}}_ptr->recordCacheContents(cntrl, tr);')
913
914        code.dedent()
915        code('''
916}
917
918// Actions
919''')
920        if self.TBEType != None and self.EntryType != None:
921            for action in self.actions.itervalues():
922                if "c_code" not in action:
923                 continue
924
925                code('''
926/** \\brief ${{action.desc}} */
927void
928$c_ident::${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr)
929{
930    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
931    ${{action["c_code"]}}
932}
933
934''')
935        elif self.TBEType != None:
936            for action in self.actions.itervalues():
937                if "c_code" not in action:
938                 continue
939
940                code('''
941/** \\brief ${{action.desc}} */
942void
943$c_ident::${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, const Address& addr)
944{
945    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
946    ${{action["c_code"]}}
947}
948
949''')
950        elif self.EntryType != None:
951            for action in self.actions.itervalues():
952                if "c_code" not in action:
953                 continue
954
955                code('''
956/** \\brief ${{action.desc}} */
957void
958$c_ident::${{action.ident}}(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr)
959{
960    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
961    ${{action["c_code"]}}
962}
963
964''')
965        else:
966            for action in self.actions.itervalues():
967                if "c_code" not in action:
968                 continue
969
970                code('''
971/** \\brief ${{action.desc}} */
972void
973$c_ident::${{action.ident}}(const Address& addr)
974{
975    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
976    ${{action["c_code"]}}
977}
978
979''')
980        for func in self.functions:
981            code(func.generateCode())
982
983        # Function for functional reads from messages buffered in the controller
984        code('''
985bool
986$c_ident::functionalReadBuffers(PacketPtr& pkt)
987{
988''')
989        for var in self.objects:
990            vtype = var.type
991            if vtype.isBuffer:
992                vid = "m_%s_ptr" % var.c_ident
993                code('if ($vid->functionalRead(pkt)) { return true; }')
994        code('''
995                return false;
996}
997''')
998
999        # Function for functional writes to messages buffered in the controller
1000        code('''
1001uint32_t
1002$c_ident::functionalWriteBuffers(PacketPtr& pkt)
1003{
1004    uint32_t num_functional_writes = 0;
1005''')
1006        for var in self.objects:
1007            vtype = var.type
1008            if vtype.isBuffer:
1009                vid = "m_%s_ptr" % var.c_ident
1010                code('num_functional_writes += $vid->functionalWrite(pkt);')
1011        code('''
1012    return num_functional_writes;
1013}
1014''')
1015
1016        # Check if this controller has a peer, if yes then write the
1017        # function for connecting to the peer.
1018        if has_peer:
1019            code('''
1020
1021void
1022$c_ident::getQueuesFromPeer(AbstractController *peer)
1023{
1024''')
1025            for var in self.objects:
1026                if "network" in var and "physical_network" in var and \
1027                   var["network"] == "From":
1028                    code('''
1029m_${{var.c_ident}}_ptr = peer->getPeerQueue(${{var["physical_network"]}});
1030assert(m_${{var.c_ident}}_ptr != NULL);
1031m_${{var.c_ident}}_ptr->setReceiver(this);
1032
1033''')
1034            code('}')
1035
1036        code.write(path, "%s.cc" % c_ident)
1037
1038    def printCWakeup(self, path, includes):
1039        '''Output the wakeup loop for the events'''
1040
1041        code = self.symtab.codeFormatter()
1042        ident = self.ident
1043
1044        outputRequest_types = True
1045        if len(self.request_types) == 0:
1046            outputRequest_types = False
1047
1048        code('''
1049// Auto generated C++ code started by $__file__:$__line__
1050// ${ident}: ${{self.short}}
1051
1052#include <sys/types.h>
1053#include <unistd.h>
1054
1055#include <cassert>
1056
1057#include "base/misc.hh"
1058#include "debug/RubySlicc.hh"
1059#include "mem/protocol/${ident}_Controller.hh"
1060#include "mem/protocol/${ident}_Event.hh"
1061#include "mem/protocol/${ident}_State.hh"
1062''')
1063
1064        if outputRequest_types:
1065            code('''#include "mem/protocol/${ident}_RequestType.hh"''')
1066
1067        code('''
1068#include "mem/protocol/Types.hh"
1069#include "mem/ruby/common/Global.hh"
1070#include "mem/ruby/system/System.hh"
1071''')
1072
1073
1074        for include_path in includes:
1075            code('#include "${{include_path}}"')
1076
1077        code('''
1078
1079using namespace std;
1080
1081void
1082${ident}_Controller::wakeup()
1083{
1084    int counter = 0;
1085    while (true) {
1086        // Some cases will put us into an infinite loop without this limit
1087        assert(counter <= m_transitions_per_cycle);
1088        if (counter == m_transitions_per_cycle) {
1089            // Count how often we are fully utilized
1090            m_fully_busy_cycles++;
1091
1092            // Wakeup in another cycle and try again
1093            scheduleEvent(Cycles(1));
1094            break;
1095        }
1096''')
1097
1098        code.indent()
1099        code.indent()
1100
1101        # InPorts
1102        #
1103        for port in self.in_ports:
1104            code.indent()
1105            code('// ${ident}InPort $port')
1106            if port.pairs.has_key("rank"):
1107                code('m_cur_in_port_rank = ${{port.pairs["rank"]}};')
1108            else:
1109                code('m_cur_in_port_rank = 0;')
1110            code('${{port["c_code_in_port"]}}')
1111            code.dedent()
1112
1113            code('')
1114
1115        code.dedent()
1116        code.dedent()
1117        code('''
1118        break;  // If we got this far, we have nothing left todo
1119    }
1120}
1121''')
1122
1123        code.write(path, "%s_Wakeup.cc" % self.ident)
1124
1125    def printCSwitch(self, path):
1126        '''Output switch statement for transition table'''
1127
1128        code = self.symtab.codeFormatter()
1129        ident = self.ident
1130
1131        code('''
1132// Auto generated C++ code started by $__file__:$__line__
1133// ${ident}: ${{self.short}}
1134
1135#include <cassert>
1136
1137#include "base/misc.hh"
1138#include "base/trace.hh"
1139#include "debug/ProtocolTrace.hh"
1140#include "debug/RubyGenerated.hh"
1141#include "mem/protocol/${ident}_Controller.hh"
1142#include "mem/protocol/${ident}_Event.hh"
1143#include "mem/protocol/${ident}_State.hh"
1144#include "mem/protocol/Types.hh"
1145#include "mem/ruby/common/Global.hh"
1146#include "mem/ruby/system/System.hh"
1147
1148#define HASH_FUN(state, event)  ((int(state)*${ident}_Event_NUM)+int(event))
1149
1150#define GET_TRANSITION_COMMENT() (${ident}_transitionComment.str())
1151#define CLEAR_TRANSITION_COMMENT() (${ident}_transitionComment.str(""))
1152
1153TransitionResult
1154${ident}_Controller::doTransition(${ident}_Event event,
1155''')
1156        if self.EntryType != None:
1157            code('''
1158                                  ${{self.EntryType.c_ident}}* m_cache_entry_ptr,
1159''')
1160        if self.TBEType != None:
1161            code('''
1162                                  ${{self.TBEType.c_ident}}* m_tbe_ptr,
1163''')
1164        code('''
1165                                  const Address &addr)
1166{
1167''')
1168        if self.TBEType != None and self.EntryType != None:
1169            code('${ident}_State state = getState(m_tbe_ptr, m_cache_entry_ptr, addr);')
1170        elif self.TBEType != None:
1171            code('${ident}_State state = getState(m_tbe_ptr, addr);')
1172        elif self.EntryType != None:
1173            code('${ident}_State state = getState(m_cache_entry_ptr, addr);')
1174        else:
1175            code('${ident}_State state = getState(addr);')
1176
1177        code('''
1178    ${ident}_State next_state = state;
1179
1180    DPRINTF(RubyGenerated, "%s, Time: %lld, state: %s, event: %s, addr: %s\\n",
1181            *this, curCycle(), ${ident}_State_to_string(state),
1182            ${ident}_Event_to_string(event), addr);
1183
1184    TransitionResult result =
1185''')
1186        if self.TBEType != None and self.EntryType != None:
1187            code('doTransitionWorker(event, state, next_state, m_tbe_ptr, m_cache_entry_ptr, addr);')
1188        elif self.TBEType != None:
1189            code('doTransitionWorker(event, state, next_state, m_tbe_ptr, addr);')
1190        elif self.EntryType != None:
1191            code('doTransitionWorker(event, state, next_state, m_cache_entry_ptr, addr);')
1192        else:
1193            code('doTransitionWorker(event, state, next_state, addr);')
1194
1195        code('''
1196    if (result == TransitionResult_Valid) {
1197        DPRINTF(RubyGenerated, "next_state: %s\\n",
1198                ${ident}_State_to_string(next_state));
1199        countTransition(state, event);
1200        DPRINTFR(ProtocolTrace, "%15d %3s %10s%20s %6s>%-6s %s %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 %s %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 %s %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
1240    return result;
1241}
1242
1243TransitionResult
1244${ident}_Controller::doTransitionWorker(${ident}_Event event,
1245                                        ${ident}_State state,
1246                                        ${ident}_State& next_state,
1247''')
1248
1249        if self.TBEType != None:
1250            code('''
1251                                        ${{self.TBEType.c_ident}}*& m_tbe_ptr,
1252''')
1253        if self.EntryType != None:
1254                  code('''
1255                                        ${{self.EntryType.c_ident}}*& m_cache_entry_ptr,
1256''')
1257        code('''
1258                                        const Address& addr)
1259{
1260    switch(HASH_FUN(state, event)) {
1261''')
1262
1263        # This map will allow suppress generating duplicate code
1264        cases = orderdict()
1265
1266        for trans in self.transitions:
1267            case_string = "%s_State_%s, %s_Event_%s" % \
1268                (self.ident, trans.state.ident, self.ident, trans.event.ident)
1269
1270            case = self.symtab.codeFormatter()
1271            # Only set next_state if it changes
1272            if trans.state != trans.nextState:
1273                ns_ident = trans.nextState.ident
1274                case('next_state = ${ident}_State_${ns_ident};')
1275
1276            actions = trans.actions
1277            request_types = trans.request_types
1278
1279            # Check for resources
1280            case_sorter = []
1281            res = trans.resources
1282            for key,val in res.iteritems():
1283                if key.type.ident != "DNUCAStopTable":
1284                    val = '''
1285if (!%s.areNSlotsAvailable(%s))
1286    return TransitionResult_ResourceStall;
1287''' % (key.code, val)
1288                case_sorter.append(val)
1289
1290            # Check all of the request_types for resource constraints
1291            for request_type in request_types:
1292                val = '''
1293if (!checkResourceAvailable(%s_RequestType_%s, addr)) {
1294    return TransitionResult_ResourceStall;
1295}
1296''' % (self.ident, request_type.ident)
1297                case_sorter.append(val)
1298
1299            # Emit the code sequences in a sorted order.  This makes the
1300            # output deterministic (without this the output order can vary
1301            # since Map's keys() on a vector of pointers is not deterministic
1302            for c in sorted(case_sorter):
1303                case("$c")
1304
1305            # Record access types for this transition
1306            for request_type in request_types:
1307                case('recordRequestType(${ident}_RequestType_${{request_type.ident}}, addr);')
1308
1309            # Figure out if we stall
1310            stall = False
1311            for action in actions:
1312                if action.ident == "z_stall":
1313                    stall = True
1314                    break
1315
1316            if stall:
1317                case('return TransitionResult_ProtocolStall;')
1318            else:
1319                if self.TBEType != None and self.EntryType != None:
1320                    for action in actions:
1321                        case('${{action.ident}}(m_tbe_ptr, m_cache_entry_ptr, addr);')
1322                elif self.TBEType != None:
1323                    for action in actions:
1324                        case('${{action.ident}}(m_tbe_ptr, addr);')
1325                elif self.EntryType != None:
1326                    for action in actions:
1327                        case('${{action.ident}}(m_cache_entry_ptr, addr);')
1328                else:
1329                    for action in actions:
1330                        case('${{action.ident}}(addr);')
1331                case('return TransitionResult_Valid;')
1332
1333            case = str(case)
1334
1335            # Look to see if this transition code is unique.
1336            if case not in cases:
1337                cases[case] = []
1338
1339            cases[case].append(case_string)
1340
1341        # Walk through all of the unique code blocks and spit out the
1342        # corresponding case statement elements
1343        for case,transitions in cases.iteritems():
1344            # Iterative over all the multiple transitions that share
1345            # the same code
1346            for trans in transitions:
1347                code('  case HASH_FUN($trans):')
1348            code('    $case')
1349
1350        code('''
1351      default:
1352        fatal("Invalid transition\\n"
1353              "%s time: %d addr: %s event: %s state: %s\\n",
1354              name(), curCycle(), addr, event, state);
1355    }
1356    return TransitionResult_Valid;
1357}
1358''')
1359        code.write(path, "%s_Transitions.cc" % self.ident)
1360
1361
1362    # **************************
1363    # ******* HTML Files *******
1364    # **************************
1365    def frameRef(self, click_href, click_target, over_href, over_num, text):
1366        code = self.symtab.codeFormatter(fix_newlines=False)
1367        code("""<A href=\"$click_href\" target=\"$click_target\" onmouseover=\"
1368    if (parent.frames[$over_num].location != parent.location + '$over_href') {
1369        parent.frames[$over_num].location='$over_href'
1370    }\">
1371    ${{html.formatShorthand(text)}}
1372    </A>""")
1373        return str(code)
1374
1375    def writeHTMLFiles(self, path):
1376        # Create table with no row hilighted
1377        self.printHTMLTransitions(path, None)
1378
1379        # Generate transition tables
1380        for state in self.states.itervalues():
1381            self.printHTMLTransitions(path, state)
1382
1383        # Generate action descriptions
1384        for action in self.actions.itervalues():
1385            name = "%s_action_%s.html" % (self.ident, action.ident)
1386            code = html.createSymbol(action, "Action")
1387            code.write(path, name)
1388
1389        # Generate state descriptions
1390        for state in self.states.itervalues():
1391            name = "%s_State_%s.html" % (self.ident, state.ident)
1392            code = html.createSymbol(state, "State")
1393            code.write(path, name)
1394
1395        # Generate event descriptions
1396        for event in self.events.itervalues():
1397            name = "%s_Event_%s.html" % (self.ident, event.ident)
1398            code = html.createSymbol(event, "Event")
1399            code.write(path, name)
1400
1401    def printHTMLTransitions(self, path, active_state):
1402        code = self.symtab.codeFormatter()
1403
1404        code('''
1405<HTML>
1406<BODY link="blue" vlink="blue">
1407
1408<H1 align="center">${{html.formatShorthand(self.short)}}:
1409''')
1410        code.indent()
1411        for i,machine in enumerate(self.symtab.getAllType(StateMachine)):
1412            mid = machine.ident
1413            if i != 0:
1414                extra = " - "
1415            else:
1416                extra = ""
1417            if machine == self:
1418                code('$extra$mid')
1419            else:
1420                code('$extra<A target="Table" href="${mid}_table.html">$mid</A>')
1421        code.dedent()
1422
1423        code("""
1424</H1>
1425
1426<TABLE border=1>
1427<TR>
1428  <TH> </TH>
1429""")
1430
1431        for event in self.events.itervalues():
1432            href = "%s_Event_%s.html" % (self.ident, event.ident)
1433            ref = self.frameRef(href, "Status", href, "1", event.short)
1434            code('<TH bgcolor=white>$ref</TH>')
1435
1436        code('</TR>')
1437        # -- Body of table
1438        for state in self.states.itervalues():
1439            # -- Each row
1440            if state == active_state:
1441                color = "yellow"
1442            else:
1443                color = "white"
1444
1445            click = "%s_table_%s.html" % (self.ident, state.ident)
1446            over = "%s_State_%s.html" % (self.ident, state.ident)
1447            text = html.formatShorthand(state.short)
1448            ref = self.frameRef(click, "Table", over, "1", state.short)
1449            code('''
1450<TR>
1451  <TH bgcolor=$color>$ref</TH>
1452''')
1453
1454            # -- One column for each event
1455            for event in self.events.itervalues():
1456                trans = self.table.get((state,event), None)
1457                if trans is None:
1458                    # This is the no transition case
1459                    if state == active_state:
1460                        color = "#C0C000"
1461                    else:
1462                        color = "lightgrey"
1463
1464                    code('<TD bgcolor=$color>&nbsp;</TD>')
1465                    continue
1466
1467                next = trans.nextState
1468                stall_action = False
1469
1470                # -- Get the actions
1471                for action in trans.actions:
1472                    if action.ident == "z_stall" or \
1473                       action.ident == "zz_recycleMandatoryQueue":
1474                        stall_action = True
1475
1476                # -- Print out "actions/next-state"
1477                if stall_action:
1478                    if state == active_state:
1479                        color = "#C0C000"
1480                    else:
1481                        color = "lightgrey"
1482
1483                elif active_state and next.ident == active_state.ident:
1484                    color = "aqua"
1485                elif state == active_state:
1486                    color = "yellow"
1487                else:
1488                    color = "white"
1489
1490                code('<TD bgcolor=$color>')
1491                for action in trans.actions:
1492                    href = "%s_action_%s.html" % (self.ident, action.ident)
1493                    ref = self.frameRef(href, "Status", href, "1",
1494                                        action.short)
1495                    code('  $ref')
1496                if next != state:
1497                    if trans.actions:
1498                        code('/')
1499                    click = "%s_table_%s.html" % (self.ident, next.ident)
1500                    over = "%s_State_%s.html" % (self.ident, next.ident)
1501                    ref = self.frameRef(click, "Table", over, "1", next.short)
1502                    code("$ref")
1503                code("</TD>")
1504
1505            # -- Each row
1506            if state == active_state:
1507                color = "yellow"
1508            else:
1509                color = "white"
1510
1511            click = "%s_table_%s.html" % (self.ident, state.ident)
1512            over = "%s_State_%s.html" % (self.ident, state.ident)
1513            ref = self.frameRef(click, "Table", over, "1", state.short)
1514            code('''
1515  <TH bgcolor=$color>$ref</TH>
1516</TR>
1517''')
1518        code('''
1519<!- Column footer->
1520<TR>
1521  <TH> </TH>
1522''')
1523
1524        for event in self.events.itervalues():
1525            href = "%s_Event_%s.html" % (self.ident, event.ident)
1526            ref = self.frameRef(href, "Status", href, "1", event.short)
1527            code('<TH bgcolor=white>$ref</TH>')
1528        code('''
1529</TR>
1530</TABLE>
1531</BODY></HTML>
1532''')
1533
1534
1535        if active_state:
1536            name = "%s_table_%s.html" % (self.ident, active_state.ident)
1537        else:
1538            name = "%s_table.html" % self.ident
1539        code.write(path, name)
1540
1541__all__ = [ "StateMachine" ]
1542