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