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