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