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