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