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