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