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