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