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