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