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