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