StateMachine.py revision 7805
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 "base/misc.hh"
833#include "mem/ruby/common/Global.hh"
834#include "mem/protocol/${ident}_Controller.hh"
835#include "mem/protocol/${ident}_State.hh"
836#include "mem/protocol/${ident}_Event.hh"
837#include "mem/protocol/Types.hh"
838#include "mem/ruby/system/System.hh"
839
840#define HASH_FUN(state, event)  ((int(state)*${ident}_Event_NUM)+int(event))
841
842#define GET_TRANSITION_COMMENT() (${ident}_transitionComment.str())
843#define CLEAR_TRANSITION_COMMENT() (${ident}_transitionComment.str(""))
844
845TransitionResult
846${ident}_Controller::doTransition(${ident}_Event event,
847                                  ${ident}_State state,
848                                  const Address &addr)
849{
850    ${ident}_State next_state = state;
851
852    DPRINTF(RubyGenerated, "%s, Time: %lld, state: %s, event: %s, addr: %s\\n",
853            *this,
854            g_eventQueue_ptr->getTime(),
855            ${ident}_State_to_string(state),
856            ${ident}_Event_to_string(event),
857            addr);
858
859    TransitionResult result =
860        doTransitionWorker(event, state, next_state, addr);
861
862    if (result == TransitionResult_Valid) {
863        DPRINTF(RubyGenerated, "next_state: %s\\n",
864                ${ident}_State_to_string(next_state));
865        m_profiler.countTransition(state, event);
866        if (Debug::getProtocolTrace()) {
867            g_system_ptr->getProfiler()->profileTransition("${ident}",
868                    m_version, addr,
869                    ${ident}_State_to_string(state),
870                    ${ident}_Event_to_string(event),
871                    ${ident}_State_to_string(next_state),
872                    GET_TRANSITION_COMMENT());
873        }
874    CLEAR_TRANSITION_COMMENT();
875    ${ident}_setState(addr, next_state);
876
877    } else if (result == TransitionResult_ResourceStall) {
878        if (Debug::getProtocolTrace()) {
879            g_system_ptr->getProfiler()->profileTransition("${ident}",
880                   m_version, addr,
881                   ${ident}_State_to_string(state),
882                   ${ident}_Event_to_string(event),
883                   ${ident}_State_to_string(next_state),
884                   "Resource Stall");
885        }
886    } else if (result == TransitionResult_ProtocolStall) {
887        DPRINTF(RubyGenerated, "stalling\\n");
888        if (Debug::getProtocolTrace()) {
889            g_system_ptr->getProfiler()->profileTransition("${ident}",
890                   m_version, addr,
891                   ${ident}_State_to_string(state),
892                   ${ident}_Event_to_string(event),
893                   ${ident}_State_to_string(next_state),
894                   "Protocol Stall");
895        }
896    }
897
898    return result;
899}
900
901TransitionResult
902${ident}_Controller::doTransitionWorker(${ident}_Event event,
903                                        ${ident}_State state,
904                                        ${ident}_State& next_state,
905                                        const Address& addr)
906{
907    switch(HASH_FUN(state, event)) {
908''')
909
910        # This map will allow suppress generating duplicate code
911        cases = orderdict()
912
913        for trans in self.transitions:
914            case_string = "%s_State_%s, %s_Event_%s" % \
915                (self.ident, trans.state.ident, self.ident, trans.event.ident)
916
917            case = self.symtab.codeFormatter()
918            # Only set next_state if it changes
919            if trans.state != trans.nextState:
920                ns_ident = trans.nextState.ident
921                case('next_state = ${ident}_State_${ns_ident};')
922
923            actions = trans.actions
924
925            # Check for resources
926            case_sorter = []
927            res = trans.resources
928            for key,val in res.iteritems():
929                if key.type.ident != "DNUCAStopTable":
930                    val = '''
931if (!%s.areNSlotsAvailable(%s))
932    return TransitionResult_ResourceStall;
933''' % (key.code, val)
934                case_sorter.append(val)
935
936
937            # Emit the code sequences in a sorted order.  This makes the
938            # output deterministic (without this the output order can vary
939            # since Map's keys() on a vector of pointers is not deterministic
940            for c in sorted(case_sorter):
941                case("$c")
942
943            # Figure out if we stall
944            stall = False
945            for action in actions:
946                if action.ident == "z_stall":
947                    stall = True
948                    break
949
950            if stall:
951                case('return TransitionResult_ProtocolStall;')
952            else:
953                for action in actions:
954                    case('${{action.ident}}(addr);')
955                case('return TransitionResult_Valid;')
956
957            case = str(case)
958
959            # Look to see if this transition code is unique.
960            if case not in cases:
961                cases[case] = []
962
963            cases[case].append(case_string)
964
965        # Walk through all of the unique code blocks and spit out the
966        # corresponding case statement elements
967        for case,transitions in cases.iteritems():
968            # Iterative over all the multiple transitions that share
969            # the same code
970            for trans in transitions:
971                code('  case HASH_FUN($trans):')
972            code('    $case')
973
974        code('''
975      default:
976        fatal("Invalid transition\\n"
977              "version: %d time: %d addr: %s event: %s state: %s\\n",
978              m_version, g_eventQueue_ptr->getTime(), addr, event, state);
979    }
980    return TransitionResult_Valid;
981}
982''')
983        code.write(path, "%s_Transitions.cc" % self.ident)
984
985    def printProfileDumperHH(self, path):
986        code = self.symtab.codeFormatter()
987        ident = self.ident
988
989        code('''
990// Auto generated C++ code started by $__file__:$__line__
991// ${ident}: ${{self.short}}
992
993#ifndef __${ident}_PROFILE_DUMPER_HH__
994#define __${ident}_PROFILE_DUMPER_HH__
995
996#include <iostream>
997#include <vector>
998
999#include "${ident}_Profiler.hh"
1000#include "${ident}_Event.hh"
1001
1002typedef std::vector<${ident}_Profiler *> ${ident}_profilers;
1003
1004class ${ident}_ProfileDumper
1005{
1006  public:
1007    ${ident}_ProfileDumper();
1008    void registerProfiler(${ident}_Profiler* profiler);
1009    void dumpStats(std::ostream& out) const;
1010
1011  private:
1012    ${ident}_profilers m_profilers;
1013};
1014
1015#endif // __${ident}_PROFILE_DUMPER_HH__
1016''')
1017        code.write(path, "%s_ProfileDumper.hh" % self.ident)
1018
1019    def printProfileDumperCC(self, path):
1020        code = self.symtab.codeFormatter()
1021        ident = self.ident
1022
1023        code('''
1024// Auto generated C++ code started by $__file__:$__line__
1025// ${ident}: ${{self.short}}
1026
1027#include "mem/protocol/${ident}_ProfileDumper.hh"
1028
1029${ident}_ProfileDumper::${ident}_ProfileDumper()
1030{
1031}
1032
1033void
1034${ident}_ProfileDumper::registerProfiler(${ident}_Profiler* profiler)
1035{
1036    m_profilers.push_back(profiler);
1037}
1038
1039void
1040${ident}_ProfileDumper::dumpStats(std::ostream& out) const
1041{
1042    out << " --- ${ident} ---\\n";
1043    out << " - Event Counts -\\n";
1044    for (${ident}_Event event = ${ident}_Event_FIRST;
1045         event < ${ident}_Event_NUM;
1046         ++event) {
1047        out << (${ident}_Event) event << " [";
1048        uint64 total = 0;
1049        for (int i = 0; i < m_profilers.size(); i++) {
1050             out << m_profilers[i]->getEventCount(event) << " ";
1051             total += m_profilers[i]->getEventCount(event);
1052        }
1053        out << "] " << total << "\\n";
1054    }
1055    out << "\\n";
1056    out << " - Transitions -\\n";
1057    for (${ident}_State state = ${ident}_State_FIRST;
1058         state < ${ident}_State_NUM;
1059         ++state) {
1060        for (${ident}_Event event = ${ident}_Event_FIRST;
1061             event < ${ident}_Event_NUM;
1062             ++event) {
1063            if (m_profilers[0]->isPossible(state, event)) {
1064                out << (${ident}_State) state << "  "
1065                    << (${ident}_Event) event << " [";
1066                uint64 total = 0;
1067                for (int i = 0; i < m_profilers.size(); i++) {
1068                     out << m_profilers[i]->getTransitionCount(state, event) << " ";
1069                     total += m_profilers[i]->getTransitionCount(state, event);
1070                }
1071                out << "] " << total << "\\n";
1072            }
1073        }
1074        out << "\\n";
1075    }
1076}
1077''')
1078        code.write(path, "%s_ProfileDumper.cc" % self.ident)
1079
1080    def printProfilerHH(self, path):
1081        code = self.symtab.codeFormatter()
1082        ident = self.ident
1083
1084        code('''
1085// Auto generated C++ code started by $__file__:$__line__
1086// ${ident}: ${{self.short}}
1087
1088#ifndef __${ident}_PROFILER_HH__
1089#define __${ident}_PROFILER_HH__
1090
1091#include <iostream>
1092
1093#include "mem/ruby/common/Global.hh"
1094#include "mem/protocol/${ident}_State.hh"
1095#include "mem/protocol/${ident}_Event.hh"
1096
1097class ${ident}_Profiler
1098{
1099  public:
1100    ${ident}_Profiler();
1101    void setVersion(int version);
1102    void countTransition(${ident}_State state, ${ident}_Event event);
1103    void possibleTransition(${ident}_State state, ${ident}_Event event);
1104    uint64 getEventCount(${ident}_Event event);
1105    bool isPossible(${ident}_State state, ${ident}_Event event);
1106    uint64 getTransitionCount(${ident}_State state, ${ident}_Event event);
1107    void clearStats();
1108
1109  private:
1110    int m_counters[${ident}_State_NUM][${ident}_Event_NUM];
1111    int m_event_counters[${ident}_Event_NUM];
1112    bool m_possible[${ident}_State_NUM][${ident}_Event_NUM];
1113    int m_version;
1114};
1115
1116#endif // __${ident}_PROFILER_HH__
1117''')
1118        code.write(path, "%s_Profiler.hh" % self.ident)
1119
1120    def printProfilerCC(self, path):
1121        code = self.symtab.codeFormatter()
1122        ident = self.ident
1123
1124        code('''
1125// Auto generated C++ code started by $__file__:$__line__
1126// ${ident}: ${{self.short}}
1127
1128#include "mem/protocol/${ident}_Profiler.hh"
1129
1130${ident}_Profiler::${ident}_Profiler()
1131{
1132    for (int state = 0; state < ${ident}_State_NUM; state++) {
1133        for (int event = 0; event < ${ident}_Event_NUM; event++) {
1134            m_possible[state][event] = false;
1135            m_counters[state][event] = 0;
1136        }
1137    }
1138    for (int event = 0; event < ${ident}_Event_NUM; event++) {
1139        m_event_counters[event] = 0;
1140    }
1141}
1142
1143void
1144${ident}_Profiler::setVersion(int version)
1145{
1146    m_version = version;
1147}
1148
1149void
1150${ident}_Profiler::clearStats()
1151{
1152    for (int state = 0; state < ${ident}_State_NUM; state++) {
1153        for (int event = 0; event < ${ident}_Event_NUM; event++) {
1154            m_counters[state][event] = 0;
1155        }
1156    }
1157
1158    for (int event = 0; event < ${ident}_Event_NUM; event++) {
1159        m_event_counters[event] = 0;
1160    }
1161}
1162void
1163${ident}_Profiler::countTransition(${ident}_State state, ${ident}_Event event)
1164{
1165    assert(m_possible[state][event]);
1166    m_counters[state][event]++;
1167    m_event_counters[event]++;
1168}
1169void
1170${ident}_Profiler::possibleTransition(${ident}_State state,
1171                                      ${ident}_Event event)
1172{
1173    m_possible[state][event] = true;
1174}
1175
1176uint64
1177${ident}_Profiler::getEventCount(${ident}_Event event)
1178{
1179    return m_event_counters[event];
1180}
1181
1182bool
1183${ident}_Profiler::isPossible(${ident}_State state, ${ident}_Event event)
1184{
1185    return m_possible[state][event];
1186}
1187
1188uint64
1189${ident}_Profiler::getTransitionCount(${ident}_State state,
1190                                      ${ident}_Event event)
1191{
1192    return m_counters[state][event];
1193}
1194
1195''')
1196        code.write(path, "%s_Profiler.cc" % self.ident)
1197
1198    # **************************
1199    # ******* HTML Files *******
1200    # **************************
1201    def frameRef(self, click_href, click_target, over_href, over_num, text):
1202        code = self.symtab.codeFormatter(fix_newlines=False)
1203        code("""<A href=\"$click_href\" target=\"$click_target\" onmouseover=\"
1204    if (parent.frames[$over_num].location != parent.location + '$over_href') {
1205        parent.frames[$over_num].location='$over_href'
1206    }\">
1207    ${{html.formatShorthand(text)}}
1208    </A>""")
1209        return str(code)
1210
1211    def writeHTMLFiles(self, path):
1212        # Create table with no row hilighted
1213        self.printHTMLTransitions(path, None)
1214
1215        # Generate transition tables
1216        for state in self.states.itervalues():
1217            self.printHTMLTransitions(path, state)
1218
1219        # Generate action descriptions
1220        for action in self.actions.itervalues():
1221            name = "%s_action_%s.html" % (self.ident, action.ident)
1222            code = html.createSymbol(action, "Action")
1223            code.write(path, name)
1224
1225        # Generate state descriptions
1226        for state in self.states.itervalues():
1227            name = "%s_State_%s.html" % (self.ident, state.ident)
1228            code = html.createSymbol(state, "State")
1229            code.write(path, name)
1230
1231        # Generate event descriptions
1232        for event in self.events.itervalues():
1233            name = "%s_Event_%s.html" % (self.ident, event.ident)
1234            code = html.createSymbol(event, "Event")
1235            code.write(path, name)
1236
1237    def printHTMLTransitions(self, path, active_state):
1238        code = self.symtab.codeFormatter()
1239
1240        code('''
1241<HTML>
1242<BODY link="blue" vlink="blue">
1243
1244<H1 align="center">${{html.formatShorthand(self.short)}}:
1245''')
1246        code.indent()
1247        for i,machine in enumerate(self.symtab.getAllType(StateMachine)):
1248            mid = machine.ident
1249            if i != 0:
1250                extra = " - "
1251            else:
1252                extra = ""
1253            if machine == self:
1254                code('$extra$mid')
1255            else:
1256                code('$extra<A target="Table" href="${mid}_table.html">$mid</A>')
1257        code.dedent()
1258
1259        code("""
1260</H1>
1261
1262<TABLE border=1>
1263<TR>
1264  <TH> </TH>
1265""")
1266
1267        for event in self.events.itervalues():
1268            href = "%s_Event_%s.html" % (self.ident, event.ident)
1269            ref = self.frameRef(href, "Status", href, "1", event.short)
1270            code('<TH bgcolor=white>$ref</TH>')
1271
1272        code('</TR>')
1273        # -- Body of table
1274        for state in self.states.itervalues():
1275            # -- Each row
1276            if state == active_state:
1277                color = "yellow"
1278            else:
1279                color = "white"
1280
1281            click = "%s_table_%s.html" % (self.ident, state.ident)
1282            over = "%s_State_%s.html" % (self.ident, state.ident)
1283            text = html.formatShorthand(state.short)
1284            ref = self.frameRef(click, "Table", over, "1", state.short)
1285            code('''
1286<TR>
1287  <TH bgcolor=$color>$ref</TH>
1288''')
1289
1290            # -- One column for each event
1291            for event in self.events.itervalues():
1292                trans = self.table.get((state,event), None)
1293                if trans is None:
1294                    # This is the no transition case
1295                    if state == active_state:
1296                        color = "#C0C000"
1297                    else:
1298                        color = "lightgrey"
1299
1300                    code('<TD bgcolor=$color>&nbsp;</TD>')
1301                    continue
1302
1303                next = trans.nextState
1304                stall_action = False
1305
1306                # -- Get the actions
1307                for action in trans.actions:
1308                    if action.ident == "z_stall" or \
1309                       action.ident == "zz_recycleMandatoryQueue":
1310                        stall_action = True
1311
1312                # -- Print out "actions/next-state"
1313                if stall_action:
1314                    if state == active_state:
1315                        color = "#C0C000"
1316                    else:
1317                        color = "lightgrey"
1318
1319                elif active_state and next.ident == active_state.ident:
1320                    color = "aqua"
1321                elif state == active_state:
1322                    color = "yellow"
1323                else:
1324                    color = "white"
1325
1326                code('<TD bgcolor=$color>')
1327                for action in trans.actions:
1328                    href = "%s_action_%s.html" % (self.ident, action.ident)
1329                    ref = self.frameRef(href, "Status", href, "1",
1330                                        action.short)
1331                    code('  $ref')
1332                if next != state:
1333                    if trans.actions:
1334                        code('/')
1335                    click = "%s_table_%s.html" % (self.ident, next.ident)
1336                    over = "%s_State_%s.html" % (self.ident, next.ident)
1337                    ref = self.frameRef(click, "Table", over, "1", next.short)
1338                    code("$ref")
1339                code("</TD>")
1340
1341            # -- Each row
1342            if state == active_state:
1343                color = "yellow"
1344            else:
1345                color = "white"
1346
1347            click = "%s_table_%s.html" % (self.ident, state.ident)
1348            over = "%s_State_%s.html" % (self.ident, state.ident)
1349            ref = self.frameRef(click, "Table", over, "1", state.short)
1350            code('''
1351  <TH bgcolor=$color>$ref</TH>
1352</TR>
1353''')
1354        code('''
1355<!- Column footer->
1356<TR>
1357  <TH> </TH>
1358''')
1359
1360        for event in self.events.itervalues():
1361            href = "%s_Event_%s.html" % (self.ident, event.ident)
1362            ref = self.frameRef(href, "Status", href, "1", event.short)
1363            code('<TH bgcolor=white>$ref</TH>')
1364        code('''
1365</TR>
1366</TABLE>
1367</BODY></HTML>
1368''')
1369
1370
1371        if active_state:
1372            name = "%s_table_%s.html" % (self.ident, active_state.ident)
1373        else:
1374            name = "%s_table.html" % self.ident
1375        code.write(path, name)
1376
1377__all__ = [ "StateMachine" ]
1378