StateMachine.py revision 8651:c3d878fbdaea
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    void stallBuffer(MessageBuffer* buf, Address addr);
257    void wakeUpBuffers(Address addr);
258    void wakeUpAllBuffers();
259    void initNetworkPtr(Network* net_ptr) { m_net_ptr = net_ptr; }
260    void print(std::ostream& out) const;
261    void printConfig(std::ostream& out) const;
262    void wakeup();
263    void printStats(std::ostream& out) const;
264    void clearStats();
265    void blockOnQueue(Address addr, MessageBuffer* port);
266    void unblock(Address addr);
267
268private:
269''')
270
271        code.indent()
272        # added by SS
273        for param in self.config_parameters:
274            if param.pointer:
275                code('${{param.type_ast.type}}* m_${{param.ident}}_ptr;')
276            else:
277                code('${{param.type_ast.type}} m_${{param.ident}};')
278
279        code('''
280int m_number_of_TBEs;
281
282TransitionResult doTransition(${ident}_Event event,
283''')
284
285        if self.EntryType != None:
286            code('''
287                              ${{self.EntryType.c_ident}}* m_cache_entry_ptr,
288''')
289        if self.TBEType != None:
290            code('''
291                              ${{self.TBEType.c_ident}}* m_tbe_ptr,
292''')
293
294        code('''
295                              const Address& addr);
296
297TransitionResult doTransitionWorker(${ident}_Event event,
298                                    ${ident}_State state,
299                                    ${ident}_State& next_state,
300''')
301
302        if self.TBEType != None:
303            code('''
304                                    ${{self.TBEType.c_ident}}*& m_tbe_ptr,
305''')
306        if self.EntryType != None:
307            code('''
308                                    ${{self.EntryType.c_ident}}*& m_cache_entry_ptr,
309''')
310
311        code('''
312                                    const Address& addr);
313
314std::string m_name;
315int m_transitions_per_cycle;
316int m_buffer_size;
317int m_recycle_latency;
318std::map<std::string, std::string> m_cfg;
319NodeID m_version;
320Network* m_net_ptr;
321MachineID m_machineID;
322bool m_is_blocking;
323std::map<Address, MessageBuffer*> m_block_map;
324typedef std::vector<MessageBuffer*> MsgVecType;
325typedef m5::hash_map< Address, MsgVecType* > WaitingBufType;
326WaitingBufType m_waiting_buffers;
327int m_max_in_port_rank;
328int m_cur_in_port_rank;
329static ${ident}_ProfileDumper s_profileDumper;
330${ident}_Profiler m_profiler;
331static int m_num_controllers;
332
333// Internal functions
334''')
335
336        for func in self.functions:
337            proto = func.prototype
338            if proto:
339                code('$proto')
340
341        if self.EntryType != None:
342            code('''
343
344// Set and Reset for cache_entry variable
345void set_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, AbstractCacheEntry* m_new_cache_entry);
346void unset_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr);
347''')
348
349        if self.TBEType != None:
350            code('''
351
352// Set and Reset for tbe variable
353void set_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${ident}_TBE* m_new_tbe);
354void unset_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr);
355''')
356
357        code('''
358
359// Actions
360''')
361        if self.TBEType != None and self.EntryType != None:
362            for action in self.actions.itervalues():
363                code('/** \\brief ${{action.desc}} */')
364                code('void ${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr);')
365        elif self.TBEType != None:
366            for action in self.actions.itervalues():
367                code('/** \\brief ${{action.desc}} */')
368                code('void ${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, const Address& addr);')
369        elif self.EntryType != None:
370            for action in self.actions.itervalues():
371                code('/** \\brief ${{action.desc}} */')
372                code('void ${{action.ident}}(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr);')
373        else:
374            for action in self.actions.itervalues():
375                code('/** \\brief ${{action.desc}} */')
376                code('void ${{action.ident}}(const Address& addr);')
377
378        # the controller internal variables
379        code('''
380
381// Objects
382''')
383        for var in self.objects:
384            th = var.get("template_hack", "")
385            code('${{var.type.c_ident}}$th* m_${{var.c_ident}}_ptr;')
386
387            if var.type.ident == "MessageBuffer":
388                self.message_buffer_names.append("m_%s_ptr" % var.c_ident)
389
390        code.dedent()
391        code('};')
392        code('#endif // __${ident}_CONTROLLER_H__')
393        code.write(path, '%s.hh' % c_ident)
394
395    def printControllerCC(self, path):
396        '''Output the actions for performing the actions'''
397
398        code = self.symtab.codeFormatter()
399        ident = self.ident
400        c_ident = "%s_Controller" % self.ident
401
402        code('''
403/** \\file $c_ident.cc
404 *
405 * Auto generated C++ code started by $__file__:$__line__
406 * Created by slicc definition of Module "${{self.short}}"
407 */
408
409#include <cassert>
410#include <sstream>
411#include <string>
412
413#include "base/compiler.hh"
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
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    if (m_waiting_buffers.count(addr) > 0) {
723        //
724        // Wake up all possible lower rank (i.e. lower priority) buffers that could
725        // be waiting on this message.
726        //
727        for (int in_port_rank = m_cur_in_port_rank - 1;
728             in_port_rank >= 0;
729             in_port_rank--) {
730            if ((*(m_waiting_buffers[addr]))[in_port_rank] != NULL) {
731                (*(m_waiting_buffers[addr]))[in_port_rank]->reanalyzeMessages(addr);
732            }
733        }
734        delete m_waiting_buffers[addr];
735        m_waiting_buffers.erase(addr);
736    }
737}
738
739void
740$c_ident::wakeUpAllBuffers()
741{
742    //
743    // Wake up all possible buffers that could be waiting on any message.
744    //
745
746    std::vector<MsgVecType*> wokeUpMsgVecs;
747
748    if(m_waiting_buffers.size() > 0) {
749        for (WaitingBufType::iterator buf_iter = m_waiting_buffers.begin();
750             buf_iter != m_waiting_buffers.end();
751             ++buf_iter) {
752             for (MsgVecType::iterator vec_iter = buf_iter->second->begin();
753                  vec_iter != buf_iter->second->end();
754                  ++vec_iter) {
755                  if (*vec_iter != NULL) {
756                      (*vec_iter)->reanalyzeAllMessages();
757                  }
758             }
759             wokeUpMsgVecs.push_back(buf_iter->second);
760        }
761
762        for (std::vector<MsgVecType*>::iterator wb_iter = wokeUpMsgVecs.begin();
763             wb_iter != wokeUpMsgVecs.end();
764             ++wb_iter) {
765             delete (*wb_iter);
766        }
767
768        m_waiting_buffers.clear();
769    }
770}
771
772void
773$c_ident::blockOnQueue(Address addr, MessageBuffer* port)
774{
775    m_is_blocking = true;
776    m_block_map[addr] = port;
777}
778
779void
780$c_ident::unblock(Address addr)
781{
782    m_block_map.erase(addr);
783    if (m_block_map.size() == 0) {
784       m_is_blocking = false;
785    }
786}
787
788void
789$c_ident::print(ostream& out) const
790{
791    out << "[$c_ident " << m_version << "]";
792}
793
794void
795$c_ident::printConfig(ostream& out) const
796{
797    out << "$c_ident config: " << m_name << endl;
798    out << "  version: " << m_version << endl;
799    map<string, string>::const_iterator it;
800    for (it = m_cfg.begin(); it != m_cfg.end(); it++)
801        out << "  " << it->first << ": " << it->second << endl;
802}
803
804void
805$c_ident::printStats(ostream& out) const
806{
807''')
808        #
809        # Cache and Memory Controllers have specific profilers associated with
810        # them.  Print out these stats before dumping state transition stats.
811        #
812        for param in self.config_parameters:
813            if param.type_ast.type.ident == "CacheMemory" or \
814               param.type_ast.type.ident == "DirectoryMemory" or \
815                   param.type_ast.type.ident == "MemoryControl":
816                assert(param.pointer)
817                code('    m_${{param.ident}}_ptr->printStats(out);')
818
819        code('''
820    if (m_version == 0) {
821        s_profileDumper.dumpStats(out);
822    }
823}
824
825void $c_ident::clearStats() {
826''')
827        #
828        # Cache and Memory Controllers have specific profilers associated with
829        # them.  These stats must be cleared too.
830        #
831        for param in self.config_parameters:
832            if param.type_ast.type.ident == "CacheMemory" or \
833                   param.type_ast.type.ident == "MemoryControl":
834                assert(param.pointer)
835                code('    m_${{param.ident}}_ptr->clearStats();')
836
837        code('''
838    m_profiler.clearStats();
839}
840''')
841
842        if self.EntryType != None:
843            code('''
844
845// Set and Reset for cache_entry variable
846void
847$c_ident::set_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, AbstractCacheEntry* m_new_cache_entry)
848{
849  m_cache_entry_ptr = (${{self.EntryType.c_ident}}*)m_new_cache_entry;
850}
851
852void
853$c_ident::unset_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr)
854{
855  m_cache_entry_ptr = 0;
856}
857''')
858
859        if self.TBEType != None:
860            code('''
861
862// Set and Reset for tbe variable
863void
864$c_ident::set_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.TBEType.c_ident}}* m_new_tbe)
865{
866  m_tbe_ptr = m_new_tbe;
867}
868
869void
870$c_ident::unset_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr)
871{
872  m_tbe_ptr = NULL;
873}
874''')
875
876        code('''
877
878// Actions
879''')
880        if self.TBEType != None and self.EntryType != None:
881            for action in self.actions.itervalues():
882                if "c_code" not in action:
883                 continue
884
885                code('''
886/** \\brief ${{action.desc}} */
887void
888$c_ident::${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr)
889{
890    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
891    ${{action["c_code"]}}
892}
893
894''')
895        elif self.TBEType != None:
896            for action in self.actions.itervalues():
897                if "c_code" not in action:
898                 continue
899
900                code('''
901/** \\brief ${{action.desc}} */
902void
903$c_ident::${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, const Address& addr)
904{
905    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
906    ${{action["c_code"]}}
907}
908
909''')
910        elif self.EntryType != None:
911            for action in self.actions.itervalues():
912                if "c_code" not in action:
913                 continue
914
915                code('''
916/** \\brief ${{action.desc}} */
917void
918$c_ident::${{action.ident}}(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr)
919{
920    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
921    ${{action["c_code"]}}
922}
923
924''')
925        else:
926            for action in self.actions.itervalues():
927                if "c_code" not in action:
928                 continue
929
930                code('''
931/** \\brief ${{action.desc}} */
932void
933$c_ident::${{action.ident}}(const Address& addr)
934{
935    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
936    ${{action["c_code"]}}
937}
938
939''')
940        for func in self.functions:
941            code(func.generateCode())
942
943        code.write(path, "%s.cc" % c_ident)
944
945    def printCWakeup(self, path):
946        '''Output the wakeup loop for the events'''
947
948        code = self.symtab.codeFormatter()
949        ident = self.ident
950
951        code('''
952// Auto generated C++ code started by $__file__:$__line__
953// ${ident}: ${{self.short}}
954
955#include <cassert>
956
957#include "base/misc.hh"
958#include "debug/RubySlicc.hh"
959#include "mem/protocol/${ident}_Controller.hh"
960#include "mem/protocol/${ident}_Event.hh"
961#include "mem/protocol/${ident}_State.hh"
962#include "mem/protocol/Types.hh"
963#include "mem/ruby/common/Global.hh"
964#include "mem/ruby/slicc_interface/RubySlicc_includes.hh"
965#include "mem/ruby/system/System.hh"
966
967using namespace std;
968
969void
970${ident}_Controller::wakeup()
971{
972    int counter = 0;
973    while (true) {
974        // Some cases will put us into an infinite loop without this limit
975        assert(counter <= m_transitions_per_cycle);
976        if (counter == m_transitions_per_cycle) {
977            // Count how often we are fully utilized
978            g_system_ptr->getProfiler()->controllerBusy(m_machineID);
979
980            // Wakeup in another cycle and try again
981            g_eventQueue_ptr->scheduleEvent(this, 1);
982            break;
983        }
984''')
985
986        code.indent()
987        code.indent()
988
989        # InPorts
990        #
991        for port in self.in_ports:
992            code.indent()
993            code('// ${ident}InPort $port')
994            if port.pairs.has_key("rank"):
995                code('m_cur_in_port_rank = ${{port.pairs["rank"]}};')
996            else:
997                code('m_cur_in_port_rank = 0;')
998            code('${{port["c_code_in_port"]}}')
999            code.dedent()
1000
1001            code('')
1002
1003        code.dedent()
1004        code.dedent()
1005        code('''
1006        break;  // If we got this far, we have nothing left todo
1007    }
1008    // g_eventQueue_ptr->scheduleEvent(this, 1);
1009}
1010''')
1011
1012        code.write(path, "%s_Wakeup.cc" % self.ident)
1013
1014    def printCSwitch(self, path):
1015        '''Output switch statement for transition table'''
1016
1017        code = self.symtab.codeFormatter()
1018        ident = self.ident
1019
1020        code('''
1021// Auto generated C++ code started by $__file__:$__line__
1022// ${ident}: ${{self.short}}
1023
1024#include <cassert>
1025
1026#include "base/misc.hh"
1027#include "base/trace.hh"
1028#include "debug/ProtocolTrace.hh"
1029#include "debug/RubyGenerated.hh"
1030#include "mem/protocol/${ident}_Controller.hh"
1031#include "mem/protocol/${ident}_Event.hh"
1032#include "mem/protocol/${ident}_State.hh"
1033#include "mem/protocol/Types.hh"
1034#include "mem/ruby/common/Global.hh"
1035#include "mem/ruby/system/System.hh"
1036
1037#define HASH_FUN(state, event)  ((int(state)*${ident}_Event_NUM)+int(event))
1038
1039#define GET_TRANSITION_COMMENT() (${ident}_transitionComment.str())
1040#define CLEAR_TRANSITION_COMMENT() (${ident}_transitionComment.str(""))
1041
1042TransitionResult
1043${ident}_Controller::doTransition(${ident}_Event event,
1044''')
1045        if self.EntryType != None:
1046            code('''
1047                                  ${{self.EntryType.c_ident}}* m_cache_entry_ptr,
1048''')
1049        if self.TBEType != None:
1050            code('''
1051                                  ${{self.TBEType.c_ident}}* m_tbe_ptr,
1052''')
1053        code('''
1054                                  const Address &addr)
1055{
1056''')
1057        if self.TBEType != None and self.EntryType != None:
1058            code('${ident}_State state = getState(m_tbe_ptr, m_cache_entry_ptr, addr);')
1059        elif self.TBEType != None:
1060            code('${ident}_State state = getState(m_tbe_ptr, addr);')
1061        elif self.EntryType != None:
1062            code('${ident}_State state = getState(m_cache_entry_ptr, addr);')
1063        else:
1064            code('${ident}_State state = getState(addr);')
1065
1066        code('''
1067    ${ident}_State next_state = state;
1068
1069    DPRINTF(RubyGenerated, "%s, Time: %lld, state: %s, event: %s, addr: %s\\n",
1070            *this,
1071            g_eventQueue_ptr->getTime(),
1072            ${ident}_State_to_string(state),
1073            ${ident}_Event_to_string(event),
1074            addr);
1075
1076    TransitionResult result =
1077''')
1078        if self.TBEType != None and self.EntryType != None:
1079            code('doTransitionWorker(event, state, next_state, m_tbe_ptr, m_cache_entry_ptr, addr);')
1080        elif self.TBEType != None:
1081            code('doTransitionWorker(event, state, next_state, m_tbe_ptr, addr);')
1082        elif self.EntryType != None:
1083            code('doTransitionWorker(event, state, next_state, m_cache_entry_ptr, addr);')
1084        else:
1085            code('doTransitionWorker(event, state, next_state, addr);')
1086
1087        code('''
1088    if (result == TransitionResult_Valid) {
1089        DPRINTF(RubyGenerated, "next_state: %s\\n",
1090                ${ident}_State_to_string(next_state));
1091        m_profiler.countTransition(state, event);
1092        DPRINTFR(ProtocolTrace, "%15d %3s %10s%20s %6s>%-6s %s %s\\n",
1093                 curTick(), m_version, "${ident}",
1094                 ${ident}_Event_to_string(event),
1095                 ${ident}_State_to_string(state),
1096                 ${ident}_State_to_string(next_state),
1097                 addr, GET_TRANSITION_COMMENT());
1098
1099        CLEAR_TRANSITION_COMMENT();
1100''')
1101        if self.TBEType != None and self.EntryType != None:
1102            code('setState(m_tbe_ptr, m_cache_entry_ptr, addr, next_state);')
1103            code('setAccessPermission(m_cache_entry_ptr, addr, next_state);')
1104        elif self.TBEType != None:
1105            code('setState(m_tbe_ptr, addr, next_state);')
1106            code('setAccessPermission(addr, next_state);')
1107        elif self.EntryType != None:
1108            code('setState(m_cache_entry_ptr, addr, next_state);')
1109            code('setAccessPermission(m_cache_entry_ptr, addr, next_state);')
1110        else:
1111            code('setState(addr, next_state);')
1112            code('setAccessPermission(addr, next_state);')
1113
1114        code('''
1115    } else if (result == TransitionResult_ResourceStall) {
1116        DPRINTFR(ProtocolTrace, "%15s %3s %10s%20s %6s>%-6s %s %s\\n",
1117                 curTick(), m_version, "${ident}",
1118                 ${ident}_Event_to_string(event),
1119                 ${ident}_State_to_string(state),
1120                 ${ident}_State_to_string(next_state),
1121                 addr, "Resource Stall");
1122    } else if (result == TransitionResult_ProtocolStall) {
1123        DPRINTF(RubyGenerated, "stalling\\n");
1124        DPRINTFR(ProtocolTrace, "%15s %3s %10s%20s %6s>%-6s %s %s\\n",
1125                 curTick(), m_version, "${ident}",
1126                 ${ident}_Event_to_string(event),
1127                 ${ident}_State_to_string(state),
1128                 ${ident}_State_to_string(next_state),
1129                 addr, "Protocol Stall");
1130    }
1131
1132    return result;
1133}
1134
1135TransitionResult
1136${ident}_Controller::doTransitionWorker(${ident}_Event event,
1137                                        ${ident}_State state,
1138                                        ${ident}_State& next_state,
1139''')
1140
1141        if self.TBEType != None:
1142            code('''
1143                                        ${{self.TBEType.c_ident}}*& m_tbe_ptr,
1144''')
1145        if self.EntryType != None:
1146                  code('''
1147                                        ${{self.EntryType.c_ident}}*& m_cache_entry_ptr,
1148''')
1149        code('''
1150                                        const Address& addr)
1151{
1152    switch(HASH_FUN(state, event)) {
1153''')
1154
1155        # This map will allow suppress generating duplicate code
1156        cases = orderdict()
1157
1158        for trans in self.transitions:
1159            case_string = "%s_State_%s, %s_Event_%s" % \
1160                (self.ident, trans.state.ident, self.ident, trans.event.ident)
1161
1162            case = self.symtab.codeFormatter()
1163            # Only set next_state if it changes
1164            if trans.state != trans.nextState:
1165                ns_ident = trans.nextState.ident
1166                case('next_state = ${ident}_State_${ns_ident};')
1167
1168            actions = trans.actions
1169
1170            # Check for resources
1171            case_sorter = []
1172            res = trans.resources
1173            for key,val in res.iteritems():
1174                if key.type.ident != "DNUCAStopTable":
1175                    val = '''
1176if (!%s.areNSlotsAvailable(%s))
1177    return TransitionResult_ResourceStall;
1178''' % (key.code, val)
1179                case_sorter.append(val)
1180
1181
1182            # Emit the code sequences in a sorted order.  This makes the
1183            # output deterministic (without this the output order can vary
1184            # since Map's keys() on a vector of pointers is not deterministic
1185            for c in sorted(case_sorter):
1186                case("$c")
1187
1188            # Figure out if we stall
1189            stall = False
1190            for action in actions:
1191                if action.ident == "z_stall":
1192                    stall = True
1193                    break
1194
1195            if stall:
1196                case('return TransitionResult_ProtocolStall;')
1197            else:
1198                if self.TBEType != None and self.EntryType != None:
1199                    for action in actions:
1200                        case('${{action.ident}}(m_tbe_ptr, m_cache_entry_ptr, addr);')
1201                elif self.TBEType != None:
1202                    for action in actions:
1203                        case('${{action.ident}}(m_tbe_ptr, addr);')
1204                elif self.EntryType != None:
1205                    for action in actions:
1206                        case('${{action.ident}}(m_cache_entry_ptr, addr);')
1207                else:
1208                    for action in actions:
1209                        case('${{action.ident}}(addr);')
1210                case('return TransitionResult_Valid;')
1211
1212            case = str(case)
1213
1214            # Look to see if this transition code is unique.
1215            if case not in cases:
1216                cases[case] = []
1217
1218            cases[case].append(case_string)
1219
1220        # Walk through all of the unique code blocks and spit out the
1221        # corresponding case statement elements
1222        for case,transitions in cases.iteritems():
1223            # Iterative over all the multiple transitions that share
1224            # the same code
1225            for trans in transitions:
1226                code('  case HASH_FUN($trans):')
1227            code('    $case')
1228
1229        code('''
1230      default:
1231        fatal("Invalid transition\\n"
1232              "%s time: %d addr: %s event: %s state: %s\\n",
1233              name(), g_eventQueue_ptr->getTime(), addr, event, state);
1234    }
1235    return TransitionResult_Valid;
1236}
1237''')
1238        code.write(path, "%s_Transitions.cc" % self.ident)
1239
1240    def printProfileDumperHH(self, path):
1241        code = self.symtab.codeFormatter()
1242        ident = self.ident
1243
1244        code('''
1245// Auto generated C++ code started by $__file__:$__line__
1246// ${ident}: ${{self.short}}
1247
1248#ifndef __${ident}_PROFILE_DUMPER_HH__
1249#define __${ident}_PROFILE_DUMPER_HH__
1250
1251#include <cassert>
1252#include <iostream>
1253#include <vector>
1254
1255#include "${ident}_Event.hh"
1256#include "${ident}_Profiler.hh"
1257
1258typedef std::vector<${ident}_Profiler *> ${ident}_profilers;
1259
1260class ${ident}_ProfileDumper
1261{
1262  public:
1263    ${ident}_ProfileDumper();
1264    void registerProfiler(${ident}_Profiler* profiler);
1265    void dumpStats(std::ostream& out) const;
1266
1267  private:
1268    ${ident}_profilers m_profilers;
1269};
1270
1271#endif // __${ident}_PROFILE_DUMPER_HH__
1272''')
1273        code.write(path, "%s_ProfileDumper.hh" % self.ident)
1274
1275    def printProfileDumperCC(self, path):
1276        code = self.symtab.codeFormatter()
1277        ident = self.ident
1278
1279        code('''
1280// Auto generated C++ code started by $__file__:$__line__
1281// ${ident}: ${{self.short}}
1282
1283#include "mem/protocol/${ident}_ProfileDumper.hh"
1284
1285${ident}_ProfileDumper::${ident}_ProfileDumper()
1286{
1287}
1288
1289void
1290${ident}_ProfileDumper::registerProfiler(${ident}_Profiler* profiler)
1291{
1292    m_profilers.push_back(profiler);
1293}
1294
1295void
1296${ident}_ProfileDumper::dumpStats(std::ostream& out) const
1297{
1298    out << " --- ${ident} ---\\n";
1299    out << " - Event Counts -\\n";
1300    for (${ident}_Event event = ${ident}_Event_FIRST;
1301         event < ${ident}_Event_NUM;
1302         ++event) {
1303        out << (${ident}_Event) event << " [";
1304        uint64 total = 0;
1305        for (int i = 0; i < m_profilers.size(); i++) {
1306             out << m_profilers[i]->getEventCount(event) << " ";
1307             total += m_profilers[i]->getEventCount(event);
1308        }
1309        out << "] " << total << "\\n";
1310    }
1311    out << "\\n";
1312    out << " - Transitions -\\n";
1313    for (${ident}_State state = ${ident}_State_FIRST;
1314         state < ${ident}_State_NUM;
1315         ++state) {
1316        for (${ident}_Event event = ${ident}_Event_FIRST;
1317             event < ${ident}_Event_NUM;
1318             ++event) {
1319            if (m_profilers[0]->isPossible(state, event)) {
1320                out << (${ident}_State) state << "  "
1321                    << (${ident}_Event) event << " [";
1322                uint64 total = 0;
1323                for (int i = 0; i < m_profilers.size(); i++) {
1324                     out << m_profilers[i]->getTransitionCount(state, event) << " ";
1325                     total += m_profilers[i]->getTransitionCount(state, event);
1326                }
1327                out << "] " << total << "\\n";
1328            }
1329        }
1330        out << "\\n";
1331    }
1332}
1333''')
1334        code.write(path, "%s_ProfileDumper.cc" % self.ident)
1335
1336    def printProfilerHH(self, path):
1337        code = self.symtab.codeFormatter()
1338        ident = self.ident
1339
1340        code('''
1341// Auto generated C++ code started by $__file__:$__line__
1342// ${ident}: ${{self.short}}
1343
1344#ifndef __${ident}_PROFILER_HH__
1345#define __${ident}_PROFILER_HH__
1346
1347#include <cassert>
1348#include <iostream>
1349
1350#include "mem/protocol/${ident}_Event.hh"
1351#include "mem/protocol/${ident}_State.hh"
1352#include "mem/ruby/common/TypeDefines.hh"
1353
1354class ${ident}_Profiler
1355{
1356  public:
1357    ${ident}_Profiler();
1358    void setVersion(int version);
1359    void countTransition(${ident}_State state, ${ident}_Event event);
1360    void possibleTransition(${ident}_State state, ${ident}_Event event);
1361    uint64 getEventCount(${ident}_Event event);
1362    bool isPossible(${ident}_State state, ${ident}_Event event);
1363    uint64 getTransitionCount(${ident}_State state, ${ident}_Event event);
1364    void clearStats();
1365
1366  private:
1367    int m_counters[${ident}_State_NUM][${ident}_Event_NUM];
1368    int m_event_counters[${ident}_Event_NUM];
1369    bool m_possible[${ident}_State_NUM][${ident}_Event_NUM];
1370    int m_version;
1371};
1372
1373#endif // __${ident}_PROFILER_HH__
1374''')
1375        code.write(path, "%s_Profiler.hh" % self.ident)
1376
1377    def printProfilerCC(self, path):
1378        code = self.symtab.codeFormatter()
1379        ident = self.ident
1380
1381        code('''
1382// Auto generated C++ code started by $__file__:$__line__
1383// ${ident}: ${{self.short}}
1384
1385#include <cassert>
1386
1387#include "mem/protocol/${ident}_Profiler.hh"
1388
1389${ident}_Profiler::${ident}_Profiler()
1390{
1391    for (int state = 0; state < ${ident}_State_NUM; state++) {
1392        for (int event = 0; event < ${ident}_Event_NUM; event++) {
1393            m_possible[state][event] = false;
1394            m_counters[state][event] = 0;
1395        }
1396    }
1397    for (int event = 0; event < ${ident}_Event_NUM; event++) {
1398        m_event_counters[event] = 0;
1399    }
1400}
1401
1402void
1403${ident}_Profiler::setVersion(int version)
1404{
1405    m_version = version;
1406}
1407
1408void
1409${ident}_Profiler::clearStats()
1410{
1411    for (int state = 0; state < ${ident}_State_NUM; state++) {
1412        for (int event = 0; event < ${ident}_Event_NUM; event++) {
1413            m_counters[state][event] = 0;
1414        }
1415    }
1416
1417    for (int event = 0; event < ${ident}_Event_NUM; event++) {
1418        m_event_counters[event] = 0;
1419    }
1420}
1421void
1422${ident}_Profiler::countTransition(${ident}_State state, ${ident}_Event event)
1423{
1424    assert(m_possible[state][event]);
1425    m_counters[state][event]++;
1426    m_event_counters[event]++;
1427}
1428void
1429${ident}_Profiler::possibleTransition(${ident}_State state,
1430                                      ${ident}_Event event)
1431{
1432    m_possible[state][event] = true;
1433}
1434
1435uint64
1436${ident}_Profiler::getEventCount(${ident}_Event event)
1437{
1438    return m_event_counters[event];
1439}
1440
1441bool
1442${ident}_Profiler::isPossible(${ident}_State state, ${ident}_Event event)
1443{
1444    return m_possible[state][event];
1445}
1446
1447uint64
1448${ident}_Profiler::getTransitionCount(${ident}_State state,
1449                                      ${ident}_Event event)
1450{
1451    return m_counters[state][event];
1452}
1453
1454''')
1455        code.write(path, "%s_Profiler.cc" % self.ident)
1456
1457    # **************************
1458    # ******* HTML Files *******
1459    # **************************
1460    def frameRef(self, click_href, click_target, over_href, over_num, text):
1461        code = self.symtab.codeFormatter(fix_newlines=False)
1462        code("""<A href=\"$click_href\" target=\"$click_target\" onmouseover=\"
1463    if (parent.frames[$over_num].location != parent.location + '$over_href') {
1464        parent.frames[$over_num].location='$over_href'
1465    }\">
1466    ${{html.formatShorthand(text)}}
1467    </A>""")
1468        return str(code)
1469
1470    def writeHTMLFiles(self, path):
1471        # Create table with no row hilighted
1472        self.printHTMLTransitions(path, None)
1473
1474        # Generate transition tables
1475        for state in self.states.itervalues():
1476            self.printHTMLTransitions(path, state)
1477
1478        # Generate action descriptions
1479        for action in self.actions.itervalues():
1480            name = "%s_action_%s.html" % (self.ident, action.ident)
1481            code = html.createSymbol(action, "Action")
1482            code.write(path, name)
1483
1484        # Generate state descriptions
1485        for state in self.states.itervalues():
1486            name = "%s_State_%s.html" % (self.ident, state.ident)
1487            code = html.createSymbol(state, "State")
1488            code.write(path, name)
1489
1490        # Generate event descriptions
1491        for event in self.events.itervalues():
1492            name = "%s_Event_%s.html" % (self.ident, event.ident)
1493            code = html.createSymbol(event, "Event")
1494            code.write(path, name)
1495
1496    def printHTMLTransitions(self, path, active_state):
1497        code = self.symtab.codeFormatter()
1498
1499        code('''
1500<HTML>
1501<BODY link="blue" vlink="blue">
1502
1503<H1 align="center">${{html.formatShorthand(self.short)}}:
1504''')
1505        code.indent()
1506        for i,machine in enumerate(self.symtab.getAllType(StateMachine)):
1507            mid = machine.ident
1508            if i != 0:
1509                extra = " - "
1510            else:
1511                extra = ""
1512            if machine == self:
1513                code('$extra$mid')
1514            else:
1515                code('$extra<A target="Table" href="${mid}_table.html">$mid</A>')
1516        code.dedent()
1517
1518        code("""
1519</H1>
1520
1521<TABLE border=1>
1522<TR>
1523  <TH> </TH>
1524""")
1525
1526        for event in self.events.itervalues():
1527            href = "%s_Event_%s.html" % (self.ident, event.ident)
1528            ref = self.frameRef(href, "Status", href, "1", event.short)
1529            code('<TH bgcolor=white>$ref</TH>')
1530
1531        code('</TR>')
1532        # -- Body of table
1533        for state in self.states.itervalues():
1534            # -- Each row
1535            if state == active_state:
1536                color = "yellow"
1537            else:
1538                color = "white"
1539
1540            click = "%s_table_%s.html" % (self.ident, state.ident)
1541            over = "%s_State_%s.html" % (self.ident, state.ident)
1542            text = html.formatShorthand(state.short)
1543            ref = self.frameRef(click, "Table", over, "1", state.short)
1544            code('''
1545<TR>
1546  <TH bgcolor=$color>$ref</TH>
1547''')
1548
1549            # -- One column for each event
1550            for event in self.events.itervalues():
1551                trans = self.table.get((state,event), None)
1552                if trans is None:
1553                    # This is the no transition case
1554                    if state == active_state:
1555                        color = "#C0C000"
1556                    else:
1557                        color = "lightgrey"
1558
1559                    code('<TD bgcolor=$color>&nbsp;</TD>')
1560                    continue
1561
1562                next = trans.nextState
1563                stall_action = False
1564
1565                # -- Get the actions
1566                for action in trans.actions:
1567                    if action.ident == "z_stall" or \
1568                       action.ident == "zz_recycleMandatoryQueue":
1569                        stall_action = True
1570
1571                # -- Print out "actions/next-state"
1572                if stall_action:
1573                    if state == active_state:
1574                        color = "#C0C000"
1575                    else:
1576                        color = "lightgrey"
1577
1578                elif active_state and next.ident == active_state.ident:
1579                    color = "aqua"
1580                elif state == active_state:
1581                    color = "yellow"
1582                else:
1583                    color = "white"
1584
1585                code('<TD bgcolor=$color>')
1586                for action in trans.actions:
1587                    href = "%s_action_%s.html" % (self.ident, action.ident)
1588                    ref = self.frameRef(href, "Status", href, "1",
1589                                        action.short)
1590                    code('  $ref')
1591                if next != state:
1592                    if trans.actions:
1593                        code('/')
1594                    click = "%s_table_%s.html" % (self.ident, next.ident)
1595                    over = "%s_State_%s.html" % (self.ident, next.ident)
1596                    ref = self.frameRef(click, "Table", over, "1", next.short)
1597                    code("$ref")
1598                code("</TD>")
1599
1600            # -- Each row
1601            if state == active_state:
1602                color = "yellow"
1603            else:
1604                color = "white"
1605
1606            click = "%s_table_%s.html" % (self.ident, state.ident)
1607            over = "%s_State_%s.html" % (self.ident, state.ident)
1608            ref = self.frameRef(click, "Table", over, "1", state.short)
1609            code('''
1610  <TH bgcolor=$color>$ref</TH>
1611</TR>
1612''')
1613        code('''
1614<!- Column footer->
1615<TR>
1616  <TH> </TH>
1617''')
1618
1619        for event in self.events.itervalues():
1620            href = "%s_Event_%s.html" % (self.ident, event.ident)
1621            ref = self.frameRef(href, "Status", href, "1", event.short)
1622            code('<TH bgcolor=white>$ref</TH>')
1623        code('''
1624</TR>
1625</TABLE>
1626</BODY></HTML>
1627''')
1628
1629
1630        if active_state:
1631            name = "%s_table_%s.html" % (self.ident, active_state.ident)
1632        else:
1633            name = "%s_table.html" % self.ident
1634        code.write(path, name)
1635
1636__all__ = [ "StateMachine" ]
1637