StateMachine.py revision 6902:b5baf1dc44b4
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
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 = code_formatter()
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 = code_formatter()
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 = code_formatter()
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''')
353        code.indent()
354
355        #
356        # After initializing the universal machine parameters, initialize the
357        # this machines config parameters.  Also detemine if these configuration
358        # params include a sequencer.  This information will be used later for
359        # contecting the sequencer back to the L1 cache controller.
360        #
361        contains_sequencer = False
362        for param in self.config_parameters:
363            if param.name == "sequencer" or param.name == "dma_sequencer":
364                contains_sequencer = True
365            if param.pointer:
366                code('m_${{param.name}}_ptr = p->${{param.name}};')
367            else:
368                code('m_${{param.name}} = p->${{param.name}};')
369
370        #
371        # For the l1 cache controller, add the special atomic support which
372        # includes passing the sequencer a pointer to the controller.
373        #
374        if self.ident == "L1Cache":
375            if not contains_sequencer:
376                self.error("The L1Cache controller must include the sequencer " \
377                           "configuration parameter")
378
379            code('''
380m_sequencer_ptr->setController(this);
381''')
382        #
383        # For the DMA controller, pass the sequencer a pointer to the
384        # controller.
385        #
386        if self.ident == "DMA":
387            if not contains_sequencer:
388                self.error("The DMA controller must include the sequencer " \
389                           "configuration parameter")
390
391            code('''
392m_dma_sequencer_ptr->setController(this);
393''')
394
395        code('m_num_controllers++;')
396        for var in self.objects:
397            if var.ident.find("mandatoryQueue") >= 0:
398                code('m_${{var.c_ident}}_ptr = new ${{var.type.c_ident}}();')
399
400        code.dedent()
401        code('''
402}
403
404void $c_ident::init()
405{
406    m_machineID.type = MachineType_${ident};
407    m_machineID.num = m_version;
408
409    // Objects
410    s_profiler.setVersion(m_version);
411''')
412
413        code.indent()
414        for var in self.objects:
415            vtype = var.type
416            vid = "m_%s_ptr" % var.c_ident
417            if "network" not in var:
418                # Not a network port object
419                if "primitive" in vtype:
420                    code('$vid = new ${{vtype.c_ident}};')
421                    if "default" in var:
422                        code('(*$vid) = ${{var["default"]}};')
423                else:
424                    # Normal Object
425                    # added by SS
426                    if "factory" in var:
427                        code('$vid = ${{var["factory"]}};')
428                    elif var.ident.find("mandatoryQueue") < 0:
429                        th = var.get("template_hack", "")
430                        expr = "%s  = new %s%s" % (vid, vtype.c_ident, th)
431
432                        args = ""
433                        if "non_obj" not in vtype and not vtype.isEnumeration:
434                            if expr.find("TBETable") >= 0:
435                                args = "m_number_of_TBEs"
436                            else:
437                                args = var.get("constructor_hack", "")
438                            args = "(%s)" % args
439
440                        code('$expr$args;')
441                    else:
442                        code(';')
443
444                    code('assert($vid != NULL);')
445
446                    if "default" in var:
447                        code('(*$vid) = ${{var["default"]}}; // Object default')
448                    elif "default" in vtype:
449                        code('(*$vid) = ${{vtype["default"]}}; // Type ${{vtype.ident}} default')
450
451                    # Set ordering
452                    if "ordered" in var and "trigger_queue" not in var:
453                        # A buffer
454                        code('$vid->setOrdering(${{var["ordered"]}});')
455
456                    # Set randomization
457                    if "random" in var:
458                        # A buffer
459                        code('$vid->setRandomization(${{var["random"]}});')
460
461                    # Set Priority
462                    if vtype.isBuffer and \
463                           "rank" in var and "trigger_queue" not in var:
464                        code('$vid->setPriority(${{var["rank"]}});')
465            else:
466                # Network port object
467                network = var["network"]
468                ordered =  var["ordered"]
469                vnet = var["virtual_network"]
470
471                assert var.machine is not None
472                code('''
473$vid = m_net_ptr->get${network}NetQueue(m_version+MachineType_base_number(string_to_MachineType("${{var.machine.ident}}")), $ordered, $vnet);
474''')
475
476                code('assert($vid != NULL);')
477
478                # Set ordering
479                if "ordered" in var:
480                    # A buffer
481                    code('$vid->setOrdering(${{var["ordered"]}});')
482
483                # Set randomization
484                if "random" in var:
485                    # A buffer
486                    code('$vid->setRandomization(${{var["random"]}})')
487
488                # Set Priority
489                if "rank" in var:
490                    code('$vid->setPriority(${{var["rank"]}})')
491
492                # Set buffer size
493                if vtype.isBuffer:
494                    code('''
495if (m_buffer_size > 0) {
496    $vid->setSize(m_buffer_size);
497}
498''')
499
500                # set description (may be overriden later by port def)
501                code('$vid->setDescription("[Version " + int_to_string(m_version) + ", ${ident}, name=${{var.c_ident}}]");')
502
503        # Set the queue consumers
504        code.insert_newline()
505        for port in self.in_ports:
506            code('${{port.code}}.setConsumer(this);')
507
508        # Set the queue descriptions
509        code.insert_newline()
510        for port in self.in_ports:
511            code('${{port.code}}.setDescription("[Version " + int_to_string(m_version) + ", $ident, $port]");')
512
513        # Initialize the transition profiling
514        code.insert_newline()
515        for trans in self.transitions:
516            # Figure out if we stall
517            stall = False
518            for action in trans.actions:
519                if action.ident == "z_stall":
520                    stall = True
521
522            # Only possible if it is not a 'z' case
523            if not stall:
524                state = "%s_State_%s" % (self.ident, trans.state.ident)
525                event = "%s_Event_%s" % (self.ident, trans.event.ident)
526                code('s_profiler.possibleTransition($state, $event);')
527
528        # added by SS to initialize recycle_latency of message buffers
529        for buf in self.message_buffer_names:
530            code("$buf->setRecycleLatency(m_recycle_latency);")
531
532        code.dedent()
533        code('}')
534
535        has_mandatory_q = False
536        for port in self.in_ports:
537            if port.code.find("mandatoryQueue_ptr") >= 0:
538                has_mandatory_q = True
539
540        if has_mandatory_q:
541            mq_ident = "m_%s_mandatoryQueue_ptr" % self.ident
542        else:
543            mq_ident = "NULL"
544
545        code('''
546int $c_ident::getNumControllers() {
547    return m_num_controllers;
548}
549
550MessageBuffer* $c_ident::getMandatoryQueue() const {
551    return $mq_ident;
552}
553
554const int & $c_ident::getVersion() const{
555    return m_version;
556}
557
558const string $c_ident::toString() const{
559    return "$c_ident";
560}
561
562const string $c_ident::getName() const{
563    return m_name;
564}
565const MachineType $c_ident::getMachineType() const{
566    return MachineType_${ident};
567}
568
569void $c_ident::blockOnQueue(Address addr, MessageBuffer* port) {
570    m_is_blocking = true;
571    m_block_map[addr] = port;
572}
573void $c_ident::unblock(Address addr) {
574    m_block_map.erase(addr);
575    if (m_block_map.size() == 0) {
576       m_is_blocking = false;
577    }
578}
579
580void $c_ident::print(ostream& out) const { out << "[$c_ident " << m_version << "]"; }
581
582void $c_ident::printConfig(ostream& out) const {
583    out << "$c_ident config: " << m_name << endl;
584    out << "  version: " << m_version << endl;
585    for (map<string, string>::const_iterator it = m_cfg.begin(); it != m_cfg.end(); it++) {
586        out << "  " << (*it).first << ": " << (*it).second << endl;
587    }
588}
589
590void $c_ident::printStats(ostream& out) const {
591''')
592        #
593        # Cache and Memory Controllers have specific profilers associated with
594        # them.  Print out these stats before dumping state transition stats.
595        #
596        for param in self.config_parameters:
597            if param.type_ast.type.ident == "CacheMemory" or \
598                   param.type_ast.type.ident == "MemoryControl":
599                assert(param.pointer)
600                code('    m_${{param.ident}}_ptr->printStats(out);')
601
602        code('''
603    s_profiler.dumpStats(out);
604}
605
606void $c_ident::clearStats() {
607''')
608        #
609        # Cache and Memory Controllers have specific profilers associated with
610        # them.  These stats must be cleared too.
611        #
612        for param in self.config_parameters:
613            if param.type_ast.type.ident == "CacheMemory" or \
614                   param.type_ast.type.ident == "MemoryControl":
615                assert(param.pointer)
616                code('    m_${{param.ident}}_ptr->clearStats();')
617
618        code('''
619    s_profiler.clearStats();
620}
621
622// Actions
623''')
624
625        for action in self.actions.itervalues():
626            if "c_code" not in action:
627                continue
628
629            code('''
630/** \\brief ${{action.desc}} */
631void $c_ident::${{action.ident}}(const Address& addr)
632{
633    DEBUG_MSG(GENERATED_COMP, HighPrio, "executing");
634    ${{action["c_code"]}}
635}
636
637''')
638        code.write(path, "%s.cc" % c_ident)
639
640    def printCWakeup(self, path):
641        '''Output the wakeup loop for the events'''
642
643        code = code_formatter()
644        ident = self.ident
645
646        code('''
647// Auto generated C++ code started by $__file__:$__line__
648// ${ident}: ${{self.short}}
649
650#include "mem/ruby/common/Global.hh"
651#include "mem/ruby/slicc_interface/RubySlicc_includes.hh"
652#include "mem/protocol/${ident}_Controller.hh"
653#include "mem/protocol/${ident}_State.hh"
654#include "mem/protocol/${ident}_Event.hh"
655#include "mem/protocol/Types.hh"
656#include "mem/ruby/system/System.hh"
657
658void ${ident}_Controller::wakeup()
659{
660
661    int counter = 0;
662    while (true) {
663        // Some cases will put us into an infinite loop without this limit
664        assert(counter <= m_transitions_per_cycle);
665        if (counter == m_transitions_per_cycle) {
666            g_system_ptr->getProfiler()->controllerBusy(m_machineID); // Count how often we\'re fully utilized
667            g_eventQueue_ptr->scheduleEvent(this, 1); // Wakeup in another cycle and try again
668            break;
669        }
670''')
671
672        code.indent()
673        code.indent()
674
675        # InPorts
676        #
677        for port in self.in_ports:
678            code.indent()
679            code('// ${ident}InPort $port')
680            code('${{port["c_code_in_port"]}}')
681            code.dedent()
682
683            code('')
684
685        code.dedent()
686        code.dedent()
687        code('''
688        break;  // If we got this far, we have nothing left todo
689    }
690}
691''')
692
693        code.write(path, "%s_Wakeup.cc" % self.ident)
694
695    def printCSwitch(self, path):
696        '''Output switch statement for transition table'''
697
698        code = code_formatter()
699        ident = self.ident
700
701        code('''
702// Auto generated C++ code started by $__file__:$__line__
703// ${ident}: ${{self.short}}
704
705#include "mem/ruby/common/Global.hh"
706#include "mem/protocol/${ident}_Controller.hh"
707#include "mem/protocol/${ident}_State.hh"
708#include "mem/protocol/${ident}_Event.hh"
709#include "mem/protocol/Types.hh"
710#include "mem/ruby/system/System.hh"
711
712#define HASH_FUN(state, event)  ((int(state)*${ident}_Event_NUM)+int(event))
713
714#define GET_TRANSITION_COMMENT() (${ident}_transitionComment.str())
715#define CLEAR_TRANSITION_COMMENT() (${ident}_transitionComment.str(""))
716
717TransitionResult ${ident}_Controller::doTransition(${ident}_Event event, ${ident}_State state, const Address& addr
718)
719{
720    ${ident}_State next_state = state;
721
722    DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
723    DEBUG_MSG(GENERATED_COMP, MedPrio, *this);
724    DEBUG_EXPR(GENERATED_COMP, MedPrio, g_eventQueue_ptr->getTime());
725    DEBUG_EXPR(GENERATED_COMP, MedPrio,state);
726    DEBUG_EXPR(GENERATED_COMP, MedPrio,event);
727    DEBUG_EXPR(GENERATED_COMP, MedPrio,addr);
728
729    TransitionResult result = doTransitionWorker(event, state, next_state, addr);
730
731    if (result == TransitionResult_Valid) {
732        DEBUG_EXPR(GENERATED_COMP, MedPrio, next_state);
733        DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
734        s_profiler.countTransition(state, event);
735        if (Debug::getProtocolTrace()) {
736            g_system_ptr->getProfiler()->profileTransition("${ident}", m_version, addr,
737                    ${ident}_State_to_string(state),
738                    ${ident}_Event_to_string(event),
739                    ${ident}_State_to_string(next_state), GET_TRANSITION_COMMENT());
740        }
741    CLEAR_TRANSITION_COMMENT();
742    ${ident}_setState(addr, next_state);
743
744    } else if (result == TransitionResult_ResourceStall) {
745        if (Debug::getProtocolTrace()) {
746            g_system_ptr->getProfiler()->profileTransition("${ident}", m_version, addr,
747                   ${ident}_State_to_string(state),
748                   ${ident}_Event_to_string(event),
749                   ${ident}_State_to_string(next_state),
750                   "Resource Stall");
751        }
752    } else if (result == TransitionResult_ProtocolStall) {
753        DEBUG_MSG(GENERATED_COMP, HighPrio, "stalling");
754        DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
755        if (Debug::getProtocolTrace()) {
756            g_system_ptr->getProfiler()->profileTransition("${ident}", m_version, addr,
757                   ${ident}_State_to_string(state),
758                   ${ident}_Event_to_string(event),
759                   ${ident}_State_to_string(next_state),
760                   "Protocol Stall");
761        }
762    }
763
764    return result;
765}
766
767TransitionResult ${ident}_Controller::doTransitionWorker(${ident}_Event event, ${ident}_State state, ${ident}_State& next_state, const Address& addr
768)
769{
770    switch(HASH_FUN(state, event)) {
771''')
772
773        # This map will allow suppress generating duplicate code
774        cases = orderdict()
775
776        for trans in self.transitions:
777            case_string = "%s_State_%s, %s_Event_%s" % \
778                (self.ident, trans.state.ident, self.ident, trans.event.ident)
779
780            case = code_formatter()
781            # Only set next_state if it changes
782            if trans.state != trans.nextState:
783                ns_ident = trans.nextState.ident
784                case('next_state = ${ident}_State_${ns_ident};')
785
786            actions = trans.actions
787
788            # Check for resources
789            case_sorter = []
790            res = trans.resources
791            for key,val in res.iteritems():
792                if key.type.ident != "DNUCAStopTable":
793                    val = '''
794if (!%s.areNSlotsAvailable(%s)) {
795    return TransitionResult_ResourceStall;
796}
797''' % (key.code, val)
798                case_sorter.append(val)
799
800
801            # Emit the code sequences in a sorted order.  This makes the
802            # output deterministic (without this the output order can vary
803            # since Map's keys() on a vector of pointers is not deterministic
804            for c in sorted(case_sorter):
805                case("$c")
806
807            # Figure out if we stall
808            stall = False
809            for action in actions:
810                if action.ident == "z_stall":
811                    stall = True
812                    break
813
814            if stall:
815                case('return TransitionResult_ProtocolStall;')
816            else:
817                for action in actions:
818                    case('${{action.ident}}(addr);')
819                case('return TransitionResult_Valid;')
820
821            case = str(case)
822
823            # Look to see if this transition code is unique.
824            if case not in cases:
825                cases[case] = []
826
827            cases[case].append(case_string)
828
829        # Walk through all of the unique code blocks and spit out the
830        # corresponding case statement elements
831        for case,transitions in cases.iteritems():
832            # Iterative over all the multiple transitions that share
833            # the same code
834            for trans in transitions:
835                code('  case HASH_FUN($trans):')
836            code('  {')
837            code('    $case')
838            code('  }')
839
840        code('''
841      default:
842        WARN_EXPR(m_version);
843        WARN_EXPR(g_eventQueue_ptr->getTime());
844        WARN_EXPR(addr);
845        WARN_EXPR(event);
846        WARN_EXPR(state);
847        ERROR_MSG(\"Invalid transition\");
848    }
849    return TransitionResult_Valid;
850}
851''')
852        code.write(path, "%s_Transitions.cc" % self.ident)
853
854    def printProfilerHH(self, path):
855        code = code_formatter()
856        ident = self.ident
857
858        code('''
859// Auto generated C++ code started by $__file__:$__line__
860// ${ident}: ${{self.short}}
861
862#ifndef ${ident}_PROFILER_H
863#define ${ident}_PROFILER_H
864
865#include "mem/ruby/common/Global.hh"
866#include "mem/protocol/${ident}_State.hh"
867#include "mem/protocol/${ident}_Event.hh"
868
869class ${ident}_Profiler {
870  public:
871    ${ident}_Profiler();
872    void setVersion(int version);
873    void countTransition(${ident}_State state, ${ident}_Event event);
874    void possibleTransition(${ident}_State state, ${ident}_Event event);
875    void dumpStats(ostream& out) const;
876    void clearStats();
877
878  private:
879    int m_counters[${ident}_State_NUM][${ident}_Event_NUM];
880    int m_event_counters[${ident}_Event_NUM];
881    bool m_possible[${ident}_State_NUM][${ident}_Event_NUM];
882    int m_version;
883};
884
885#endif // ${ident}_PROFILER_H
886''')
887        code.write(path, "%s_Profiler.hh" % self.ident)
888
889    def printProfilerCC(self, path):
890        code = code_formatter()
891        ident = self.ident
892
893        code('''
894// Auto generated C++ code started by $__file__:$__line__
895// ${ident}: ${{self.short}}
896
897#include "mem/protocol/${ident}_Profiler.hh"
898
899${ident}_Profiler::${ident}_Profiler()
900{
901    for (int state = 0; state < ${ident}_State_NUM; state++) {
902        for (int event = 0; event < ${ident}_Event_NUM; event++) {
903            m_possible[state][event] = false;
904            m_counters[state][event] = 0;
905        }
906    }
907    for (int event = 0; event < ${ident}_Event_NUM; event++) {
908        m_event_counters[event] = 0;
909    }
910}
911void ${ident}_Profiler::setVersion(int version)
912{
913    m_version = version;
914}
915void ${ident}_Profiler::clearStats()
916{
917    for (int state = 0; state < ${ident}_State_NUM; state++) {
918        for (int event = 0; event < ${ident}_Event_NUM; event++) {
919            m_counters[state][event] = 0;
920        }
921    }
922
923    for (int event = 0; event < ${ident}_Event_NUM; event++) {
924        m_event_counters[event] = 0;
925    }
926}
927void ${ident}_Profiler::countTransition(${ident}_State state, ${ident}_Event event)
928{
929    assert(m_possible[state][event]);
930    m_counters[state][event]++;
931    m_event_counters[event]++;
932}
933void ${ident}_Profiler::possibleTransition(${ident}_State state, ${ident}_Event event)
934{
935    m_possible[state][event] = true;
936}
937void ${ident}_Profiler::dumpStats(ostream& out) const
938{
939    out << " --- ${ident} " << m_version << " ---" << endl;
940    out << " - Event Counts -" << endl;
941    for (int event = 0; event < ${ident}_Event_NUM; event++) {
942        int count = m_event_counters[event];
943        out << (${ident}_Event) event << "  " << count << endl;
944    }
945    out << endl;
946    out << " - Transitions -" << endl;
947    for (int state = 0; state < ${ident}_State_NUM; state++) {
948        for (int event = 0; event < ${ident}_Event_NUM; event++) {
949            if (m_possible[state][event]) {
950                int count = m_counters[state][event];
951                out << (${ident}_State) state << "  " << (${ident}_Event) event << "  " << count;
952                if (count == 0) {
953                    out << " <-- ";
954                }
955                out << endl;
956            }
957        }
958        out << endl;
959    }
960}
961''')
962        code.write(path, "%s_Profiler.cc" % self.ident)
963
964    # **************************
965    # ******* HTML Files *******
966    # **************************
967    def frameRef(self, click_href, click_target, over_href, over_target_num,
968                 text):
969        code = code_formatter(fix_newlines=False)
970        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>""")
971        return str(code)
972
973    def writeHTMLFiles(self, path):
974        # Create table with no row hilighted
975        self.printHTMLTransitions(path, None)
976
977        # Generate transition tables
978        for state in self.states.itervalues():
979            self.printHTMLTransitions(path, state)
980
981        # Generate action descriptions
982        for action in self.actions.itervalues():
983            name = "%s_action_%s.html" % (self.ident, action.ident)
984            code = html.createSymbol(action, "Action")
985            code.write(path, name)
986
987        # Generate state descriptions
988        for state in self.states.itervalues():
989            name = "%s_State_%s.html" % (self.ident, state.ident)
990            code = html.createSymbol(state, "State")
991            code.write(path, name)
992
993        # Generate event descriptions
994        for event in self.events.itervalues():
995            name = "%s_Event_%s.html" % (self.ident, event.ident)
996            code = html.createSymbol(event, "Event")
997            code.write(path, name)
998
999    def printHTMLTransitions(self, path, active_state):
1000        code = code_formatter()
1001
1002        code('''
1003<HTML><BODY link="blue" vlink="blue">
1004
1005<H1 align="center">${{html.formatShorthand(self.short)}}:
1006''')
1007        code.indent()
1008        for i,machine in enumerate(self.symtab.getAllType(StateMachine)):
1009            mid = machine.ident
1010            if i != 0:
1011                extra = " - "
1012            else:
1013                extra = ""
1014            if machine == self:
1015                code('$extra$mid')
1016            else:
1017                code('$extra<A target="Table" href="${mid}_table.html">$mid</A>')
1018        code.dedent()
1019
1020        code("""
1021</H1>
1022
1023<TABLE border=1>
1024<TR>
1025  <TH> </TH>
1026""")
1027
1028        for event in self.events.itervalues():
1029            href = "%s_Event_%s.html" % (self.ident, event.ident)
1030            ref = self.frameRef(href, "Status", href, "1", event.short)
1031            code('<TH bgcolor=white>$ref</TH>')
1032
1033        code('</TR>')
1034        # -- Body of table
1035        for state in self.states.itervalues():
1036            # -- Each row
1037            if state == active_state:
1038                color = "yellow"
1039            else:
1040                color = "white"
1041
1042            click = "%s_table_%s.html" % (self.ident, state.ident)
1043            over = "%s_State_%s.html" % (self.ident, state.ident)
1044            text = html.formatShorthand(state.short)
1045            ref = self.frameRef(click, "Table", over, "1", state.short)
1046            code('''
1047<TR>
1048  <TH bgcolor=$color>$ref</TH>
1049''')
1050
1051            # -- One column for each event
1052            for event in self.events.itervalues():
1053                trans = self.table.get((state,event), None)
1054                if trans is None:
1055                    # This is the no transition case
1056                    if state == active_state:
1057                        color = "#C0C000"
1058                    else:
1059                        color = "lightgrey"
1060
1061                    code('<TD bgcolor=$color>&nbsp;</TD>')
1062                    continue
1063
1064                next = trans.nextState
1065                stall_action = False
1066
1067                # -- Get the actions
1068                for action in trans.actions:
1069                    if action.ident == "z_stall" or \
1070                       action.ident == "zz_recycleMandatoryQueue":
1071                        stall_action = True
1072
1073                # -- Print out "actions/next-state"
1074                if stall_action:
1075                    if state == active_state:
1076                        color = "#C0C000"
1077                    else:
1078                        color = "lightgrey"
1079
1080                elif active_state and next.ident == active_state.ident:
1081                    color = "aqua"
1082                elif state == active_state:
1083                    color = "yellow"
1084                else:
1085                    color = "white"
1086
1087                fix = code.nofix()
1088                code('<TD bgcolor=$color>')
1089                for action in trans.actions:
1090                    href = "%s_action_%s.html" % (self.ident, action.ident)
1091                    ref = self.frameRef(href, "Status", href, "1",
1092                                        action.short)
1093                    code('  $ref\n')
1094                if next != state:
1095                    if trans.actions:
1096                        code('/')
1097                    click = "%s_table_%s.html" % (self.ident, next.ident)
1098                    over = "%s_State_%s.html" % (self.ident, next.ident)
1099                    ref = self.frameRef(click, "Table", over, "1", next.short)
1100                    code("$ref")
1101                code("</TD>\n")
1102                code.fix(fix)
1103
1104            # -- Each row
1105            if state == active_state:
1106                color = "yellow"
1107            else:
1108                color = "white"
1109
1110            click = "%s_table_%s.html" % (self.ident, state.ident)
1111            over = "%s_State_%s.html" % (self.ident, state.ident)
1112            ref = self.frameRef(click, "Table", over, "1", state.short)
1113            code('''
1114  <TH bgcolor=$color>$ref</TH>
1115</TR>
1116''')
1117        code('''
1118<TR>
1119  <TH> </TH>
1120''')
1121
1122        for event in self.events.itervalues():
1123            href = "%s_Event_%s.html" % (self.ident, event.ident)
1124            ref = self.frameRef(href, "Status", href, "1", event.short)
1125            code('<TH bgcolor=white>$ref</TH>')
1126        code('''
1127</TR>
1128</TABLE>
1129</BODY></HTML>
1130''')
1131
1132
1133        if active_state:
1134            name = "%s_table_%s.html" % (self.ident, active_state.ident)
1135        else:
1136            name = "%s_table.html" % self.ident
1137        code.write(path, name)
1138
1139__all__ = [ "StateMachine" ]
1140