StateMachine.py revision 6863:21fbf0412e0d
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 code_formatter, orderdict
29
30from slicc.symbols.Symbol import Symbol
31from slicc.symbols.Var import Var
32import slicc.generate.html as html
33
34class StateMachine(Symbol):
35    def __init__(self, symtab, ident, location, pairs, config_parameters):
36        super(StateMachine, self).__init__(symtab, ident, location, pairs)
37        self.table = None
38        self.config_parameters = config_parameters
39        for param in config_parameters:
40            var = Var(symtab, param.name, location, param.type_ast.type,
41                      "m_%s" % param.name, {}, self)
42            self.symtab.registerSym(param.name, var)
43
44        self.states = orderdict()
45        self.events = orderdict()
46        self.actions = orderdict()
47        self.transitions = []
48        self.in_ports = []
49        self.functions = []
50        self.objects = []
51
52        self.message_buffer_names = []
53
54    def __repr__(self):
55        return "[StateMachine: %s]" % self.ident
56
57    def addState(self, state):
58        assert self.table is None
59        self.states[state.ident] = state
60
61    def addEvent(self, event):
62        assert self.table is None
63        self.events[event.ident] = event
64
65    def addAction(self, action):
66        assert self.table is None
67
68        # Check for duplicate action
69        for other in self.actions.itervalues():
70            if action.ident == other.ident:
71                action.warning("Duplicate action definition: %s" % action.ident)
72                action.error("Duplicate action definition: %s" % action.ident)
73            if action.short == other.short:
74                other.warning("Duplicate action shorthand: %s" % other.ident)
75                other.warning("    shorthand = %s" % other.short)
76                action.warning("Duplicate action shorthand: %s" % action.ident)
77                action.error("    shorthand = %s" % action.short)
78
79        self.actions[action.ident] = action
80
81    def addTransition(self, trans):
82        assert self.table is None
83        self.transitions.append(trans)
84
85    def addInPort(self, var):
86        self.in_ports.append(var)
87
88    def addFunc(self, func):
89        # register func in the symbol table
90        self.symtab.registerSym(str(func), func)
91        self.functions.append(func)
92
93    def addObject(self, obj):
94        self.objects.append(obj)
95
96    # Needs to be called before accessing the table
97    def buildTable(self):
98        assert self.table is None
99
100        table = {}
101
102        for trans in self.transitions:
103            # Track which actions we touch so we know if we use them
104            # all -- really this should be done for all symbols as
105            # part of the symbol table, then only trigger it for
106            # Actions, States, Events, etc.
107
108            for action in trans.actions:
109                action.used = True
110
111            index = (trans.state, trans.event)
112            if index in table:
113                table[index].warning("Duplicate transition: %s" % table[index])
114                trans.error("Duplicate transition: %s" % trans)
115            table[index] = trans
116
117        # Look at all actions to make sure we used them all
118        for action in self.actions.itervalues():
119            if not action.used:
120                error_msg = "Unused action: %s" % action.ident
121                if "desc" in action:
122                    error_msg += ", "  + action.desc
123                action.warning(error_msg)
124        self.table = table
125
126    def writeCodeFiles(self, path):
127        self.printControllerHH(path)
128        self.printControllerCC(path)
129        self.printCSwitch(path)
130        self.printCWakeup(path)
131        self.printProfilerCC(path)
132        self.printProfilerHH(path)
133
134        for func in self.functions:
135            func.writeCodeFiles(path)
136
137    def printControllerHH(self, path):
138        '''Output the method declarations for the class declaration'''
139        code = code_formatter()
140        ident = self.ident
141        c_ident = "%s_Controller" % self.ident
142
143        self.message_buffer_names = []
144
145        code('''
146/** \\file $ident.hh
147 *
148 * Auto generated C++ code started by $__file__:$__line__
149 * Created by slicc definition of Module "${{self.short}}"
150 */
151
152#ifndef ${ident}_CONTROLLER_H
153#define ${ident}_CONTROLLER_H
154
155#include "mem/ruby/common/Global.hh"
156#include "mem/ruby/common/Consumer.hh"
157#include "mem/ruby/slicc_interface/AbstractController.hh"
158#include "mem/protocol/TransitionResult.hh"
159#include "mem/protocol/Types.hh"
160#include "mem/protocol/${ident}_Profiler.hh"
161''')
162
163        seen_types = set()
164        for var in self.objects:
165            if var.type.ident not in seen_types and not var.type.isPrimitive:
166                code('#include "mem/protocol/${{var.type.c_ident}}.hh"')
167            seen_types.add(var.type.ident)
168
169        # for adding information to the protocol debug trace
170        code('''
171extern stringstream ${ident}_transitionComment;
172
173class $c_ident : public AbstractController {
174#ifdef CHECK_COHERENCE
175#endif /* CHECK_COHERENCE */
176public:
177    $c_ident(const string & name);
178    static int getNumControllers();
179    void init(Network* net_ptr, const vector<string> & argv);
180    MessageBuffer* getMandatoryQueue() const;
181    const int & getVersion() const;
182    const string toString() const;
183    const string getName() const;
184    const MachineType getMachineType() const;
185    void print(ostream& out) const;
186    void printConfig(ostream& out) const;
187    void wakeup();
188    void printStats(ostream& out) const { s_profiler.dumpStats(out); }
189    void clearStats() { s_profiler.clearStats(); }
190    void blockOnQueue(Address addr, MessageBuffer* port);
191    void unblock(Address addr);
192private:
193''')
194
195        code.indent()
196        # added by SS
197        for param in self.config_parameters:
198            code('int m_${{param.ident}};')
199
200        code('''
201int m_number_of_TBEs;
202
203TransitionResult doTransition(${ident}_Event event, ${ident}_State state, const Address& addr); // in ${ident}_Transitions.cc
204TransitionResult doTransitionWorker(${ident}_Event event, ${ident}_State state, ${ident}_State& next_state, const Address& addr); // in ${ident}_Transitions.cc
205string m_name;
206int m_transitions_per_cycle;
207int m_buffer_size;
208int m_recycle_latency;
209map< string, string > m_cfg;
210NodeID m_version;
211Network* m_net_ptr;
212MachineID m_machineID;
213bool m_is_blocking;
214map< Address, MessageBuffer* > m_block_map;
215${ident}_Profiler s_profiler;
216static int m_num_controllers;
217// Internal functions
218''')
219
220        for func in self.functions:
221            proto = func.prototype
222            if proto:
223                code('$proto')
224
225        code('''
226
227// Actions
228''')
229        for action in self.actions.itervalues():
230            code('/** \\brief ${{action.desc}} */')
231            code('void ${{action.ident}}(const Address& addr);')
232
233        # the controller internal variables
234        code('''
235
236// Object
237''')
238        for var in self.objects:
239            th = var.get("template_hack", "")
240            code('${{var.type.c_ident}}$th* m_${{var.c_ident}}_ptr;')
241
242            if var.type.ident == "MessageBuffer":
243                self.message_buffer_names.append("m_%s_ptr" % var.c_ident)
244
245        code.dedent()
246        code('};')
247        code('#endif // ${ident}_CONTROLLER_H')
248        code.write(path, '%s.hh' % c_ident)
249
250    def printControllerCC(self, path):
251        '''Output the actions for performing the actions'''
252
253        code = code_formatter()
254        ident = self.ident
255        c_ident = "%s_Controller" % self.ident
256
257        code('''
258/** \\file $ident.cc
259 *
260 * Auto generated C++ code started by $__file__:$__line__
261 * Created by slicc definition of Module "${{self.short}}"
262 */
263
264#include "mem/ruby/common/Global.hh"
265#include "mem/ruby/slicc_interface/RubySlicc_includes.hh"
266#include "mem/protocol/${ident}_Controller.hh"
267#include "mem/protocol/${ident}_State.hh"
268#include "mem/protocol/${ident}_Event.hh"
269#include "mem/protocol/Types.hh"
270#include "mem/ruby/system/System.hh"
271''')
272
273        # include object classes
274        seen_types = set()
275        for var in self.objects:
276            if var.type.ident not in seen_types and not var.type.isPrimitive:
277                code('#include "mem/protocol/${{var.type.c_ident}}.hh"')
278            seen_types.add(var.type.ident)
279
280        code('''
281int $c_ident::m_num_controllers = 0;
282
283stringstream ${ident}_transitionComment;
284#define APPEND_TRANSITION_COMMENT(str) (${ident}_transitionComment << str)
285/** \\brief constructor */
286$c_ident::$c_ident(const string &name)
287    : m_name(name)
288{
289''')
290        code.indent()
291
292        code('m_num_controllers++;')
293        for var in self.objects:
294            if var.ident.find("mandatoryQueue") >= 0:
295                code('m_${{var.c_ident}}_ptr = new ${{var.type.c_ident}}();')
296
297        code.dedent()
298        code('''
299}
300
301void $c_ident::init(Network *net_ptr, const vector<string> &argv)
302{
303    for (size_t i = 0; i < argv.size(); i += 2) {
304        if (argv[i] == "version")
305            m_version = atoi(argv[i+1].c_str());
306        else if (argv[i] == "transitions_per_cycle")
307            m_transitions_per_cycle = atoi(argv[i+1].c_str());
308        else if (argv[i] == "buffer_size")
309            m_buffer_size = atoi(argv[i+1].c_str());
310        else if (argv[i] == "recycle_latency")
311            m_recycle_latency = atoi(argv[i+1].c_str());
312        else if (argv[i] == "number_of_TBEs")
313            m_number_of_TBEs = atoi(argv[i+1].c_str());
314''')
315
316        code.indent()
317        code.indent()
318        for param in self.config_parameters:
319            code('else if (argv[i] == "${{param.name}}")')
320            if param.type_ast.type.ident == "int":
321                code('    m_${{param.name}} = atoi(argv[i+1].c_str());')
322            elif param.type_ast.type.ident == "bool":
323                code('    m_${{param.name}} = string_to_bool(argv[i+1]);')
324            else:
325                self.error("only int and bool parameters are "\
326                           "currently supported")
327        code.dedent()
328        code.dedent()
329        code('''
330    }
331
332    m_net_ptr = net_ptr;
333    m_machineID.type = MachineType_${ident};
334    m_machineID.num = m_version;
335    for (size_t i = 0; i < argv.size(); i += 2) {
336        if (argv[i] != "version")
337            m_cfg[argv[i]] = argv[i+1];
338    }
339
340    // Objects
341    s_profiler.setVersion(m_version);
342''')
343
344        code.indent()
345        for var in self.objects:
346            vtype = var.type
347            vid = "m_%s_ptr" % var.c_ident
348            if "network" not in var:
349                # Not a network port object
350                if "primitive" in vtype:
351                    code('$vid = new ${{vtype.c_ident}};')
352                    if "default" in var:
353                        code('(*$vid) = ${{var["default"]}};')
354                else:
355                    # Normal Object
356                    # added by SS
357                    if "factory" in var:
358                        code('$vid = ${{var["factory"]}};')
359                    elif var.ident.find("mandatoryQueue") < 0:
360                        th = var.get("template_hack", "")
361                        expr = "%s  = new %s%s" % (vid, vtype.c_ident, th)
362
363                        args = ""
364                        if "non_obj" not in vtype and not vtype.isEnumeration:
365                            if expr.find("TBETable") >= 0:
366                                args = "m_number_of_TBEs"
367                            else:
368                                args = var.get("constructor_hack", "")
369                            args = "(%s)" % args
370
371                        code('$expr$args;')
372                    else:
373                        code(';')
374
375                    code('assert($vid != NULL);')
376
377                    if "default" in var:
378                        code('(*$vid) = ${{var["default"]}}; // Object default')
379                    elif "default" in vtype:
380                        code('(*$vid) = ${{vtype["default"]}}; // Type ${{vtype.ident}} default')
381
382                    # Set ordering
383                    if "ordered" in var and "trigger_queue" not in var:
384                        # A buffer
385                        code('$vid->setOrdering(${{var["ordered"]}});')
386
387                    # Set randomization
388                    if "random" in var:
389                        # A buffer
390                        code('$vid->setRandomization(${{var["random"]}});')
391
392                    # Set Priority
393                    if vtype.isBuffer and \
394                           "rank" in var and "trigger_queue" not in var:
395                        code('$vid->setPriority(${{var["rank"]}});')
396            else:
397                # Network port object
398                network = var["network"]
399                ordered =  var["ordered"]
400                vnet = var["virtual_network"]
401
402                assert var.machine is not None
403                code('''
404$vid = m_net_ptr->get${network}NetQueue(m_version+MachineType_base_number(string_to_MachineType("${{var.machine.ident}}")), $ordered, $vnet);
405''')
406
407                code('assert($vid != NULL);')
408
409                # Set ordering
410                if "ordered" in var:
411                    # A buffer
412                    code('$vid->setOrdering(${{var["ordered"]}});')
413
414                # Set randomization
415                if "random" in var:
416                    # A buffer
417                    code('$vid->setRandomization(${{var["random"]}})')
418
419                # Set Priority
420                if "rank" in var:
421                    code('$vid->setPriority(${{var["rank"]}})')
422
423                # Set buffer size
424                if vtype.isBuffer:
425                    code('''
426if (m_buffer_size > 0) {
427    $vid->setSize(m_buffer_size);
428}
429''')
430
431                # set description (may be overriden later by port def)
432                code('$vid->setDescription("[Version " + int_to_string(m_version) + ", ${ident}, name=${{var.c_ident}}]");')
433
434        # Set the queue consumers
435        code.insert_newline()
436        for port in self.in_ports:
437            code('${{port.code}}.setConsumer(this);')
438
439        # Set the queue descriptions
440        code.insert_newline()
441        for port in self.in_ports:
442            code('${{port.code}}.setDescription("[Version " + int_to_string(m_version) + ", $ident, $port]");')
443
444        # Initialize the transition profiling
445        code.insert_newline()
446        for trans in self.transitions:
447            # Figure out if we stall
448            stall = False
449            for action in trans.actions:
450                if action.ident == "z_stall":
451                    stall = True
452
453            # Only possible if it is not a 'z' case
454            if not stall:
455                state = "%s_State_%s" % (self.ident, trans.state.ident)
456                event = "%s_Event_%s" % (self.ident, trans.event.ident)
457                code('s_profiler.possibleTransition($state, $event);')
458
459        # added by SS to initialize recycle_latency of message buffers
460        for buf in self.message_buffer_names:
461            code("$buf->setRecycleLatency(m_recycle_latency);")
462
463        code.dedent()
464        code('}')
465
466        has_mandatory_q = False
467        for port in self.in_ports:
468            if port.code.find("mandatoryQueue_ptr") >= 0:
469                has_mandatory_q = True
470
471        if has_mandatory_q:
472            mq_ident = "m_%s_mandatoryQueue_ptr" % self.ident
473        else:
474            mq_ident = "NULL"
475
476        code('''
477int $c_ident::getNumControllers() {
478    return m_num_controllers;
479}
480
481MessageBuffer* $c_ident::getMandatoryQueue() const {
482    return $mq_ident;
483}
484
485const int & $c_ident::getVersion() const{
486    return m_version;
487}
488
489const string $c_ident::toString() const{
490    return "$c_ident";
491}
492
493const string $c_ident::getName() const{
494    return m_name;
495}
496const MachineType $c_ident::getMachineType() const{
497    return MachineType_${ident};
498}
499
500void $c_ident::blockOnQueue(Address addr, MessageBuffer* port) {
501    m_is_blocking = true;
502    m_block_map[addr] = port;
503}
504void $c_ident::unblock(Address addr) {
505    m_block_map.erase(addr);
506    if (m_block_map.size() == 0) {
507       m_is_blocking = false;
508    }
509}
510
511void $c_ident::print(ostream& out) const { out << "[$c_ident " << m_version << "]"; }
512
513void $c_ident::printConfig(ostream& out) const {
514    out << "$c_ident config: " << m_name << endl;
515    out << "  version: " << m_version << endl;
516    for (map<string, string>::const_iterator it = m_cfg.begin(); it != m_cfg.end(); it++) {
517        out << "  " << (*it).first << ": " << (*it).second << endl;
518    }
519}
520
521// Actions
522''')
523
524        for action in self.actions.itervalues():
525            if "c_code" not in action:
526                continue
527
528            code('''
529/** \\brief ${{action.desc}} */
530void $c_ident::${{action.ident}}(const Address& addr)
531{
532    DEBUG_MSG(GENERATED_COMP, HighPrio, "executing");
533    ${{action["c_code"]}}
534}
535
536''')
537        code.write(path, "%s.cc" % c_ident)
538
539    def printCWakeup(self, path):
540        '''Output the wakeup loop for the events'''
541
542        code = code_formatter()
543        ident = self.ident
544
545        code('''
546// Auto generated C++ code started by $__file__:$__line__
547// ${ident}: ${{self.short}}
548
549#include "mem/ruby/common/Global.hh"
550#include "mem/ruby/slicc_interface/RubySlicc_includes.hh"
551#include "mem/protocol/${ident}_Controller.hh"
552#include "mem/protocol/${ident}_State.hh"
553#include "mem/protocol/${ident}_Event.hh"
554#include "mem/protocol/Types.hh"
555#include "mem/ruby/system/System.hh"
556
557void ${ident}_Controller::wakeup()
558{
559
560    int counter = 0;
561    while (true) {
562        // Some cases will put us into an infinite loop without this limit
563        assert(counter <= m_transitions_per_cycle);
564        if (counter == m_transitions_per_cycle) {
565            g_system_ptr->getProfiler()->controllerBusy(m_machineID); // Count how often we\'re fully utilized
566            g_eventQueue_ptr->scheduleEvent(this, 1); // Wakeup in another cycle and try again
567            break;
568        }
569''')
570
571        code.indent()
572        code.indent()
573
574        # InPorts
575        #
576        for port in self.in_ports:
577            code.indent()
578            code('// ${ident}InPort $port')
579            code('${{port["c_code_in_port"]}}')
580            code.dedent()
581
582            code('')
583
584        code.dedent()
585        code.dedent()
586        code('''
587        break;  // If we got this far, we have nothing left todo
588    }
589}
590''')
591
592        code.write(path, "%s_Wakeup.cc" % self.ident)
593
594    def printCSwitch(self, path):
595        '''Output switch statement for transition table'''
596
597        code = code_formatter()
598        ident = self.ident
599
600        code('''
601// Auto generated C++ code started by $__file__:$__line__
602// ${ident}: ${{self.short}}
603
604#include "mem/ruby/common/Global.hh"
605#include "mem/protocol/${ident}_Controller.hh"
606#include "mem/protocol/${ident}_State.hh"
607#include "mem/protocol/${ident}_Event.hh"
608#include "mem/protocol/Types.hh"
609#include "mem/ruby/system/System.hh"
610
611#define HASH_FUN(state, event)  ((int(state)*${ident}_Event_NUM)+int(event))
612
613#define GET_TRANSITION_COMMENT() (${ident}_transitionComment.str())
614#define CLEAR_TRANSITION_COMMENT() (${ident}_transitionComment.str(""))
615
616TransitionResult ${ident}_Controller::doTransition(${ident}_Event event, ${ident}_State state, const Address& addr
617)
618{
619    ${ident}_State next_state = state;
620
621    DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
622    DEBUG_MSG(GENERATED_COMP, MedPrio, *this);
623    DEBUG_EXPR(GENERATED_COMP, MedPrio, g_eventQueue_ptr->getTime());
624    DEBUG_EXPR(GENERATED_COMP, MedPrio,state);
625    DEBUG_EXPR(GENERATED_COMP, MedPrio,event);
626    DEBUG_EXPR(GENERATED_COMP, MedPrio,addr);
627
628    TransitionResult result = doTransitionWorker(event, state, next_state, addr);
629
630    if (result == TransitionResult_Valid) {
631        DEBUG_EXPR(GENERATED_COMP, MedPrio, next_state);
632        DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
633        s_profiler.countTransition(state, event);
634        if (Debug::getProtocolTrace()) {
635            g_system_ptr->getProfiler()->profileTransition("${ident}", m_version, addr,
636                    ${ident}_State_to_string(state),
637                    ${ident}_Event_to_string(event),
638                    ${ident}_State_to_string(next_state), GET_TRANSITION_COMMENT());
639        }
640    CLEAR_TRANSITION_COMMENT();
641    ${ident}_setState(addr, next_state);
642
643    } else if (result == TransitionResult_ResourceStall) {
644        if (Debug::getProtocolTrace()) {
645            g_system_ptr->getProfiler()->profileTransition("${ident}", m_version, addr,
646                   ${ident}_State_to_string(state),
647                   ${ident}_Event_to_string(event),
648                   ${ident}_State_to_string(next_state),
649                   "Resource Stall");
650        }
651    } else if (result == TransitionResult_ProtocolStall) {
652        DEBUG_MSG(GENERATED_COMP, HighPrio, "stalling");
653        DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
654        if (Debug::getProtocolTrace()) {
655            g_system_ptr->getProfiler()->profileTransition("${ident}", m_version, addr,
656                   ${ident}_State_to_string(state),
657                   ${ident}_Event_to_string(event),
658                   ${ident}_State_to_string(next_state),
659                   "Protocol Stall");
660        }
661    }
662
663    return result;
664}
665
666TransitionResult ${ident}_Controller::doTransitionWorker(${ident}_Event event, ${ident}_State state, ${ident}_State& next_state, const Address& addr
667)
668{
669    switch(HASH_FUN(state, event)) {
670''')
671
672        # This map will allow suppress generating duplicate code
673        cases = orderdict()
674
675        for trans in self.transitions:
676            case_string = "%s_State_%s, %s_Event_%s" % \
677                (self.ident, trans.state.ident, self.ident, trans.event.ident)
678
679            case = code_formatter()
680            # Only set next_state if it changes
681            if trans.state != trans.nextState:
682                ns_ident = trans.nextState.ident
683                case('next_state = ${ident}_State_${ns_ident};')
684
685            actions = trans.actions
686
687            # Check for resources
688            case_sorter = []
689            res = trans.resources
690            for key,val in res.iteritems():
691                if key.type.ident != "DNUCAStopTable":
692                    val = '''
693if (!%s.areNSlotsAvailable(%s)) {
694    return TransitionResult_ResourceStall;
695}
696''' % (key.code, val)
697                case_sorter.append(val)
698
699
700            # Emit the code sequences in a sorted order.  This makes the
701            # output deterministic (without this the output order can vary
702            # since Map's keys() on a vector of pointers is not deterministic
703            for c in sorted(case_sorter):
704                case("$c")
705
706            # Figure out if we stall
707            stall = False
708            for action in actions:
709                if action.ident == "z_stall":
710                    stall = True
711                    break
712
713            if stall:
714                case('return TransitionResult_ProtocolStall;')
715            else:
716                for action in actions:
717                    case('${{action.ident}}(addr);')
718                case('return TransitionResult_Valid;')
719
720            case = str(case)
721
722            # Look to see if this transition code is unique.
723            if case not in cases:
724                cases[case] = []
725
726            cases[case].append(case_string)
727
728        # Walk through all of the unique code blocks and spit out the
729        # corresponding case statement elements
730        for case,transitions in cases.iteritems():
731            # Iterative over all the multiple transitions that share
732            # the same code
733            for trans in transitions:
734                code('  case HASH_FUN($trans):')
735            code('  {')
736            code('    $case')
737            code('  }')
738
739        code('''
740      default:
741        WARN_EXPR(m_version);
742        WARN_EXPR(g_eventQueue_ptr->getTime());
743        WARN_EXPR(addr);
744        WARN_EXPR(event);
745        WARN_EXPR(state);
746        ERROR_MSG(\"Invalid transition\");
747    }
748    return TransitionResult_Valid;
749}
750''')
751        code.write(path, "%s_Transitions.cc" % self.ident)
752
753    def printProfilerHH(self, path):
754        code = code_formatter()
755        ident = self.ident
756
757        code('''
758// Auto generated C++ code started by $__file__:$__line__
759// ${ident}: ${{self.short}}
760
761#ifndef ${ident}_PROFILER_H
762#define ${ident}_PROFILER_H
763
764#include "mem/ruby/common/Global.hh"
765#include "mem/protocol/${ident}_State.hh"
766#include "mem/protocol/${ident}_Event.hh"
767
768class ${ident}_Profiler {
769  public:
770    ${ident}_Profiler();
771    void setVersion(int version);
772    void countTransition(${ident}_State state, ${ident}_Event event);
773    void possibleTransition(${ident}_State state, ${ident}_Event event);
774    void dumpStats(ostream& out) const;
775    void clearStats();
776
777  private:
778    int m_counters[${ident}_State_NUM][${ident}_Event_NUM];
779    int m_event_counters[${ident}_Event_NUM];
780    bool m_possible[${ident}_State_NUM][${ident}_Event_NUM];
781    int m_version;
782};
783
784#endif // ${ident}_PROFILER_H
785''')
786        code.write(path, "%s_Profiler.hh" % self.ident)
787
788    def printProfilerCC(self, path):
789        code = code_formatter()
790        ident = self.ident
791
792        code('''
793// Auto generated C++ code started by $__file__:$__line__
794// ${ident}: ${{self.short}}
795
796#include "mem/protocol/${ident}_Profiler.hh"
797
798${ident}_Profiler::${ident}_Profiler()
799{
800    for (int state = 0; state < ${ident}_State_NUM; state++) {
801        for (int event = 0; event < ${ident}_Event_NUM; event++) {
802            m_possible[state][event] = false;
803            m_counters[state][event] = 0;
804        }
805    }
806    for (int event = 0; event < ${ident}_Event_NUM; event++) {
807        m_event_counters[event] = 0;
808    }
809}
810void ${ident}_Profiler::setVersion(int version)
811{
812    m_version = version;
813}
814void ${ident}_Profiler::clearStats()
815{
816    for (int state = 0; state < ${ident}_State_NUM; state++) {
817        for (int event = 0; event < ${ident}_Event_NUM; event++) {
818            m_counters[state][event] = 0;
819        }
820    }
821
822    for (int event = 0; event < ${ident}_Event_NUM; event++) {
823        m_event_counters[event] = 0;
824    }
825}
826void ${ident}_Profiler::countTransition(${ident}_State state, ${ident}_Event event)
827{
828    assert(m_possible[state][event]);
829    m_counters[state][event]++;
830    m_event_counters[event]++;
831}
832void ${ident}_Profiler::possibleTransition(${ident}_State state, ${ident}_Event event)
833{
834    m_possible[state][event] = true;
835}
836void ${ident}_Profiler::dumpStats(ostream& out) const
837{
838    out << " --- ${ident} " << m_version << " ---" << endl;
839    out << " - Event Counts -" << endl;
840    for (int event = 0; event < ${ident}_Event_NUM; event++) {
841        int count = m_event_counters[event];
842        out << (${ident}_Event) event << "  " << count << endl;
843    }
844    out << endl;
845    out << " - Transitions -" << endl;
846    for (int state = 0; state < ${ident}_State_NUM; state++) {
847        for (int event = 0; event < ${ident}_Event_NUM; event++) {
848            if (m_possible[state][event]) {
849                int count = m_counters[state][event];
850                out << (${ident}_State) state << "  " << (${ident}_Event) event << "  " << count;
851                if (count == 0) {
852                    out << " <-- ";
853                }
854                out << endl;
855            }
856        }
857        out << endl;
858    }
859}
860''')
861        code.write(path, "%s_Profiler.cc" % self.ident)
862
863    # **************************
864    # ******* HTML Files *******
865    # **************************
866    def frameRef(self, click_href, click_target, over_href, over_target_num,
867                 text):
868        code = code_formatter(fix_newlines=False)
869        code("""<A href=\"$click_href\" target=\"$click_target\" onMouseOver=\"if (parent.frames[$over_target_num].location != parent.location + '$over_href') { parent.frames[$over_target_num].location='$over_href' }\" >${{html.formatShorthand(text)}}</A>""")
870        return str(code)
871
872    def writeHTMLFiles(self, path):
873        # Create table with no row hilighted
874        self.printHTMLTransitions(path, None)
875
876        # Generate transition tables
877        for state in self.states.itervalues():
878            self.printHTMLTransitions(path, state)
879
880        # Generate action descriptions
881        for action in self.actions.itervalues():
882            name = "%s_action_%s.html" % (self.ident, action.ident)
883            code = html.createSymbol(action, "Action")
884            code.write(path, name)
885
886        # Generate state descriptions
887        for state in self.states.itervalues():
888            name = "%s_State_%s.html" % (self.ident, state.ident)
889            code = html.createSymbol(state, "State")
890            code.write(path, name)
891
892        # Generate event descriptions
893        for event in self.events.itervalues():
894            name = "%s_Event_%s.html" % (self.ident, event.ident)
895            code = html.createSymbol(event, "Event")
896            code.write(path, name)
897
898    def printHTMLTransitions(self, path, active_state):
899        code = code_formatter()
900
901        code('''
902<HTML><BODY link="blue" vlink="blue">
903
904<H1 align="center">${{html.formatShorthand(self.short)}}:
905''')
906        code.indent()
907        for i,machine in enumerate(self.symtab.getAllType(StateMachine)):
908            mid = machine.ident
909            if i != 0:
910                extra = " - "
911            else:
912                extra = ""
913            if machine == self:
914                code('$extra$mid')
915            else:
916                code('$extra<A target="Table" href="${mid}_table.html">$mid</A>')
917        code.dedent()
918
919        code("""
920</H1>
921
922<TABLE border=1>
923<TR>
924  <TH> </TH>
925""")
926
927        for event in self.events.itervalues():
928            href = "%s_Event_%s.html" % (self.ident, event.ident)
929            ref = self.frameRef(href, "Status", href, "1", event.short)
930            code('<TH bgcolor=white>$ref</TH>')
931
932        code('</TR>')
933        # -- Body of table
934        for state in self.states.itervalues():
935            # -- Each row
936            if state == active_state:
937                color = "yellow"
938            else:
939                color = "white"
940
941            click = "%s_table_%s.html" % (self.ident, state.ident)
942            over = "%s_State_%s.html" % (self.ident, state.ident)
943            text = html.formatShorthand(state.short)
944            ref = self.frameRef(click, "Table", over, "1", state.short)
945            code('''
946<TR>
947  <TH bgcolor=$color>$ref</TH>
948''')
949
950            # -- One column for each event
951            for event in self.events.itervalues():
952                trans = self.table.get((state,event), None)
953                if trans is None:
954                    # This is the no transition case
955                    if state == active_state:
956                        color = "#C0C000"
957                    else:
958                        color = "lightgrey"
959
960                    code('<TD bgcolor=$color>&nbsp;</TD>')
961                    continue
962
963                next = trans.nextState
964                stall_action = False
965
966                # -- Get the actions
967                for action in trans.actions:
968                    if action.ident == "z_stall" or \
969                       action.ident == "zz_recycleMandatoryQueue":
970                        stall_action = True
971
972                # -- Print out "actions/next-state"
973                if stall_action:
974                    if state == active_state:
975                        color = "#C0C000"
976                    else:
977                        color = "lightgrey"
978
979                elif active_state and next.ident == active_state.ident:
980                    color = "aqua"
981                elif state == active_state:
982                    color = "yellow"
983                else:
984                    color = "white"
985
986                fix = code.nofix()
987                code('<TD bgcolor=$color>')
988                for action in trans.actions:
989                    href = "%s_action_%s.html" % (self.ident, action.ident)
990                    ref = self.frameRef(href, "Status", href, "1",
991                                        action.short)
992                    code('  $ref\n')
993                if next != state:
994                    if trans.actions:
995                        code('/')
996                    click = "%s_table_%s.html" % (self.ident, next.ident)
997                    over = "%s_State_%s.html" % (self.ident, next.ident)
998                    ref = self.frameRef(click, "Table", over, "1", next.short)
999                    code("$ref")
1000                code("</TD>\n")
1001                code.fix(fix)
1002
1003            # -- Each row
1004            if state == active_state:
1005                color = "yellow"
1006            else:
1007                color = "white"
1008
1009            click = "%s_table_%s.html" % (self.ident, state.ident)
1010            over = "%s_State_%s.html" % (self.ident, state.ident)
1011            ref = self.frameRef(click, "Table", over, "1", state.short)
1012            code('''
1013  <TH bgcolor=$color>$ref</TH>
1014</TR>
1015''')
1016        code('''
1017<TR>
1018  <TH> </TH>
1019''')
1020
1021        for event in self.events.itervalues():
1022            href = "%s_Event_%s.html" % (self.ident, event.ident)
1023            ref = self.frameRef(href, "Status", href, "1", event.short)
1024            code('<TH bgcolor=white>$ref</TH>')
1025        code('''
1026</TR>
1027</TABLE>
1028</BODY></HTML>
1029''')
1030
1031
1032        if active_state:
1033            name = "%s_table_%s.html" % (self.ident, active_state.ident)
1034        else:
1035            name = "%s_table.html" % self.ident
1036        code.write(path, name)
1037
1038__all__ = [ "StateMachine" ]
1039