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