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