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