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