StateMachine.py revision 7672
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    DEBUG_MSG(GENERATED_COMP, HighPrio, "executing");
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    // DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
818}
819''')
820
821        code.write(path, "%s_Wakeup.cc" % self.ident)
822
823    def printCSwitch(self, path):
824        '''Output switch statement for transition table'''
825
826        code = self.symtab.codeFormatter()
827        ident = self.ident
828
829        code('''
830// Auto generated C++ code started by $__file__:$__line__
831// ${ident}: ${{self.short}}
832
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    DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
853    DEBUG_MSG(GENERATED_COMP, MedPrio, *this);
854    DEBUG_EXPR(GENERATED_COMP, MedPrio, g_eventQueue_ptr->getTime());
855    DEBUG_EXPR(GENERATED_COMP, MedPrio,state);
856    DEBUG_EXPR(GENERATED_COMP, MedPrio,event);
857    DEBUG_EXPR(GENERATED_COMP, MedPrio,addr);
858
859    TransitionResult result =
860        doTransitionWorker(event, state, next_state, addr);
861
862    if (result == TransitionResult_Valid) {
863        DEBUG_EXPR(GENERATED_COMP, MedPrio, next_state);
864        DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
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        DEBUG_MSG(GENERATED_COMP, HighPrio, "stalling");
888        DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
889        if (Debug::getProtocolTrace()) {
890            g_system_ptr->getProfiler()->profileTransition("${ident}",
891                   m_version, addr,
892                   ${ident}_State_to_string(state),
893                   ${ident}_Event_to_string(event),
894                   ${ident}_State_to_string(next_state),
895                   "Protocol Stall");
896        }
897    }
898
899    return result;
900}
901
902TransitionResult
903${ident}_Controller::doTransitionWorker(${ident}_Event event,
904                                        ${ident}_State state,
905                                        ${ident}_State& next_state,
906                                        const Address& addr)
907{
908    switch(HASH_FUN(state, event)) {
909''')
910
911        # This map will allow suppress generating duplicate code
912        cases = orderdict()
913
914        for trans in self.transitions:
915            case_string = "%s_State_%s, %s_Event_%s" % \
916                (self.ident, trans.state.ident, self.ident, trans.event.ident)
917
918            case = self.symtab.codeFormatter()
919            # Only set next_state if it changes
920            if trans.state != trans.nextState:
921                ns_ident = trans.nextState.ident
922                case('next_state = ${ident}_State_${ns_ident};')
923
924            actions = trans.actions
925
926            # Check for resources
927            case_sorter = []
928            res = trans.resources
929            for key,val in res.iteritems():
930                if key.type.ident != "DNUCAStopTable":
931                    val = '''
932if (!%s.areNSlotsAvailable(%s))
933    return TransitionResult_ResourceStall;
934''' % (key.code, val)
935                case_sorter.append(val)
936
937
938            # Emit the code sequences in a sorted order.  This makes the
939            # output deterministic (without this the output order can vary
940            # since Map's keys() on a vector of pointers is not deterministic
941            for c in sorted(case_sorter):
942                case("$c")
943
944            # Figure out if we stall
945            stall = False
946            for action in actions:
947                if action.ident == "z_stall":
948                    stall = True
949                    break
950
951            if stall:
952                case('return TransitionResult_ProtocolStall;')
953            else:
954                for action in actions:
955                    case('${{action.ident}}(addr);')
956                case('return TransitionResult_Valid;')
957
958            case = str(case)
959
960            # Look to see if this transition code is unique.
961            if case not in cases:
962                cases[case] = []
963
964            cases[case].append(case_string)
965
966        # Walk through all of the unique code blocks and spit out the
967        # corresponding case statement elements
968        for case,transitions in cases.iteritems():
969            # Iterative over all the multiple transitions that share
970            # the same code
971            for trans in transitions:
972                code('  case HASH_FUN($trans):')
973            code('    $case')
974
975        code('''
976      default:
977        WARN_EXPR(m_version);
978        WARN_EXPR(g_eventQueue_ptr->getTime());
979        WARN_EXPR(addr);
980        WARN_EXPR(event);
981        WARN_EXPR(state);
982        ERROR_MSG(\"Invalid transition\");
983    }
984    return TransitionResult_Valid;
985}
986''')
987        code.write(path, "%s_Transitions.cc" % self.ident)
988
989    def printProfileDumperHH(self, path):
990        code = self.symtab.codeFormatter()
991        ident = self.ident
992
993        code('''
994// Auto generated C++ code started by $__file__:$__line__
995// ${ident}: ${{self.short}}
996
997#ifndef __${ident}_PROFILE_DUMPER_HH__
998#define __${ident}_PROFILE_DUMPER_HH__
999
1000#include <iostream>
1001#include <vector>
1002
1003#include "${ident}_Profiler.hh"
1004#include "${ident}_Event.hh"
1005
1006typedef std::vector<${ident}_Profiler *> ${ident}_profilers;
1007
1008class ${ident}_ProfileDumper
1009{
1010  public:
1011    ${ident}_ProfileDumper();
1012    void registerProfiler(${ident}_Profiler* profiler);
1013    void dumpStats(std::ostream& out) const;
1014
1015  private:
1016    ${ident}_profilers m_profilers;
1017};
1018
1019#endif // __${ident}_PROFILE_DUMPER_HH__
1020''')
1021        code.write(path, "%s_ProfileDumper.hh" % self.ident)
1022
1023    def printProfileDumperCC(self, path):
1024        code = self.symtab.codeFormatter()
1025        ident = self.ident
1026
1027        code('''
1028// Auto generated C++ code started by $__file__:$__line__
1029// ${ident}: ${{self.short}}
1030
1031#include "mem/protocol/${ident}_ProfileDumper.hh"
1032
1033${ident}_ProfileDumper::${ident}_ProfileDumper()
1034{
1035}
1036
1037void
1038${ident}_ProfileDumper::registerProfiler(${ident}_Profiler* profiler)
1039{
1040    m_profilers.push_back(profiler);
1041}
1042
1043void
1044${ident}_ProfileDumper::dumpStats(std::ostream& out) const
1045{
1046    out << " --- ${ident} ---\\n";
1047    out << " - Event Counts -\\n";
1048    for (${ident}_Event event = ${ident}_Event_FIRST;
1049         event < ${ident}_Event_NUM;
1050         ++event) {
1051        out << (${ident}_Event) event << " [";
1052        uint64 total = 0;
1053        for (int i = 0; i < m_profilers.size(); i++) {
1054             out << m_profilers[i]->getEventCount(event) << " ";
1055             total += m_profilers[i]->getEventCount(event);
1056        }
1057        out << "] " << total << "\\n";
1058    }
1059    out << "\\n";
1060    out << " - Transitions -\\n";
1061    for (${ident}_State state = ${ident}_State_FIRST;
1062         state < ${ident}_State_NUM;
1063         ++state) {
1064        for (${ident}_Event event = ${ident}_Event_FIRST;
1065             event < ${ident}_Event_NUM;
1066             ++event) {
1067            if (m_profilers[0]->isPossible(state, event)) {
1068                out << (${ident}_State) state << "  "
1069                    << (${ident}_Event) event << " [";
1070                uint64 total = 0;
1071                for (int i = 0; i < m_profilers.size(); i++) {
1072                     out << m_profilers[i]->getTransitionCount(state, event) << " ";
1073                     total += m_profilers[i]->getTransitionCount(state, event);
1074                }
1075                out << "] " << total << "\\n";
1076            }
1077        }
1078        out << "\\n";
1079    }
1080}
1081''')
1082        code.write(path, "%s_ProfileDumper.cc" % self.ident)
1083
1084    def printProfilerHH(self, path):
1085        code = self.symtab.codeFormatter()
1086        ident = self.ident
1087
1088        code('''
1089// Auto generated C++ code started by $__file__:$__line__
1090// ${ident}: ${{self.short}}
1091
1092#ifndef __${ident}_PROFILER_HH__
1093#define __${ident}_PROFILER_HH__
1094
1095#include <iostream>
1096
1097#include "mem/ruby/common/Global.hh"
1098#include "mem/protocol/${ident}_State.hh"
1099#include "mem/protocol/${ident}_Event.hh"
1100
1101class ${ident}_Profiler
1102{
1103  public:
1104    ${ident}_Profiler();
1105    void setVersion(int version);
1106    void countTransition(${ident}_State state, ${ident}_Event event);
1107    void possibleTransition(${ident}_State state, ${ident}_Event event);
1108    uint64 getEventCount(${ident}_Event event);
1109    bool isPossible(${ident}_State state, ${ident}_Event event);
1110    uint64 getTransitionCount(${ident}_State state, ${ident}_Event event);
1111    void clearStats();
1112
1113  private:
1114    int m_counters[${ident}_State_NUM][${ident}_Event_NUM];
1115    int m_event_counters[${ident}_Event_NUM];
1116    bool m_possible[${ident}_State_NUM][${ident}_Event_NUM];
1117    int m_version;
1118};
1119
1120#endif // __${ident}_PROFILER_HH__
1121''')
1122        code.write(path, "%s_Profiler.hh" % self.ident)
1123
1124    def printProfilerCC(self, path):
1125        code = self.symtab.codeFormatter()
1126        ident = self.ident
1127
1128        code('''
1129// Auto generated C++ code started by $__file__:$__line__
1130// ${ident}: ${{self.short}}
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