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