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