StateMachine.py revision 8341:30daf1dd5c91
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 "mem/protocol/${ident}_ProfileDumper.hh"
226#include "mem/protocol/${ident}_Profiler.hh"
227#include "mem/protocol/TransitionResult.hh"
228#include "mem/protocol/Types.hh"
229#include "mem/ruby/common/Consumer.hh"
230#include "mem/ruby/common/Global.hh"
231#include "mem/ruby/slicc_interface/AbstractController.hh"
232#include "params/$c_ident.hh"
233''')
234
235        seen_types = set()
236        for var in self.objects:
237            if var.type.ident not in seen_types and not var.type.isPrimitive:
238                code('#include "mem/protocol/${{var.type.c_ident}}.hh"')
239            seen_types.add(var.type.ident)
240
241        # for adding information to the protocol debug trace
242        code('''
243extern std::stringstream ${ident}_transitionComment;
244
245class $c_ident : public AbstractController
246{
247// the coherence checker needs to call isBlockExclusive() and isBlockShared()
248// making the Chip a friend class is an easy way to do this for now
249
250public:
251    typedef ${c_ident}Params Params;
252    $c_ident(const Params *p);
253    static int getNumControllers();
254    void init();
255    MessageBuffer* getMandatoryQueue() const;
256    const int & getVersion() const;
257    const std::string toString() const;
258    const std::string getName() const;
259    const MachineType getMachineType() const;
260    void stallBuffer(MessageBuffer* buf, Address addr);
261    void wakeUpBuffers(Address addr);
262    void wakeUpAllBuffers();
263    void initNetworkPtr(Network* net_ptr) { m_net_ptr = net_ptr; }
264    void print(std::ostream& out) const;
265    void printConfig(std::ostream& out) const;
266    void wakeup();
267    void printStats(std::ostream& out) const;
268    void clearStats();
269    void blockOnQueue(Address addr, MessageBuffer* port);
270    void unblock(Address addr);
271
272private:
273''')
274
275        code.indent()
276        # added by SS
277        for param in self.config_parameters:
278            if param.pointer:
279                code('${{param.type_ast.type}}* m_${{param.ident}}_ptr;')
280            else:
281                code('${{param.type_ast.type}} m_${{param.ident}};')
282
283        code('''
284int m_number_of_TBEs;
285
286TransitionResult doTransition(${ident}_Event event,
287''')
288
289        if self.EntryType != None:
290            code('''
291                              ${{self.EntryType.c_ident}}* m_cache_entry_ptr,
292''')
293        if self.TBEType != None:
294            code('''
295                              ${{self.TBEType.c_ident}}* m_tbe_ptr,
296''')
297
298        code('''
299                              const Address& addr);
300
301TransitionResult doTransitionWorker(${ident}_Event event,
302                                    ${ident}_State state,
303                                    ${ident}_State& next_state,
304''')
305
306        if self.TBEType != None:
307            code('''
308                                    ${{self.TBEType.c_ident}}*& m_tbe_ptr,
309''')
310        if self.EntryType != None:
311            code('''
312                                    ${{self.EntryType.c_ident}}*& m_cache_entry_ptr,
313''')
314
315        code('''
316                                    const Address& addr);
317
318std::string m_name;
319int m_transitions_per_cycle;
320int m_buffer_size;
321int m_recycle_latency;
322std::map<std::string, std::string> m_cfg;
323NodeID m_version;
324Network* m_net_ptr;
325MachineID m_machineID;
326bool m_is_blocking;
327std::map<Address, MessageBuffer*> m_block_map;
328typedef std::vector<MessageBuffer*> MsgVecType;
329typedef m5::hash_map< Address, MsgVecType* > WaitingBufType;
330WaitingBufType m_waiting_buffers;
331int m_max_in_port_rank;
332int m_cur_in_port_rank;
333static ${ident}_ProfileDumper s_profileDumper;
334${ident}_Profiler m_profiler;
335static int m_num_controllers;
336
337// Internal functions
338''')
339
340        for func in self.functions:
341            proto = func.prototype
342            if proto:
343                code('$proto')
344
345        if self.EntryType != None:
346            code('''
347
348// Set and Reset for cache_entry variable
349void set_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, AbstractCacheEntry* m_new_cache_entry);
350void unset_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr);
351''')
352
353        if self.TBEType != None:
354            code('''
355
356// Set and Reset for tbe variable
357void set_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${ident}_TBE* m_new_tbe);
358void unset_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr);
359''')
360
361        code('''
362
363// Actions
364''')
365        if self.TBEType != None and self.EntryType != None:
366            for action in self.actions.itervalues():
367                code('/** \\brief ${{action.desc}} */')
368                code('void ${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr);')
369        elif self.TBEType != None:
370            for action in self.actions.itervalues():
371                code('/** \\brief ${{action.desc}} */')
372                code('void ${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, const Address& addr);')
373        elif self.EntryType != None:
374            for action in self.actions.itervalues():
375                code('/** \\brief ${{action.desc}} */')
376                code('void ${{action.ident}}(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr);')
377        else:
378            for action in self.actions.itervalues():
379                code('/** \\brief ${{action.desc}} */')
380                code('void ${{action.ident}}(const Address& addr);')
381
382        # the controller internal variables
383        code('''
384
385// Objects
386''')
387        for var in self.objects:
388            th = var.get("template_hack", "")
389            code('${{var.type.c_ident}}$th* m_${{var.c_ident}}_ptr;')
390
391            if var.type.ident == "MessageBuffer":
392                self.message_buffer_names.append("m_%s_ptr" % var.c_ident)
393
394        code.dedent()
395        code('};')
396        code('#endif // __${ident}_CONTROLLER_H__')
397        code.write(path, '%s.hh' % c_ident)
398
399    def printControllerCC(self, path):
400        '''Output the actions for performing the actions'''
401
402        code = self.symtab.codeFormatter()
403        ident = self.ident
404        c_ident = "%s_Controller" % self.ident
405
406        code('''
407/** \\file $c_ident.cc
408 *
409 * Auto generated C++ code started by $__file__:$__line__
410 * Created by slicc definition of Module "${{self.short}}"
411 */
412
413#include <cassert>
414#include <sstream>
415#include <string>
416
417#include "base/cprintf.hh"
418#include "debug/RubyGenerated.hh"
419#include "debug/RubySlicc.hh"
420#include "mem/protocol/${ident}_Controller.hh"
421#include "mem/protocol/${ident}_Event.hh"
422#include "mem/protocol/${ident}_State.hh"
423#include "mem/protocol/Types.hh"
424#include "mem/ruby/common/Global.hh"
425#include "mem/ruby/slicc_interface/RubySlicc_includes.hh"
426#include "mem/ruby/system/System.hh"
427
428using namespace std;
429''')
430
431        # include object classes
432        seen_types = set()
433        for var in self.objects:
434            if var.type.ident not in seen_types and not var.type.isPrimitive:
435                code('#include "mem/protocol/${{var.type.c_ident}}.hh"')
436            seen_types.add(var.type.ident)
437
438        code('''
439$c_ident *
440${c_ident}Params::create()
441{
442    return new $c_ident(this);
443}
444
445int $c_ident::m_num_controllers = 0;
446${ident}_ProfileDumper $c_ident::s_profileDumper;
447
448// for adding information to the protocol debug trace
449stringstream ${ident}_transitionComment;
450#define APPEND_TRANSITION_COMMENT(str) (${ident}_transitionComment << str)
451
452/** \\brief constructor */
453$c_ident::$c_ident(const Params *p)
454    : AbstractController(p)
455{
456    m_version = p->version;
457    m_transitions_per_cycle = p->transitions_per_cycle;
458    m_buffer_size = p->buffer_size;
459    m_recycle_latency = p->recycle_latency;
460    m_number_of_TBEs = p->number_of_TBEs;
461    m_is_blocking = false;
462''')
463        #
464        # max_port_rank is used to size vectors and thus should be one plus the
465        # largest port rank
466        #
467        max_port_rank = self.in_ports[0].pairs["max_port_rank"] + 1
468        code('    m_max_in_port_rank = $max_port_rank;')
469        code.indent()
470
471        #
472        # After initializing the universal machine parameters, initialize the
473        # this machines config parameters.  Also detemine if these configuration
474        # params include a sequencer.  This information will be used later for
475        # contecting the sequencer back to the L1 cache controller.
476        #
477        contains_dma_sequencer = False
478        sequencers = []
479        for param in self.config_parameters:
480            if param.name == "dma_sequencer":
481                contains_dma_sequencer = True
482            elif re.compile("sequencer").search(param.name):
483                sequencers.append(param.name)
484            if param.pointer:
485                code('m_${{param.name}}_ptr = p->${{param.name}};')
486            else:
487                code('m_${{param.name}} = p->${{param.name}};')
488
489        #
490        # For the l1 cache controller, add the special atomic support which
491        # includes passing the sequencer a pointer to the controller.
492        #
493        if self.ident == "L1Cache":
494            if not sequencers:
495                self.error("The L1Cache controller must include the sequencer " \
496                           "configuration parameter")
497
498            for seq in sequencers:
499                code('''
500m_${{seq}}_ptr->setController(this);
501    ''')
502        #
503        # For the DMA controller, pass the sequencer a pointer to the
504        # controller.
505        #
506        if self.ident == "DMA":
507            if not contains_dma_sequencer:
508                self.error("The DMA controller must include the sequencer " \
509                           "configuration parameter")
510
511            code('''
512m_dma_sequencer_ptr->setController(this);
513''')
514
515        code('m_num_controllers++;')
516        for var in self.objects:
517            if var.ident.find("mandatoryQueue") >= 0:
518                code('m_${{var.c_ident}}_ptr = new ${{var.type.c_ident}}();')
519
520        code.dedent()
521        code('''
522}
523
524void
525$c_ident::init()
526{
527    MachineType machine_type;
528    int base;
529
530    m_machineID.type = MachineType_${ident};
531    m_machineID.num = m_version;
532
533    // initialize objects
534    m_profiler.setVersion(m_version);
535    s_profileDumper.registerProfiler(&m_profiler);
536
537''')
538
539        code.indent()
540        for var in self.objects:
541            vtype = var.type
542            vid = "m_%s_ptr" % var.c_ident
543            if "network" not in var:
544                # Not a network port object
545                if "primitive" in vtype:
546                    code('$vid = new ${{vtype.c_ident}};')
547                    if "default" in var:
548                        code('(*$vid) = ${{var["default"]}};')
549                else:
550                    # Normal Object
551                    # added by SS
552                    if "factory" in var:
553                        code('$vid = ${{var["factory"]}};')
554                    elif var.ident.find("mandatoryQueue") < 0:
555                        th = var.get("template_hack", "")
556                        expr = "%s  = new %s%s" % (vid, vtype.c_ident, th)
557
558                        args = ""
559                        if "non_obj" not in vtype and not vtype.isEnumeration:
560                            if expr.find("TBETable") >= 0:
561                                args = "m_number_of_TBEs"
562                            else:
563                                args = var.get("constructor_hack", "")
564
565                        code('$expr($args);')
566
567                    code('assert($vid != NULL);')
568
569                    if "default" in var:
570                        code('*$vid = ${{var["default"]}}; // Object default')
571                    elif "default" in vtype:
572                        comment = "Type %s default" % vtype.ident
573                        code('*$vid = ${{vtype["default"]}}; // $comment')
574
575                    # Set ordering
576                    if "ordered" in var and "trigger_queue" not in var:
577                        # A buffer
578                        code('$vid->setOrdering(${{var["ordered"]}});')
579
580                    # Set randomization
581                    if "random" in var:
582                        # A buffer
583                        code('$vid->setRandomization(${{var["random"]}});')
584
585                    # Set Priority
586                    if vtype.isBuffer and \
587                           "rank" in var and "trigger_queue" not in var:
588                        code('$vid->setPriority(${{var["rank"]}});')
589
590            else:
591                # Network port object
592                network = var["network"]
593                ordered =  var["ordered"]
594                vnet = var["virtual_network"]
595                vnet_type = var["vnet_type"]
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, "$vnet_type");
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''')
866
867        if self.TBEType != None:
868            code('''
869
870// Set and Reset for tbe variable
871void
872$c_ident::set_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.TBEType.c_ident}}* m_new_tbe)
873{
874  m_tbe_ptr = m_new_tbe;
875}
876
877void
878$c_ident::unset_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr)
879{
880  m_tbe_ptr = NULL;
881}
882''')
883
884        code('''
885
886// Actions
887''')
888        if self.TBEType != None and self.EntryType != None:
889            for action in self.actions.itervalues():
890                if "c_code" not in action:
891                 continue
892
893                code('''
894/** \\brief ${{action.desc}} */
895void
896$c_ident::${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr)
897{
898    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
899    ${{action["c_code"]}}
900}
901
902''')
903        elif self.TBEType != None:
904            for action in self.actions.itervalues():
905                if "c_code" not in action:
906                 continue
907
908                code('''
909/** \\brief ${{action.desc}} */
910void
911$c_ident::${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, const Address& addr)
912{
913    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
914    ${{action["c_code"]}}
915}
916
917''')
918        elif self.EntryType != None:
919            for action in self.actions.itervalues():
920                if "c_code" not in action:
921                 continue
922
923                code('''
924/** \\brief ${{action.desc}} */
925void
926$c_ident::${{action.ident}}(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr)
927{
928    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
929    ${{action["c_code"]}}
930}
931
932''')
933        else:
934            for action in self.actions.itervalues():
935                if "c_code" not in action:
936                 continue
937
938                code('''
939/** \\brief ${{action.desc}} */
940void
941$c_ident::${{action.ident}}(const Address& addr)
942{
943    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
944    ${{action["c_code"]}}
945}
946
947''')
948        code.write(path, "%s.cc" % c_ident)
949
950    def printCWakeup(self, path):
951        '''Output the wakeup loop for the events'''
952
953        code = self.symtab.codeFormatter()
954        ident = self.ident
955
956        code('''
957// Auto generated C++ code started by $__file__:$__line__
958// ${ident}: ${{self.short}}
959
960#include <cassert>
961
962#include "base/misc.hh"
963#include "debug/RubySlicc.hh"
964#include "mem/protocol/${ident}_Controller.hh"
965#include "mem/protocol/${ident}_Event.hh"
966#include "mem/protocol/${ident}_State.hh"
967#include "mem/protocol/Types.hh"
968#include "mem/ruby/common/Global.hh"
969#include "mem/ruby/slicc_interface/RubySlicc_includes.hh"
970#include "mem/ruby/system/System.hh"
971
972using namespace std;
973
974void
975${ident}_Controller::wakeup()
976{
977    int counter = 0;
978    while (true) {
979        // Some cases will put us into an infinite loop without this limit
980        assert(counter <= m_transitions_per_cycle);
981        if (counter == m_transitions_per_cycle) {
982            // Count how often we are fully utilized
983            g_system_ptr->getProfiler()->controllerBusy(m_machineID);
984
985            // Wakeup in another cycle and try again
986            g_eventQueue_ptr->scheduleEvent(this, 1);
987            break;
988        }
989''')
990
991        code.indent()
992        code.indent()
993
994        # InPorts
995        #
996        for port in self.in_ports:
997            code.indent()
998            code('// ${ident}InPort $port')
999            if port.pairs.has_key("rank"):
1000                code('m_cur_in_port_rank = ${{port.pairs["rank"]}};')
1001            else:
1002                code('m_cur_in_port_rank = 0;')
1003            code('${{port["c_code_in_port"]}}')
1004            code.dedent()
1005
1006            code('')
1007
1008        code.dedent()
1009        code.dedent()
1010        code('''
1011        break;  // If we got this far, we have nothing left todo
1012    }
1013    // g_eventQueue_ptr->scheduleEvent(this, 1);
1014}
1015''')
1016
1017        code.write(path, "%s_Wakeup.cc" % self.ident)
1018
1019    def printCSwitch(self, path):
1020        '''Output switch statement for transition table'''
1021
1022        code = self.symtab.codeFormatter()
1023        ident = self.ident
1024
1025        code('''
1026// Auto generated C++ code started by $__file__:$__line__
1027// ${ident}: ${{self.short}}
1028
1029#include <cassert>
1030
1031#include "base/misc.hh"
1032#include "base/trace.hh"
1033#include "debug/ProtocolTrace.hh"
1034#include "debug/RubyGenerated.hh"
1035#include "mem/protocol/${ident}_Controller.hh"
1036#include "mem/protocol/${ident}_Event.hh"
1037#include "mem/protocol/${ident}_State.hh"
1038#include "mem/protocol/Types.hh"
1039#include "mem/ruby/common/Global.hh"
1040#include "mem/ruby/system/System.hh"
1041
1042#define HASH_FUN(state, event)  ((int(state)*${ident}_Event_NUM)+int(event))
1043
1044#define GET_TRANSITION_COMMENT() (${ident}_transitionComment.str())
1045#define CLEAR_TRANSITION_COMMENT() (${ident}_transitionComment.str(""))
1046
1047TransitionResult
1048${ident}_Controller::doTransition(${ident}_Event event,
1049''')
1050        if self.EntryType != None:
1051            code('''
1052                                  ${{self.EntryType.c_ident}}* m_cache_entry_ptr,
1053''')
1054        if self.TBEType != None:
1055            code('''
1056                                  ${{self.TBEType.c_ident}}* m_tbe_ptr,
1057''')
1058        code('''
1059                                  const Address &addr)
1060{
1061''')
1062        if self.TBEType != None and self.EntryType != None:
1063            code('${ident}_State state = getState(m_tbe_ptr, m_cache_entry_ptr, addr);')
1064        elif self.TBEType != None:
1065            code('${ident}_State state = getState(m_tbe_ptr, addr);')
1066        elif self.EntryType != None:
1067            code('${ident}_State state = getState(m_cache_entry_ptr, addr);')
1068        else:
1069            code('${ident}_State state = getState(addr);')
1070
1071        code('''
1072    ${ident}_State next_state = state;
1073
1074    DPRINTF(RubyGenerated, "%s, Time: %lld, state: %s, event: %s, addr: %s\\n",
1075            *this,
1076            g_eventQueue_ptr->getTime(),
1077            ${ident}_State_to_string(state),
1078            ${ident}_Event_to_string(event),
1079            addr);
1080
1081    TransitionResult result =
1082''')
1083        if self.TBEType != None and self.EntryType != None:
1084            code('doTransitionWorker(event, state, next_state, m_tbe_ptr, m_cache_entry_ptr, addr);')
1085        elif self.TBEType != None:
1086            code('doTransitionWorker(event, state, next_state, m_tbe_ptr, addr);')
1087        elif self.EntryType != None:
1088            code('doTransitionWorker(event, state, next_state, m_cache_entry_ptr, addr);')
1089        else:
1090            code('doTransitionWorker(event, state, next_state, addr);')
1091
1092        code('''
1093    if (result == TransitionResult_Valid) {
1094        DPRINTF(RubyGenerated, "next_state: %s\\n",
1095                ${ident}_State_to_string(next_state));
1096        m_profiler.countTransition(state, event);
1097        DPRINTFR(ProtocolTrace, "%15d %3s %10s%20s %6s>%-6s %s %s\\n",
1098                 curTick(), m_version, "${ident}",
1099                 ${ident}_Event_to_string(event),
1100                 ${ident}_State_to_string(state),
1101                 ${ident}_State_to_string(next_state),
1102                 addr, GET_TRANSITION_COMMENT());
1103
1104        CLEAR_TRANSITION_COMMENT();
1105''')
1106        if self.TBEType != None and self.EntryType != None:
1107            code('setState(m_tbe_ptr, m_cache_entry_ptr, addr, next_state);')
1108            code('setAccessPermission(m_cache_entry_ptr, addr, next_state);')
1109        elif self.TBEType != None:
1110            code('setState(m_tbe_ptr, addr, next_state);')
1111            code('setAccessPermission(addr, next_state);')
1112        elif self.EntryType != None:
1113            code('setState(m_cache_entry_ptr, addr, next_state);')
1114            code('setAccessPermission(m_cache_entry_ptr, addr, next_state);')
1115        else:
1116            code('setState(addr, next_state);')
1117            code('setAccessPermission(addr, next_state);')
1118
1119        code('''
1120    } else if (result == TransitionResult_ResourceStall) {
1121        DPRINTFR(ProtocolTrace, "%15s %3s %10s%20s %6s>%-6s %s %s\\n",
1122                 curTick(), m_version, "${ident}",
1123                 ${ident}_Event_to_string(event),
1124                 ${ident}_State_to_string(state),
1125                 ${ident}_State_to_string(next_state),
1126                 addr, "Resource Stall");
1127    } else if (result == TransitionResult_ProtocolStall) {
1128        DPRINTF(RubyGenerated, "stalling\\n");
1129        DPRINTFR(ProtocolTrace, "%15s %3s %10s%20s %6s>%-6s %s %s\\n",
1130                 curTick(), m_version, "${ident}",
1131                 ${ident}_Event_to_string(event),
1132                 ${ident}_State_to_string(state),
1133                 ${ident}_State_to_string(next_state),
1134                 addr, "Protocol Stall");
1135    }
1136
1137    return result;
1138}
1139
1140TransitionResult
1141${ident}_Controller::doTransitionWorker(${ident}_Event event,
1142                                        ${ident}_State state,
1143                                        ${ident}_State& next_state,
1144''')
1145
1146        if self.TBEType != None:
1147            code('''
1148                                        ${{self.TBEType.c_ident}}*& m_tbe_ptr,
1149''')
1150        if self.EntryType != None:
1151                  code('''
1152                                        ${{self.EntryType.c_ident}}*& m_cache_entry_ptr,
1153''')
1154        code('''
1155                                        const Address& addr)
1156{
1157    switch(HASH_FUN(state, event)) {
1158''')
1159
1160        # This map will allow suppress generating duplicate code
1161        cases = orderdict()
1162
1163        for trans in self.transitions:
1164            case_string = "%s_State_%s, %s_Event_%s" % \
1165                (self.ident, trans.state.ident, self.ident, trans.event.ident)
1166
1167            case = self.symtab.codeFormatter()
1168            # Only set next_state if it changes
1169            if trans.state != trans.nextState:
1170                ns_ident = trans.nextState.ident
1171                case('next_state = ${ident}_State_${ns_ident};')
1172
1173            actions = trans.actions
1174
1175            # Check for resources
1176            case_sorter = []
1177            res = trans.resources
1178            for key,val in res.iteritems():
1179                if key.type.ident != "DNUCAStopTable":
1180                    val = '''
1181if (!%s.areNSlotsAvailable(%s))
1182    return TransitionResult_ResourceStall;
1183''' % (key.code, val)
1184                case_sorter.append(val)
1185
1186
1187            # Emit the code sequences in a sorted order.  This makes the
1188            # output deterministic (without this the output order can vary
1189            # since Map's keys() on a vector of pointers is not deterministic
1190            for c in sorted(case_sorter):
1191                case("$c")
1192
1193            # Figure out if we stall
1194            stall = False
1195            for action in actions:
1196                if action.ident == "z_stall":
1197                    stall = True
1198                    break
1199
1200            if stall:
1201                case('return TransitionResult_ProtocolStall;')
1202            else:
1203                if self.TBEType != None and self.EntryType != None:
1204                    for action in actions:
1205                        case('${{action.ident}}(m_tbe_ptr, m_cache_entry_ptr, addr);')
1206                elif self.TBEType != None:
1207                    for action in actions:
1208                        case('${{action.ident}}(m_tbe_ptr, addr);')
1209                elif self.EntryType != None:
1210                    for action in actions:
1211                        case('${{action.ident}}(m_cache_entry_ptr, addr);')
1212                else:
1213                    for action in actions:
1214                        case('${{action.ident}}(addr);')
1215                case('return TransitionResult_Valid;')
1216
1217            case = str(case)
1218
1219            # Look to see if this transition code is unique.
1220            if case not in cases:
1221                cases[case] = []
1222
1223            cases[case].append(case_string)
1224
1225        # Walk through all of the unique code blocks and spit out the
1226        # corresponding case statement elements
1227        for case,transitions in cases.iteritems():
1228            # Iterative over all the multiple transitions that share
1229            # the same code
1230            for trans in transitions:
1231                code('  case HASH_FUN($trans):')
1232            code('    $case')
1233
1234        code('''
1235      default:
1236        fatal("Invalid transition\\n"
1237              "%s time: %d addr: %s event: %s state: %s\\n",
1238              name(), g_eventQueue_ptr->getTime(), addr, event, state);
1239    }
1240    return TransitionResult_Valid;
1241}
1242''')
1243        code.write(path, "%s_Transitions.cc" % self.ident)
1244
1245    def printProfileDumperHH(self, path):
1246        code = self.symtab.codeFormatter()
1247        ident = self.ident
1248
1249        code('''
1250// Auto generated C++ code started by $__file__:$__line__
1251// ${ident}: ${{self.short}}
1252
1253#ifndef __${ident}_PROFILE_DUMPER_HH__
1254#define __${ident}_PROFILE_DUMPER_HH__
1255
1256#include <cassert>
1257#include <iostream>
1258#include <vector>
1259
1260#include "${ident}_Event.hh"
1261#include "${ident}_Profiler.hh"
1262
1263typedef std::vector<${ident}_Profiler *> ${ident}_profilers;
1264
1265class ${ident}_ProfileDumper
1266{
1267  public:
1268    ${ident}_ProfileDumper();
1269    void registerProfiler(${ident}_Profiler* profiler);
1270    void dumpStats(std::ostream& out) const;
1271
1272  private:
1273    ${ident}_profilers m_profilers;
1274};
1275
1276#endif // __${ident}_PROFILE_DUMPER_HH__
1277''')
1278        code.write(path, "%s_ProfileDumper.hh" % self.ident)
1279
1280    def printProfileDumperCC(self, path):
1281        code = self.symtab.codeFormatter()
1282        ident = self.ident
1283
1284        code('''
1285// Auto generated C++ code started by $__file__:$__line__
1286// ${ident}: ${{self.short}}
1287
1288#include "mem/protocol/${ident}_ProfileDumper.hh"
1289
1290${ident}_ProfileDumper::${ident}_ProfileDumper()
1291{
1292}
1293
1294void
1295${ident}_ProfileDumper::registerProfiler(${ident}_Profiler* profiler)
1296{
1297    m_profilers.push_back(profiler);
1298}
1299
1300void
1301${ident}_ProfileDumper::dumpStats(std::ostream& out) const
1302{
1303    out << " --- ${ident} ---\\n";
1304    out << " - Event Counts -\\n";
1305    for (${ident}_Event event = ${ident}_Event_FIRST;
1306         event < ${ident}_Event_NUM;
1307         ++event) {
1308        out << (${ident}_Event) event << " [";
1309        uint64 total = 0;
1310        for (int i = 0; i < m_profilers.size(); i++) {
1311             out << m_profilers[i]->getEventCount(event) << " ";
1312             total += m_profilers[i]->getEventCount(event);
1313        }
1314        out << "] " << total << "\\n";
1315    }
1316    out << "\\n";
1317    out << " - Transitions -\\n";
1318    for (${ident}_State state = ${ident}_State_FIRST;
1319         state < ${ident}_State_NUM;
1320         ++state) {
1321        for (${ident}_Event event = ${ident}_Event_FIRST;
1322             event < ${ident}_Event_NUM;
1323             ++event) {
1324            if (m_profilers[0]->isPossible(state, event)) {
1325                out << (${ident}_State) state << "  "
1326                    << (${ident}_Event) event << " [";
1327                uint64 total = 0;
1328                for (int i = 0; i < m_profilers.size(); i++) {
1329                     out << m_profilers[i]->getTransitionCount(state, event) << " ";
1330                     total += m_profilers[i]->getTransitionCount(state, event);
1331                }
1332                out << "] " << total << "\\n";
1333            }
1334        }
1335        out << "\\n";
1336    }
1337}
1338''')
1339        code.write(path, "%s_ProfileDumper.cc" % self.ident)
1340
1341    def printProfilerHH(self, path):
1342        code = self.symtab.codeFormatter()
1343        ident = self.ident
1344
1345        code('''
1346// Auto generated C++ code started by $__file__:$__line__
1347// ${ident}: ${{self.short}}
1348
1349#ifndef __${ident}_PROFILER_HH__
1350#define __${ident}_PROFILER_HH__
1351
1352#include <cassert>
1353#include <iostream>
1354
1355#include "mem/protocol/${ident}_Event.hh"
1356#include "mem/protocol/${ident}_State.hh"
1357#include "mem/ruby/common/Global.hh"
1358
1359class ${ident}_Profiler
1360{
1361  public:
1362    ${ident}_Profiler();
1363    void setVersion(int version);
1364    void countTransition(${ident}_State state, ${ident}_Event event);
1365    void possibleTransition(${ident}_State state, ${ident}_Event event);
1366    uint64 getEventCount(${ident}_Event event);
1367    bool isPossible(${ident}_State state, ${ident}_Event event);
1368    uint64 getTransitionCount(${ident}_State state, ${ident}_Event event);
1369    void clearStats();
1370
1371  private:
1372    int m_counters[${ident}_State_NUM][${ident}_Event_NUM];
1373    int m_event_counters[${ident}_Event_NUM];
1374    bool m_possible[${ident}_State_NUM][${ident}_Event_NUM];
1375    int m_version;
1376};
1377
1378#endif // __${ident}_PROFILER_HH__
1379''')
1380        code.write(path, "%s_Profiler.hh" % self.ident)
1381
1382    def printProfilerCC(self, path):
1383        code = self.symtab.codeFormatter()
1384        ident = self.ident
1385
1386        code('''
1387// Auto generated C++ code started by $__file__:$__line__
1388// ${ident}: ${{self.short}}
1389
1390#include <cassert>
1391
1392#include "mem/protocol/${ident}_Profiler.hh"
1393
1394${ident}_Profiler::${ident}_Profiler()
1395{
1396    for (int state = 0; state < ${ident}_State_NUM; state++) {
1397        for (int event = 0; event < ${ident}_Event_NUM; event++) {
1398            m_possible[state][event] = false;
1399            m_counters[state][event] = 0;
1400        }
1401    }
1402    for (int event = 0; event < ${ident}_Event_NUM; event++) {
1403        m_event_counters[event] = 0;
1404    }
1405}
1406
1407void
1408${ident}_Profiler::setVersion(int version)
1409{
1410    m_version = version;
1411}
1412
1413void
1414${ident}_Profiler::clearStats()
1415{
1416    for (int state = 0; state < ${ident}_State_NUM; state++) {
1417        for (int event = 0; event < ${ident}_Event_NUM; event++) {
1418            m_counters[state][event] = 0;
1419        }
1420    }
1421
1422    for (int event = 0; event < ${ident}_Event_NUM; event++) {
1423        m_event_counters[event] = 0;
1424    }
1425}
1426void
1427${ident}_Profiler::countTransition(${ident}_State state, ${ident}_Event event)
1428{
1429    assert(m_possible[state][event]);
1430    m_counters[state][event]++;
1431    m_event_counters[event]++;
1432}
1433void
1434${ident}_Profiler::possibleTransition(${ident}_State state,
1435                                      ${ident}_Event event)
1436{
1437    m_possible[state][event] = true;
1438}
1439
1440uint64
1441${ident}_Profiler::getEventCount(${ident}_Event event)
1442{
1443    return m_event_counters[event];
1444}
1445
1446bool
1447${ident}_Profiler::isPossible(${ident}_State state, ${ident}_Event event)
1448{
1449    return m_possible[state][event];
1450}
1451
1452uint64
1453${ident}_Profiler::getTransitionCount(${ident}_State state,
1454                                      ${ident}_Event event)
1455{
1456    return m_counters[state][event];
1457}
1458
1459''')
1460        code.write(path, "%s_Profiler.cc" % self.ident)
1461
1462    # **************************
1463    # ******* HTML Files *******
1464    # **************************
1465    def frameRef(self, click_href, click_target, over_href, over_num, text):
1466        code = self.symtab.codeFormatter(fix_newlines=False)
1467        code("""<A href=\"$click_href\" target=\"$click_target\" onmouseover=\"
1468    if (parent.frames[$over_num].location != parent.location + '$over_href') {
1469        parent.frames[$over_num].location='$over_href'
1470    }\">
1471    ${{html.formatShorthand(text)}}
1472    </A>""")
1473        return str(code)
1474
1475    def writeHTMLFiles(self, path):
1476        # Create table with no row hilighted
1477        self.printHTMLTransitions(path, None)
1478
1479        # Generate transition tables
1480        for state in self.states.itervalues():
1481            self.printHTMLTransitions(path, state)
1482
1483        # Generate action descriptions
1484        for action in self.actions.itervalues():
1485            name = "%s_action_%s.html" % (self.ident, action.ident)
1486            code = html.createSymbol(action, "Action")
1487            code.write(path, name)
1488
1489        # Generate state descriptions
1490        for state in self.states.itervalues():
1491            name = "%s_State_%s.html" % (self.ident, state.ident)
1492            code = html.createSymbol(state, "State")
1493            code.write(path, name)
1494
1495        # Generate event descriptions
1496        for event in self.events.itervalues():
1497            name = "%s_Event_%s.html" % (self.ident, event.ident)
1498            code = html.createSymbol(event, "Event")
1499            code.write(path, name)
1500
1501    def printHTMLTransitions(self, path, active_state):
1502        code = self.symtab.codeFormatter()
1503
1504        code('''
1505<HTML>
1506<BODY link="blue" vlink="blue">
1507
1508<H1 align="center">${{html.formatShorthand(self.short)}}:
1509''')
1510        code.indent()
1511        for i,machine in enumerate(self.symtab.getAllType(StateMachine)):
1512            mid = machine.ident
1513            if i != 0:
1514                extra = " - "
1515            else:
1516                extra = ""
1517            if machine == self:
1518                code('$extra$mid')
1519            else:
1520                code('$extra<A target="Table" href="${mid}_table.html">$mid</A>')
1521        code.dedent()
1522
1523        code("""
1524</H1>
1525
1526<TABLE border=1>
1527<TR>
1528  <TH> </TH>
1529""")
1530
1531        for event in self.events.itervalues():
1532            href = "%s_Event_%s.html" % (self.ident, event.ident)
1533            ref = self.frameRef(href, "Status", href, "1", event.short)
1534            code('<TH bgcolor=white>$ref</TH>')
1535
1536        code('</TR>')
1537        # -- Body of table
1538        for state in self.states.itervalues():
1539            # -- Each row
1540            if state == active_state:
1541                color = "yellow"
1542            else:
1543                color = "white"
1544
1545            click = "%s_table_%s.html" % (self.ident, state.ident)
1546            over = "%s_State_%s.html" % (self.ident, state.ident)
1547            text = html.formatShorthand(state.short)
1548            ref = self.frameRef(click, "Table", over, "1", state.short)
1549            code('''
1550<TR>
1551  <TH bgcolor=$color>$ref</TH>
1552''')
1553
1554            # -- One column for each event
1555            for event in self.events.itervalues():
1556                trans = self.table.get((state,event), None)
1557                if trans is None:
1558                    # This is the no transition case
1559                    if state == active_state:
1560                        color = "#C0C000"
1561                    else:
1562                        color = "lightgrey"
1563
1564                    code('<TD bgcolor=$color>&nbsp;</TD>')
1565                    continue
1566
1567                next = trans.nextState
1568                stall_action = False
1569
1570                # -- Get the actions
1571                for action in trans.actions:
1572                    if action.ident == "z_stall" or \
1573                       action.ident == "zz_recycleMandatoryQueue":
1574                        stall_action = True
1575
1576                # -- Print out "actions/next-state"
1577                if stall_action:
1578                    if state == active_state:
1579                        color = "#C0C000"
1580                    else:
1581                        color = "lightgrey"
1582
1583                elif active_state and next.ident == active_state.ident:
1584                    color = "aqua"
1585                elif state == active_state:
1586                    color = "yellow"
1587                else:
1588                    color = "white"
1589
1590                code('<TD bgcolor=$color>')
1591                for action in trans.actions:
1592                    href = "%s_action_%s.html" % (self.ident, action.ident)
1593                    ref = self.frameRef(href, "Status", href, "1",
1594                                        action.short)
1595                    code('  $ref')
1596                if next != state:
1597                    if trans.actions:
1598                        code('/')
1599                    click = "%s_table_%s.html" % (self.ident, next.ident)
1600                    over = "%s_State_%s.html" % (self.ident, next.ident)
1601                    ref = self.frameRef(click, "Table", over, "1", next.short)
1602                    code("$ref")
1603                code("</TD>")
1604
1605            # -- Each row
1606            if state == active_state:
1607                color = "yellow"
1608            else:
1609                color = "white"
1610
1611            click = "%s_table_%s.html" % (self.ident, state.ident)
1612            over = "%s_State_%s.html" % (self.ident, state.ident)
1613            ref = self.frameRef(click, "Table", over, "1", state.short)
1614            code('''
1615  <TH bgcolor=$color>$ref</TH>
1616</TR>
1617''')
1618        code('''
1619<!- Column footer->
1620<TR>
1621  <TH> </TH>
1622''')
1623
1624        for event in self.events.itervalues():
1625            href = "%s_Event_%s.html" % (self.ident, event.ident)
1626            ref = self.frameRef(href, "Status", href, "1", event.short)
1627            code('<TH bgcolor=white>$ref</TH>')
1628        code('''
1629</TR>
1630</TABLE>
1631</BODY></HTML>
1632''')
1633
1634
1635        if active_state:
1636            name = "%s_table_%s.html" % (self.ident, active_state.ident)
1637        else:
1638            name = "%s_table.html" % self.ident
1639        code.write(path, name)
1640
1641__all__ = [ "StateMachine" ]
1642