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