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