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