StateMachine.py revision 7542:49327b849c7f
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            else:
498                # Network port object
499                network = var["network"]
500                ordered =  var["ordered"]
501                vnet = var["virtual_network"]
502
503                assert var.machine is not None
504                code('''
505machine_type = string_to_MachineType("${{var.machine.ident}}");
506base = MachineType_base_number(machine_type);
507$vid = m_net_ptr->get${network}NetQueue(m_version + base, $ordered, $vnet);
508''')
509
510                code('assert($vid != NULL);')
511
512                # Set ordering
513                if "ordered" in var:
514                    # A buffer
515                    code('$vid->setOrdering(${{var["ordered"]}});')
516
517                # Set randomization
518                if "random" in var:
519                    # A buffer
520                    code('$vid->setRandomization(${{var["random"]}})')
521
522                # Set Priority
523                if "rank" in var:
524                    code('$vid->setPriority(${{var["rank"]}})')
525
526                # Set buffer size
527                if vtype.isBuffer:
528                    code('''
529if (m_buffer_size > 0) {
530    $vid->resize(m_buffer_size);
531}
532''')
533
534                # set description (may be overriden later by port def)
535                code('''
536$vid->setDescription("[Version " + to_string(m_version) + ", ${ident}, name=${{var.c_ident}}]");
537
538''')
539
540        # Set the queue consumers
541        code.insert_newline()
542        for port in self.in_ports:
543            code('${{port.code}}.setConsumer(this);')
544
545        # Set the queue descriptions
546        code.insert_newline()
547        for port in self.in_ports:
548            code('${{port.code}}.setDescription("[Version " + to_string(m_version) + ", $ident, $port]");')
549
550        # Initialize the transition profiling
551        code.insert_newline()
552        for trans in self.transitions:
553            # Figure out if we stall
554            stall = False
555            for action in trans.actions:
556                if action.ident == "z_stall":
557                    stall = True
558
559            # Only possible if it is not a 'z' case
560            if not stall:
561                state = "%s_State_%s" % (self.ident, trans.state.ident)
562                event = "%s_Event_%s" % (self.ident, trans.event.ident)
563                code('m_profiler.possibleTransition($state, $event);')
564
565        # added by SS to initialize recycle_latency of message buffers
566        for buf in self.message_buffer_names:
567            code("$buf->setRecycleLatency(m_recycle_latency);")
568
569        code.dedent()
570        code('}')
571
572        has_mandatory_q = False
573        for port in self.in_ports:
574            if port.code.find("mandatoryQueue_ptr") >= 0:
575                has_mandatory_q = True
576
577        if has_mandatory_q:
578            mq_ident = "m_%s_mandatoryQueue_ptr" % self.ident
579        else:
580            mq_ident = "NULL"
581
582        code('''
583int
584$c_ident::getNumControllers()
585{
586    return m_num_controllers;
587}
588
589MessageBuffer*
590$c_ident::getMandatoryQueue() const
591{
592    return $mq_ident;
593}
594
595const int &
596$c_ident::getVersion() const
597{
598    return m_version;
599}
600
601const string
602$c_ident::toString() const
603{
604    return "$c_ident";
605}
606
607const string
608$c_ident::getName() const
609{
610    return m_name;
611}
612
613const MachineType
614$c_ident::getMachineType() const
615{
616    return MachineType_${ident};
617}
618
619void
620$c_ident::blockOnQueue(Address addr, MessageBuffer* port)
621{
622    m_is_blocking = true;
623    m_block_map[addr] = port;
624}
625
626void
627$c_ident::unblock(Address addr)
628{
629    m_block_map.erase(addr);
630    if (m_block_map.size() == 0) {
631       m_is_blocking = false;
632    }
633}
634
635void
636$c_ident::print(ostream& out) const
637{
638    out << "[$c_ident " << m_version << "]";
639}
640
641void
642$c_ident::printConfig(ostream& out) const
643{
644    out << "$c_ident config: " << m_name << endl;
645    out << "  version: " << m_version << endl;
646    map<string, string>::const_iterator it;
647    for (it = m_cfg.begin(); it != m_cfg.end(); it++)
648        out << "  " << it->first << ": " << it->second << endl;
649}
650
651void
652$c_ident::printStats(ostream& out) const
653{
654''')
655        #
656        # Cache and Memory Controllers have specific profilers associated with
657        # them.  Print out these stats before dumping state transition stats.
658        #
659        for param in self.config_parameters:
660            if param.type_ast.type.ident == "CacheMemory" or \
661               param.type_ast.type.ident == "DirectoryMemory" or \
662                   param.type_ast.type.ident == "MemoryControl":
663                assert(param.pointer)
664                code('    m_${{param.ident}}_ptr->printStats(out);')
665
666        code('''
667    if (m_version == 0) {
668        s_profileDumper.dumpStats(out);
669    }
670}
671
672void $c_ident::clearStats() {
673''')
674        #
675        # Cache and Memory Controllers have specific profilers associated with
676        # them.  These stats must be cleared too.
677        #
678        for param in self.config_parameters:
679            if param.type_ast.type.ident == "CacheMemory" or \
680                   param.type_ast.type.ident == "MemoryControl":
681                assert(param.pointer)
682                code('    m_${{param.ident}}_ptr->clearStats();')
683
684        code('''
685    m_profiler.clearStats();
686}
687
688// Actions
689''')
690
691        for action in self.actions.itervalues():
692            if "c_code" not in action:
693                continue
694
695            code('''
696/** \\brief ${{action.desc}} */
697void
698$c_ident::${{action.ident}}(const Address& addr)
699{
700    DEBUG_MSG(GENERATED_COMP, HighPrio, "executing");
701    ${{action["c_code"]}}
702}
703
704''')
705        code.write(path, "%s.cc" % c_ident)
706
707    def printCWakeup(self, path):
708        '''Output the wakeup loop for the events'''
709
710        code = self.symtab.codeFormatter()
711        ident = self.ident
712
713        code('''
714// Auto generated C++ code started by $__file__:$__line__
715// ${ident}: ${{self.short}}
716
717#include "base/misc.hh"
718#include "mem/ruby/common/Global.hh"
719#include "mem/ruby/slicc_interface/RubySlicc_includes.hh"
720#include "mem/protocol/${ident}_Controller.hh"
721#include "mem/protocol/${ident}_State.hh"
722#include "mem/protocol/${ident}_Event.hh"
723#include "mem/protocol/Types.hh"
724#include "mem/ruby/system/System.hh"
725
726using namespace std;
727
728void
729${ident}_Controller::wakeup()
730{
731    // DEBUG_EXPR(GENERATED_COMP, MedPrio, *this);
732    // DEBUG_EXPR(GENERATED_COMP, MedPrio, g_eventQueue_ptr->getTime());
733
734    int counter = 0;
735    while (true) {
736        // Some cases will put us into an infinite loop without this limit
737        assert(counter <= m_transitions_per_cycle);
738        if (counter == m_transitions_per_cycle) {
739            // Count how often we are fully utilized
740            g_system_ptr->getProfiler()->controllerBusy(m_machineID);
741
742            // Wakeup in another cycle and try again
743            g_eventQueue_ptr->scheduleEvent(this, 1);
744            break;
745        }
746''')
747
748        code.indent()
749        code.indent()
750
751        # InPorts
752        #
753        for port in self.in_ports:
754            code.indent()
755            code('// ${ident}InPort $port')
756            code('${{port["c_code_in_port"]}}')
757            code.dedent()
758
759            code('')
760
761        code.dedent()
762        code.dedent()
763        code('''
764        break;  // If we got this far, we have nothing left todo
765    }
766    // g_eventQueue_ptr->scheduleEvent(this, 1);
767    // DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
768}
769''')
770
771        code.write(path, "%s_Wakeup.cc" % self.ident)
772
773    def printCSwitch(self, path):
774        '''Output switch statement for transition table'''
775
776        code = self.symtab.codeFormatter()
777        ident = self.ident
778
779        code('''
780// Auto generated C++ code started by $__file__:$__line__
781// ${ident}: ${{self.short}}
782
783#include "mem/ruby/common/Global.hh"
784#include "mem/protocol/${ident}_Controller.hh"
785#include "mem/protocol/${ident}_State.hh"
786#include "mem/protocol/${ident}_Event.hh"
787#include "mem/protocol/Types.hh"
788#include "mem/ruby/system/System.hh"
789
790#define HASH_FUN(state, event)  ((int(state)*${ident}_Event_NUM)+int(event))
791
792#define GET_TRANSITION_COMMENT() (${ident}_transitionComment.str())
793#define CLEAR_TRANSITION_COMMENT() (${ident}_transitionComment.str(""))
794
795TransitionResult
796${ident}_Controller::doTransition(${ident}_Event event,
797                                  ${ident}_State state,
798                                  const Address &addr)
799{
800    ${ident}_State next_state = state;
801
802    DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
803    DEBUG_MSG(GENERATED_COMP, MedPrio, *this);
804    DEBUG_EXPR(GENERATED_COMP, MedPrio, g_eventQueue_ptr->getTime());
805    DEBUG_EXPR(GENERATED_COMP, MedPrio,state);
806    DEBUG_EXPR(GENERATED_COMP, MedPrio,event);
807    DEBUG_EXPR(GENERATED_COMP, MedPrio,addr);
808
809    TransitionResult result =
810        doTransitionWorker(event, state, next_state, addr);
811
812    if (result == TransitionResult_Valid) {
813        DEBUG_EXPR(GENERATED_COMP, MedPrio, next_state);
814        DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
815        m_profiler.countTransition(state, event);
816        if (Debug::getProtocolTrace()) {
817            g_system_ptr->getProfiler()->profileTransition("${ident}",
818                    m_version, addr,
819                    ${ident}_State_to_string(state),
820                    ${ident}_Event_to_string(event),
821                    ${ident}_State_to_string(next_state),
822                    GET_TRANSITION_COMMENT());
823        }
824    CLEAR_TRANSITION_COMMENT();
825    ${ident}_setState(addr, next_state);
826
827    } else if (result == TransitionResult_ResourceStall) {
828        if (Debug::getProtocolTrace()) {
829            g_system_ptr->getProfiler()->profileTransition("${ident}",
830                   m_version, addr,
831                   ${ident}_State_to_string(state),
832                   ${ident}_Event_to_string(event),
833                   ${ident}_State_to_string(next_state),
834                   "Resource Stall");
835        }
836    } else if (result == TransitionResult_ProtocolStall) {
837        DEBUG_MSG(GENERATED_COMP, HighPrio, "stalling");
838        DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
839        if (Debug::getProtocolTrace()) {
840            g_system_ptr->getProfiler()->profileTransition("${ident}",
841                   m_version, addr,
842                   ${ident}_State_to_string(state),
843                   ${ident}_Event_to_string(event),
844                   ${ident}_State_to_string(next_state),
845                   "Protocol Stall");
846        }
847    }
848
849    return result;
850}
851
852TransitionResult
853${ident}_Controller::doTransitionWorker(${ident}_Event event,
854                                        ${ident}_State state,
855                                        ${ident}_State& next_state,
856                                        const Address& addr)
857{
858    switch(HASH_FUN(state, event)) {
859''')
860
861        # This map will allow suppress generating duplicate code
862        cases = orderdict()
863
864        for trans in self.transitions:
865            case_string = "%s_State_%s, %s_Event_%s" % \
866                (self.ident, trans.state.ident, self.ident, trans.event.ident)
867
868            case = self.symtab.codeFormatter()
869            # Only set next_state if it changes
870            if trans.state != trans.nextState:
871                ns_ident = trans.nextState.ident
872                case('next_state = ${ident}_State_${ns_ident};')
873
874            actions = trans.actions
875
876            # Check for resources
877            case_sorter = []
878            res = trans.resources
879            for key,val in res.iteritems():
880                if key.type.ident != "DNUCAStopTable":
881                    val = '''
882if (!%s.areNSlotsAvailable(%s))
883    return TransitionResult_ResourceStall;
884''' % (key.code, val)
885                case_sorter.append(val)
886
887
888            # Emit the code sequences in a sorted order.  This makes the
889            # output deterministic (without this the output order can vary
890            # since Map's keys() on a vector of pointers is not deterministic
891            for c in sorted(case_sorter):
892                case("$c")
893
894            # Figure out if we stall
895            stall = False
896            for action in actions:
897                if action.ident == "z_stall":
898                    stall = True
899                    break
900
901            if stall:
902                case('return TransitionResult_ProtocolStall;')
903            else:
904                for action in actions:
905                    case('${{action.ident}}(addr);')
906                case('return TransitionResult_Valid;')
907
908            case = str(case)
909
910            # Look to see if this transition code is unique.
911            if case not in cases:
912                cases[case] = []
913
914            cases[case].append(case_string)
915
916        # Walk through all of the unique code blocks and spit out the
917        # corresponding case statement elements
918        for case,transitions in cases.iteritems():
919            # Iterative over all the multiple transitions that share
920            # the same code
921            for trans in transitions:
922                code('  case HASH_FUN($trans):')
923            code('    $case')
924
925        code('''
926      default:
927        WARN_EXPR(m_version);
928        WARN_EXPR(g_eventQueue_ptr->getTime());
929        WARN_EXPR(addr);
930        WARN_EXPR(event);
931        WARN_EXPR(state);
932        ERROR_MSG(\"Invalid transition\");
933    }
934    return TransitionResult_Valid;
935}
936''')
937        code.write(path, "%s_Transitions.cc" % self.ident)
938
939    def printProfileDumperHH(self, path):
940        code = self.symtab.codeFormatter()
941        ident = self.ident
942
943        code('''
944// Auto generated C++ code started by $__file__:$__line__
945// ${ident}: ${{self.short}}
946
947#ifndef __${ident}_PROFILE_DUMPER_HH__
948#define __${ident}_PROFILE_DUMPER_HH__
949
950#include <iostream>
951#include <vector>
952
953#include "${ident}_Profiler.hh"
954#include "${ident}_Event.hh"
955
956typedef std::vector<${ident}_Profiler *> ${ident}_profilers;
957
958class ${ident}_ProfileDumper
959{
960  public:
961    ${ident}_ProfileDumper();
962    void registerProfiler(${ident}_Profiler* profiler);
963    void dumpStats(std::ostream& out) const;
964
965  private:
966    ${ident}_profilers m_profilers;
967};
968
969#endif // __${ident}_PROFILE_DUMPER_HH__
970''')
971        code.write(path, "%s_ProfileDumper.hh" % self.ident)
972
973    def printProfileDumperCC(self, path):
974        code = self.symtab.codeFormatter()
975        ident = self.ident
976
977        code('''
978// Auto generated C++ code started by $__file__:$__line__
979// ${ident}: ${{self.short}}
980
981#include "mem/protocol/${ident}_ProfileDumper.hh"
982
983${ident}_ProfileDumper::${ident}_ProfileDumper()
984{
985}
986
987void
988${ident}_ProfileDumper::registerProfiler(${ident}_Profiler* profiler)
989{
990    m_profilers.push_back(profiler);
991}
992
993void
994${ident}_ProfileDumper::dumpStats(std::ostream& out) const
995{
996    out << " --- ${ident} ---\\n";
997    out << " - Event Counts -\\n";
998    for (${ident}_Event event = ${ident}_Event_FIRST;
999         event < ${ident}_Event_NUM;
1000         ++event) {
1001        out << (${ident}_Event) event << " [";
1002        uint64 total = 0;
1003        for (int i = 0; i < m_profilers.size(); i++) {
1004             out << m_profilers[i]->getEventCount(event) << " ";
1005             total += m_profilers[i]->getEventCount(event);
1006        }
1007        out << "] " << total << "\\n";
1008    }
1009    out << "\\n";
1010    out << " - Transitions -\\n";
1011    for (${ident}_State state = ${ident}_State_FIRST;
1012         state < ${ident}_State_NUM;
1013         ++state) {
1014        for (${ident}_Event event = ${ident}_Event_FIRST;
1015             event < ${ident}_Event_NUM;
1016             ++event) {
1017            if (m_profilers[0]->isPossible(state, event)) {
1018                out << (${ident}_State) state << "  "
1019                    << (${ident}_Event) event << " [";
1020                uint64 total = 0;
1021                for (int i = 0; i < m_profilers.size(); i++) {
1022                     out << m_profilers[i]->getTransitionCount(state, event) << " ";
1023                     total += m_profilers[i]->getTransitionCount(state, event);
1024                }
1025                out << "] " << total << "\\n";
1026            }
1027        }
1028        out << "\\n";
1029    }
1030}
1031''')
1032        code.write(path, "%s_ProfileDumper.cc" % self.ident)
1033
1034    def printProfilerHH(self, path):
1035        code = self.symtab.codeFormatter()
1036        ident = self.ident
1037
1038        code('''
1039// Auto generated C++ code started by $__file__:$__line__
1040// ${ident}: ${{self.short}}
1041
1042#ifndef __${ident}_PROFILER_HH__
1043#define __${ident}_PROFILER_HH__
1044
1045#include <iostream>
1046
1047#include "mem/ruby/common/Global.hh"
1048#include "mem/protocol/${ident}_State.hh"
1049#include "mem/protocol/${ident}_Event.hh"
1050
1051class ${ident}_Profiler
1052{
1053  public:
1054    ${ident}_Profiler();
1055    void setVersion(int version);
1056    void countTransition(${ident}_State state, ${ident}_Event event);
1057    void possibleTransition(${ident}_State state, ${ident}_Event event);
1058    uint64 getEventCount(${ident}_Event event);
1059    bool isPossible(${ident}_State state, ${ident}_Event event);
1060    uint64 getTransitionCount(${ident}_State state, ${ident}_Event event);
1061    void clearStats();
1062
1063  private:
1064    int m_counters[${ident}_State_NUM][${ident}_Event_NUM];
1065    int m_event_counters[${ident}_Event_NUM];
1066    bool m_possible[${ident}_State_NUM][${ident}_Event_NUM];
1067    int m_version;
1068};
1069
1070#endif // __${ident}_PROFILER_HH__
1071''')
1072        code.write(path, "%s_Profiler.hh" % self.ident)
1073
1074    def printProfilerCC(self, path):
1075        code = self.symtab.codeFormatter()
1076        ident = self.ident
1077
1078        code('''
1079// Auto generated C++ code started by $__file__:$__line__
1080// ${ident}: ${{self.short}}
1081
1082#include "mem/protocol/${ident}_Profiler.hh"
1083
1084${ident}_Profiler::${ident}_Profiler()
1085{
1086    for (int state = 0; state < ${ident}_State_NUM; state++) {
1087        for (int event = 0; event < ${ident}_Event_NUM; event++) {
1088            m_possible[state][event] = false;
1089            m_counters[state][event] = 0;
1090        }
1091    }
1092    for (int event = 0; event < ${ident}_Event_NUM; event++) {
1093        m_event_counters[event] = 0;
1094    }
1095}
1096
1097void
1098${ident}_Profiler::setVersion(int version)
1099{
1100    m_version = version;
1101}
1102
1103void
1104${ident}_Profiler::clearStats()
1105{
1106    for (int state = 0; state < ${ident}_State_NUM; state++) {
1107        for (int event = 0; event < ${ident}_Event_NUM; event++) {
1108            m_counters[state][event] = 0;
1109        }
1110    }
1111
1112    for (int event = 0; event < ${ident}_Event_NUM; event++) {
1113        m_event_counters[event] = 0;
1114    }
1115}
1116void
1117${ident}_Profiler::countTransition(${ident}_State state, ${ident}_Event event)
1118{
1119    assert(m_possible[state][event]);
1120    m_counters[state][event]++;
1121    m_event_counters[event]++;
1122}
1123void
1124${ident}_Profiler::possibleTransition(${ident}_State state,
1125                                      ${ident}_Event event)
1126{
1127    m_possible[state][event] = true;
1128}
1129
1130uint64
1131${ident}_Profiler::getEventCount(${ident}_Event event)
1132{
1133    return m_event_counters[event];
1134}
1135
1136bool
1137${ident}_Profiler::isPossible(${ident}_State state, ${ident}_Event event)
1138{
1139    return m_possible[state][event];
1140}
1141
1142uint64
1143${ident}_Profiler::getTransitionCount(${ident}_State state,
1144                                      ${ident}_Event event)
1145{
1146    return m_counters[state][event];
1147}
1148
1149''')
1150        code.write(path, "%s_Profiler.cc" % self.ident)
1151
1152    # **************************
1153    # ******* HTML Files *******
1154    # **************************
1155    def frameRef(self, click_href, click_target, over_href, over_num, text):
1156        code = self.symtab.codeFormatter(fix_newlines=False)
1157        code("""<A href=\"$click_href\" target=\"$click_target\" onmouseover=\"
1158    if (parent.frames[$over_num].location != parent.location + '$over_href') {
1159        parent.frames[$over_num].location='$over_href'
1160    }\">
1161    ${{html.formatShorthand(text)}}
1162    </A>""")
1163        return str(code)
1164
1165    def writeHTMLFiles(self, path):
1166        # Create table with no row hilighted
1167        self.printHTMLTransitions(path, None)
1168
1169        # Generate transition tables
1170        for state in self.states.itervalues():
1171            self.printHTMLTransitions(path, state)
1172
1173        # Generate action descriptions
1174        for action in self.actions.itervalues():
1175            name = "%s_action_%s.html" % (self.ident, action.ident)
1176            code = html.createSymbol(action, "Action")
1177            code.write(path, name)
1178
1179        # Generate state descriptions
1180        for state in self.states.itervalues():
1181            name = "%s_State_%s.html" % (self.ident, state.ident)
1182            code = html.createSymbol(state, "State")
1183            code.write(path, name)
1184
1185        # Generate event descriptions
1186        for event in self.events.itervalues():
1187            name = "%s_Event_%s.html" % (self.ident, event.ident)
1188            code = html.createSymbol(event, "Event")
1189            code.write(path, name)
1190
1191    def printHTMLTransitions(self, path, active_state):
1192        code = self.symtab.codeFormatter()
1193
1194        code('''
1195<HTML>
1196<BODY link="blue" vlink="blue">
1197
1198<H1 align="center">${{html.formatShorthand(self.short)}}:
1199''')
1200        code.indent()
1201        for i,machine in enumerate(self.symtab.getAllType(StateMachine)):
1202            mid = machine.ident
1203            if i != 0:
1204                extra = " - "
1205            else:
1206                extra = ""
1207            if machine == self:
1208                code('$extra$mid')
1209            else:
1210                code('$extra<A target="Table" href="${mid}_table.html">$mid</A>')
1211        code.dedent()
1212
1213        code("""
1214</H1>
1215
1216<TABLE border=1>
1217<TR>
1218  <TH> </TH>
1219""")
1220
1221        for event in self.events.itervalues():
1222            href = "%s_Event_%s.html" % (self.ident, event.ident)
1223            ref = self.frameRef(href, "Status", href, "1", event.short)
1224            code('<TH bgcolor=white>$ref</TH>')
1225
1226        code('</TR>')
1227        # -- Body of table
1228        for state in self.states.itervalues():
1229            # -- Each row
1230            if state == active_state:
1231                color = "yellow"
1232            else:
1233                color = "white"
1234
1235            click = "%s_table_%s.html" % (self.ident, state.ident)
1236            over = "%s_State_%s.html" % (self.ident, state.ident)
1237            text = html.formatShorthand(state.short)
1238            ref = self.frameRef(click, "Table", over, "1", state.short)
1239            code('''
1240<TR>
1241  <TH bgcolor=$color>$ref</TH>
1242''')
1243
1244            # -- One column for each event
1245            for event in self.events.itervalues():
1246                trans = self.table.get((state,event), None)
1247                if trans is None:
1248                    # This is the no transition case
1249                    if state == active_state:
1250                        color = "#C0C000"
1251                    else:
1252                        color = "lightgrey"
1253
1254                    code('<TD bgcolor=$color>&nbsp;</TD>')
1255                    continue
1256
1257                next = trans.nextState
1258                stall_action = False
1259
1260                # -- Get the actions
1261                for action in trans.actions:
1262                    if action.ident == "z_stall" or \
1263                       action.ident == "zz_recycleMandatoryQueue":
1264                        stall_action = True
1265
1266                # -- Print out "actions/next-state"
1267                if stall_action:
1268                    if state == active_state:
1269                        color = "#C0C000"
1270                    else:
1271                        color = "lightgrey"
1272
1273                elif active_state and next.ident == active_state.ident:
1274                    color = "aqua"
1275                elif state == active_state:
1276                    color = "yellow"
1277                else:
1278                    color = "white"
1279
1280                code('<TD bgcolor=$color>')
1281                for action in trans.actions:
1282                    href = "%s_action_%s.html" % (self.ident, action.ident)
1283                    ref = self.frameRef(href, "Status", href, "1",
1284                                        action.short)
1285                    code('  $ref')
1286                if next != state:
1287                    if trans.actions:
1288                        code('/')
1289                    click = "%s_table_%s.html" % (self.ident, next.ident)
1290                    over = "%s_State_%s.html" % (self.ident, next.ident)
1291                    ref = self.frameRef(click, "Table", over, "1", next.short)
1292                    code("$ref")
1293                code("</TD>")
1294
1295            # -- Each row
1296            if state == active_state:
1297                color = "yellow"
1298            else:
1299                color = "white"
1300
1301            click = "%s_table_%s.html" % (self.ident, state.ident)
1302            over = "%s_State_%s.html" % (self.ident, state.ident)
1303            ref = self.frameRef(click, "Table", over, "1", state.short)
1304            code('''
1305  <TH bgcolor=$color>$ref</TH>
1306</TR>
1307''')
1308        code('''
1309<!- Column footer->
1310<TR>
1311  <TH> </TH>
1312''')
1313
1314        for event in self.events.itervalues():
1315            href = "%s_Event_%s.html" % (self.ident, event.ident)
1316            ref = self.frameRef(href, "Status", href, "1", event.short)
1317            code('<TH bgcolor=white>$ref</TH>')
1318        code('''
1319</TR>
1320</TABLE>
1321</BODY></HTML>
1322''')
1323
1324
1325        if active_state:
1326            name = "%s_table_%s.html" % (self.ident, active_state.ident)
1327        else:
1328            name = "%s_table.html" % self.ident
1329        code.write(path, name)
1330
1331__all__ = [ "StateMachine" ]
1332