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