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