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