StateMachine.py revision 11044:25b2a428e2eb
112855Sgabeblack@google.com# Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
212855Sgabeblack@google.com# Copyright (c) 2009 The Hewlett-Packard Development Company
312855Sgabeblack@google.com# 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        code('''
1217
1218if (result == TransitionResult_Valid) {
1219    DPRINTF(RubyGenerated, "next_state: %s\\n",
1220            ${ident}_State_to_string(next_state));
1221    countTransition(state, event);
1222
1223    DPRINTFR(ProtocolTrace, "%15d %3s %10s%20s %6s>%-6s %#x %s\\n",
1224             curTick(), m_version, "${ident}",
1225             ${ident}_Event_to_string(event),
1226             ${ident}_State_to_string(state),
1227             ${ident}_State_to_string(next_state),
1228             addr, GET_TRANSITION_COMMENT());
1229
1230    CLEAR_TRANSITION_COMMENT();
1231''')
1232        if self.TBEType != None and self.EntryType != None:
1233            code('setState(m_tbe_ptr, m_cache_entry_ptr, addr, next_state);')
1234            code('setAccessPermission(m_cache_entry_ptr, addr, next_state);')
1235        elif self.TBEType != None:
1236            code('setState(m_tbe_ptr, addr, next_state);')
1237            code('setAccessPermission(addr, next_state);')
1238        elif self.EntryType != None:
1239            code('setState(m_cache_entry_ptr, addr, next_state);')
1240            code('setAccessPermission(m_cache_entry_ptr, addr, next_state);')
1241        else:
1242            code('setState(addr, next_state);')
1243            code('setAccessPermission(addr, next_state);')
1244
1245        code('''
1246} else if (result == TransitionResult_ResourceStall) {
1247    DPRINTFR(ProtocolTrace, "%15s %3s %10s%20s %6s>%-6s %#x %s\\n",
1248             curTick(), m_version, "${ident}",
1249             ${ident}_Event_to_string(event),
1250             ${ident}_State_to_string(state),
1251             ${ident}_State_to_string(next_state),
1252             addr, "Resource Stall");
1253} else if (result == TransitionResult_ProtocolStall) {
1254    DPRINTF(RubyGenerated, "stalling\\n");
1255    DPRINTFR(ProtocolTrace, "%15s %3s %10s%20s %6s>%-6s %#x %s\\n",
1256             curTick(), m_version, "${ident}",
1257             ${ident}_Event_to_string(event),
1258             ${ident}_State_to_string(state),
1259             ${ident}_State_to_string(next_state),
1260             addr, "Protocol Stall");
1261}
1262
1263return result;
1264''')
1265        code.dedent()
1266        code('''
1267}
1268
1269TransitionResult
1270${ident}_Controller::doTransitionWorker(${ident}_Event event,
1271                                        ${ident}_State state,
1272                                        ${ident}_State& next_state,
1273''')
1274
1275        if self.TBEType != None:
1276            code('''
1277                                        ${{self.TBEType.c_ident}}*& m_tbe_ptr,
1278''')
1279        if self.EntryType != None:
1280                  code('''
1281                                        ${{self.EntryType.c_ident}}*& m_cache_entry_ptr,
1282''')
1283        code('''
1284                                        Addr addr)
1285{
1286    switch(HASH_FUN(state, event)) {
1287''')
1288
1289        # This map will allow suppress generating duplicate code
1290        cases = orderdict()
1291
1292        for trans in self.transitions:
1293            case_string = "%s_State_%s, %s_Event_%s" % \
1294                (self.ident, trans.state.ident, self.ident, trans.event.ident)
1295
1296            case = self.symtab.codeFormatter()
1297            # Only set next_state if it changes
1298            if trans.state != trans.nextState:
1299                if trans.nextState.isWildcard():
1300                    # When * is encountered as an end state of a transition,
1301                    # the next state is determined by calling the
1302                    # machine-specific getNextState function. The next state
1303                    # is determined before any actions of the transition
1304                    # execute, and therefore the next state calculation cannot
1305                    # depend on any of the transitionactions.
1306                    case('next_state = getNextState(addr);')
1307                else:
1308                    ns_ident = trans.nextState.ident
1309                    case('next_state = ${ident}_State_${ns_ident};')
1310
1311            actions = trans.actions
1312            request_types = trans.request_types
1313
1314            # Check for resources
1315            case_sorter = []
1316            res = trans.resources
1317            for key,val in res.iteritems():
1318                val = '''
1319if (!%s.areNSlotsAvailable(%s))
1320    return TransitionResult_ResourceStall;
1321''' % (key.code, val)
1322                case_sorter.append(val)
1323
1324            # Check all of the request_types for resource constraints
1325            for request_type in request_types:
1326                val = '''
1327if (!checkResourceAvailable(%s_RequestType_%s, addr)) {
1328    return TransitionResult_ResourceStall;
1329}
1330''' % (self.ident, request_type.ident)
1331                case_sorter.append(val)
1332
1333            # Emit the code sequences in a sorted order.  This makes the
1334            # output deterministic (without this the output order can vary
1335            # since Map's keys() on a vector of pointers is not deterministic
1336            for c in sorted(case_sorter):
1337                case("$c")
1338
1339            # Record access types for this transition
1340            for request_type in request_types:
1341                case('recordRequestType(${ident}_RequestType_${{request_type.ident}}, addr);')
1342
1343            # Figure out if we stall
1344            stall = False
1345            for action in actions:
1346                if action.ident == "z_stall":
1347                    stall = True
1348                    break
1349
1350            if stall:
1351                case('return TransitionResult_ProtocolStall;')
1352            else:
1353                if self.TBEType != None and self.EntryType != None:
1354                    for action in actions:
1355                        case('${{action.ident}}(m_tbe_ptr, m_cache_entry_ptr, addr);')
1356                elif self.TBEType != None:
1357                    for action in actions:
1358                        case('${{action.ident}}(m_tbe_ptr, addr);')
1359                elif self.EntryType != None:
1360                    for action in actions:
1361                        case('${{action.ident}}(m_cache_entry_ptr, addr);')
1362                else:
1363                    for action in actions:
1364                        case('${{action.ident}}(addr);')
1365                case('return TransitionResult_Valid;')
1366
1367            case = str(case)
1368
1369            # Look to see if this transition code is unique.
1370            if case not in cases:
1371                cases[case] = []
1372
1373            cases[case].append(case_string)
1374
1375        # Walk through all of the unique code blocks and spit out the
1376        # corresponding case statement elements
1377        for case,transitions in cases.iteritems():
1378            # Iterative over all the multiple transitions that share
1379            # the same code
1380            for trans in transitions:
1381                code('  case HASH_FUN($trans):')
1382            code('    $case\n')
1383
1384        code('''
1385      default:
1386        panic("Invalid transition\\n"
1387              "%s time: %d addr: %s event: %s state: %s\\n",
1388              name(), curCycle(), addr, event, state);
1389    }
1390
1391    return TransitionResult_Valid;
1392}
1393''')
1394        code.write(path, "%s_Transitions.cc" % self.ident)
1395
1396
1397    # **************************
1398    # ******* HTML Files *******
1399    # **************************
1400    def frameRef(self, click_href, click_target, over_href, over_num, text):
1401        code = self.symtab.codeFormatter(fix_newlines=False)
1402        code("""<A href=\"$click_href\" target=\"$click_target\" onmouseover=\"
1403    if (parent.frames[$over_num].location != parent.location + '$over_href') {
1404        parent.frames[$over_num].location='$over_href'
1405    }\">
1406    ${{html.formatShorthand(text)}}
1407    </A>""")
1408        return str(code)
1409
1410    def writeHTMLFiles(self, path):
1411        # Create table with no row hilighted
1412        self.printHTMLTransitions(path, None)
1413
1414        # Generate transition tables
1415        for state in self.states.itervalues():
1416            self.printHTMLTransitions(path, state)
1417
1418        # Generate action descriptions
1419        for action in self.actions.itervalues():
1420            name = "%s_action_%s.html" % (self.ident, action.ident)
1421            code = html.createSymbol(action, "Action")
1422            code.write(path, name)
1423
1424        # Generate state descriptions
1425        for state in self.states.itervalues():
1426            name = "%s_State_%s.html" % (self.ident, state.ident)
1427            code = html.createSymbol(state, "State")
1428            code.write(path, name)
1429
1430        # Generate event descriptions
1431        for event in self.events.itervalues():
1432            name = "%s_Event_%s.html" % (self.ident, event.ident)
1433            code = html.createSymbol(event, "Event")
1434            code.write(path, name)
1435
1436    def printHTMLTransitions(self, path, active_state):
1437        code = self.symtab.codeFormatter()
1438
1439        code('''
1440<HTML>
1441<BODY link="blue" vlink="blue">
1442
1443<H1 align="center">${{html.formatShorthand(self.short)}}:
1444''')
1445        code.indent()
1446        for i,machine in enumerate(self.symtab.getAllType(StateMachine)):
1447            mid = machine.ident
1448            if i != 0:
1449                extra = " - "
1450            else:
1451                extra = ""
1452            if machine == self:
1453                code('$extra$mid')
1454            else:
1455                code('$extra<A target="Table" href="${mid}_table.html">$mid</A>')
1456        code.dedent()
1457
1458        code("""
1459</H1>
1460
1461<TABLE border=1>
1462<TR>
1463  <TH> </TH>
1464""")
1465
1466        for event in self.events.itervalues():
1467            href = "%s_Event_%s.html" % (self.ident, event.ident)
1468            ref = self.frameRef(href, "Status", href, "1", event.short)
1469            code('<TH bgcolor=white>$ref</TH>')
1470
1471        code('</TR>')
1472        # -- Body of table
1473        for state in self.states.itervalues():
1474            # -- Each row
1475            if state == active_state:
1476                color = "yellow"
1477            else:
1478                color = "white"
1479
1480            click = "%s_table_%s.html" % (self.ident, state.ident)
1481            over = "%s_State_%s.html" % (self.ident, state.ident)
1482            text = html.formatShorthand(state.short)
1483            ref = self.frameRef(click, "Table", over, "1", state.short)
1484            code('''
1485<TR>
1486  <TH bgcolor=$color>$ref</TH>
1487''')
1488
1489            # -- One column for each event
1490            for event in self.events.itervalues():
1491                trans = self.table.get((state,event), None)
1492                if trans is None:
1493                    # This is the no transition case
1494                    if state == active_state:
1495                        color = "#C0C000"
1496                    else:
1497                        color = "lightgrey"
1498
1499                    code('<TD bgcolor=$color>&nbsp;</TD>')
1500                    continue
1501
1502                next = trans.nextState
1503                stall_action = False
1504
1505                # -- Get the actions
1506                for action in trans.actions:
1507                    if action.ident == "z_stall" or \
1508                       action.ident == "zz_recycleMandatoryQueue":
1509                        stall_action = True
1510
1511                # -- Print out "actions/next-state"
1512                if stall_action:
1513                    if state == active_state:
1514                        color = "#C0C000"
1515                    else:
1516                        color = "lightgrey"
1517
1518                elif active_state and next.ident == active_state.ident:
1519                    color = "aqua"
1520                elif state == active_state:
1521                    color = "yellow"
1522                else:
1523                    color = "white"
1524
1525                code('<TD bgcolor=$color>')
1526                for action in trans.actions:
1527                    href = "%s_action_%s.html" % (self.ident, action.ident)
1528                    ref = self.frameRef(href, "Status", href, "1",
1529                                        action.short)
1530                    code('  $ref')
1531                if next != state:
1532                    if trans.actions:
1533                        code('/')
1534                    click = "%s_table_%s.html" % (self.ident, next.ident)
1535                    over = "%s_State_%s.html" % (self.ident, next.ident)
1536                    ref = self.frameRef(click, "Table", over, "1", next.short)
1537                    code("$ref")
1538                code("</TD>")
1539
1540            # -- Each row
1541            if state == active_state:
1542                color = "yellow"
1543            else:
1544                color = "white"
1545
1546            click = "%s_table_%s.html" % (self.ident, state.ident)
1547            over = "%s_State_%s.html" % (self.ident, state.ident)
1548            ref = self.frameRef(click, "Table", over, "1", state.short)
1549            code('''
1550  <TH bgcolor=$color>$ref</TH>
1551</TR>
1552''')
1553        code('''
1554<!- Column footer->
1555<TR>
1556  <TH> </TH>
1557''')
1558
1559        for event in self.events.itervalues():
1560            href = "%s_Event_%s.html" % (self.ident, event.ident)
1561            ref = self.frameRef(href, "Status", href, "1", event.short)
1562            code('<TH bgcolor=white>$ref</TH>')
1563        code('''
1564</TR>
1565</TABLE>
1566</BODY></HTML>
1567''')
1568
1569
1570        if active_state:
1571            name = "%s_table_%s.html" % (self.ident, active_state.ident)
1572        else:
1573            name = "%s_table.html" % self.ident
1574        code.write(path, name)
1575
1576__all__ = [ "StateMachine" ]
1577