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