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