StateMachine.py revision 7566
16145Snate@binkert.org# Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
26145Snate@binkert.org# Copyright (c) 2009 The Hewlett-Packard Development Company
36145Snate@binkert.org# All rights reserved.
46145Snate@binkert.org#
56145Snate@binkert.org# Redistribution and use in source and binary forms, with or without
66145Snate@binkert.org# modification, are permitted provided that the following conditions are
76145Snate@binkert.org# met: redistributions of source code must retain the above copyright
86145Snate@binkert.org# notice, this list of conditions and the following disclaimer;
96145Snate@binkert.org# redistributions in binary form must reproduce the above copyright
106145Snate@binkert.org# notice, this list of conditions and the following disclaimer in the
116145Snate@binkert.org# documentation and/or other materials provided with the distribution;
126145Snate@binkert.org# neither the name of the copyright holders nor the names of its
136145Snate@binkert.org# contributors may be used to endorse or promote products derived from
146145Snate@binkert.org# this software without specific prior written permission.
156145Snate@binkert.org#
166145Snate@binkert.org# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
176145Snate@binkert.org# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
186145Snate@binkert.org# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
196145Snate@binkert.org# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
206145Snate@binkert.org# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
216145Snate@binkert.org# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
226145Snate@binkert.org# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
236145Snate@binkert.org# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
246145Snate@binkert.org# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
256145Snate@binkert.org# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
266145Snate@binkert.org# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
276145Snate@binkert.org
286145Snate@binkert.orgfrom m5.util import orderdict
296145Snate@binkert.org
306145Snate@binkert.orgfrom slicc.symbols.Symbol import Symbol
316145Snate@binkert.orgfrom slicc.symbols.Var import Var
326145Snate@binkert.orgimport slicc.generate.html as html
336145Snate@binkert.org
346145Snate@binkert.orgpython_class_map = {"int": "Int",
356145Snate@binkert.org                    "std::string": "String",
366145Snate@binkert.org                    "bool": "Bool",
376145Snate@binkert.org                    "CacheMemory": "RubyCache",
386145Snate@binkert.org                    "Sequencer": "RubySequencer",
396145Snate@binkert.org                    "DirectoryMemory": "RubyDirectoryMemory",
406145Snate@binkert.org                    "MemoryControl": "RubyMemoryControl",
416145Snate@binkert.org                    "DMASequencer": "DMASequencer"
426145Snate@binkert.org                    }
436145Snate@binkert.org
446145Snate@binkert.orgclass StateMachine(Symbol):
456145Snate@binkert.org    def __init__(self, symtab, ident, location, pairs, config_parameters):
46        super(StateMachine, self).__init__(symtab, ident, location, pairs)
47        self.table = None
48        self.config_parameters = config_parameters
49        for param in config_parameters:
50            if param.pointer:
51                var = Var(symtab, param.name, location, param.type_ast.type,
52                          "(*m_%s_ptr)" % param.name, {}, self)
53            else:
54                var = Var(symtab, param.name, location, param.type_ast.type,
55                          "m_%s" % param.name, {}, self)
56            self.symtab.registerSym(param.name, var)
57
58        self.states = orderdict()
59        self.events = orderdict()
60        self.actions = orderdict()
61        self.transitions = []
62        self.in_ports = []
63        self.functions = []
64        self.objects = []
65
66        self.message_buffer_names = []
67
68    def __repr__(self):
69        return "[StateMachine: %s]" % self.ident
70
71    def addState(self, state):
72        assert self.table is None
73        self.states[state.ident] = state
74
75    def addEvent(self, event):
76        assert self.table is None
77        self.events[event.ident] = event
78
79    def addAction(self, action):
80        assert self.table is None
81
82        # Check for duplicate action
83        for other in self.actions.itervalues():
84            if action.ident == other.ident:
85                action.warning("Duplicate action definition: %s" % action.ident)
86                action.error("Duplicate action definition: %s" % action.ident)
87            if action.short == other.short:
88                other.warning("Duplicate action shorthand: %s" % other.ident)
89                other.warning("    shorthand = %s" % other.short)
90                action.warning("Duplicate action shorthand: %s" % action.ident)
91                action.error("    shorthand = %s" % action.short)
92
93        self.actions[action.ident] = action
94
95    def addTransition(self, trans):
96        assert self.table is None
97        self.transitions.append(trans)
98
99    def addInPort(self, var):
100        self.in_ports.append(var)
101
102    def addFunc(self, func):
103        # register func in the symbol table
104        self.symtab.registerSym(str(func), func)
105        self.functions.append(func)
106
107    def addObject(self, obj):
108        self.objects.append(obj)
109
110    # Needs to be called before accessing the table
111    def buildTable(self):
112        assert self.table is None
113
114        table = {}
115
116        for trans in self.transitions:
117            # Track which actions we touch so we know if we use them
118            # all -- really this should be done for all symbols as
119            # part of the symbol table, then only trigger it for
120            # Actions, States, Events, etc.
121
122            for action in trans.actions:
123                action.used = True
124
125            index = (trans.state, trans.event)
126            if index in table:
127                table[index].warning("Duplicate transition: %s" % table[index])
128                trans.error("Duplicate transition: %s" % trans)
129            table[index] = trans
130
131        # Look at all actions to make sure we used them all
132        for action in self.actions.itervalues():
133            if not action.used:
134                error_msg = "Unused action: %s" % action.ident
135                if "desc" in action:
136                    error_msg += ", "  + action.desc
137                action.warning(error_msg)
138        self.table = table
139
140    def writeCodeFiles(self, path):
141        self.printControllerPython(path)
142        self.printControllerHH(path)
143        self.printControllerCC(path)
144        self.printCSwitch(path)
145        self.printCWakeup(path)
146        self.printProfilerCC(path)
147        self.printProfilerHH(path)
148        self.printProfileDumperCC(path)
149        self.printProfileDumperHH(path)
150
151        for func in self.functions:
152            func.writeCodeFiles(path)
153
154    def printControllerPython(self, path):
155        code = self.symtab.codeFormatter()
156        ident = self.ident
157        py_ident = "%s_Controller" % ident
158        c_ident = "%s_Controller" % self.ident
159        code('''
160from m5.params import *
161from m5.SimObject import SimObject
162from Controller import RubyController
163
164class $py_ident(RubyController):
165    type = '$py_ident'
166''')
167        code.indent()
168        for param in self.config_parameters:
169            dflt_str = ''
170            if param.default is not None:
171                dflt_str = str(param.default) + ', '
172            if python_class_map.has_key(param.type_ast.type.c_ident):
173                python_type = python_class_map[param.type_ast.type.c_ident]
174                code('${{param.name}} = Param.${{python_type}}(${dflt_str}"")')
175            else:
176                self.error("Unknown c++ to python class conversion for c++ " \
177                           "type: '%s'. Please update the python_class_map " \
178                           "in StateMachine.py", param.type_ast.type.c_ident)
179        code.dedent()
180        code.write(path, '%s.py' % py_ident)
181
182
183    def printControllerHH(self, path):
184        '''Output the method declarations for the class declaration'''
185        code = self.symtab.codeFormatter()
186        ident = self.ident
187        c_ident = "%s_Controller" % self.ident
188
189        self.message_buffer_names = []
190
191        code('''
192/** \\file $c_ident.hh
193 *
194 * Auto generated C++ code started by $__file__:$__line__
195 * Created by slicc definition of Module "${{self.short}}"
196 */
197
198#ifndef __${ident}_CONTROLLER_HH__
199#define __${ident}_CONTROLLER_HH__
200
201#include <iostream>
202#include <sstream>
203#include <string>
204
205#include "params/$c_ident.hh"
206
207#include "mem/ruby/common/Global.hh"
208#include "mem/ruby/common/Consumer.hh"
209#include "mem/ruby/slicc_interface/AbstractController.hh"
210#include "mem/protocol/TransitionResult.hh"
211#include "mem/protocol/Types.hh"
212#include "mem/protocol/${ident}_Profiler.hh"
213#include "mem/protocol/${ident}_ProfileDumper.hh"
214''')
215
216        seen_types = set()
217        for var in self.objects:
218            if var.type.ident not in seen_types and not var.type.isPrimitive:
219                code('#include "mem/protocol/${{var.type.c_ident}}.hh"')
220            seen_types.add(var.type.ident)
221
222        # for adding information to the protocol debug trace
223        code('''
224extern std::stringstream ${ident}_transitionComment;
225
226class $c_ident : public AbstractController
227{
228// the coherence checker needs to call isBlockExclusive() and isBlockShared()
229// making the Chip a friend class is an easy way to do this for now
230
231public:
232    typedef ${c_ident}Params Params;
233    $c_ident(const Params *p);
234    static int getNumControllers();
235    void init();
236    MessageBuffer* getMandatoryQueue() const;
237    const int & getVersion() const;
238    const std::string toString() const;
239    const std::string getName() const;
240    const MachineType getMachineType() const;
241    void initNetworkPtr(Network* net_ptr) { m_net_ptr = net_ptr; }
242    void print(std::ostream& out) const;
243    void printConfig(std::ostream& out) const;
244    void wakeup();
245    void printStats(std::ostream& out) const;
246    void clearStats();
247    void blockOnQueue(Address addr, MessageBuffer* port);
248    void unblock(Address addr);
249
250private:
251''')
252
253        code.indent()
254        # added by SS
255        for param in self.config_parameters:
256            if param.pointer:
257                code('${{param.type_ast.type}}* m_${{param.ident}}_ptr;')
258            else:
259                code('${{param.type_ast.type}} m_${{param.ident}};')
260
261        code('''
262int m_number_of_TBEs;
263
264TransitionResult doTransition(${ident}_Event event,
265                              ${ident}_State state,
266                              const Address& addr);
267
268TransitionResult doTransitionWorker(${ident}_Event event,
269                                    ${ident}_State state,
270                                    ${ident}_State& next_state,
271                                    const Address& addr);
272
273std::string m_name;
274int m_transitions_per_cycle;
275int m_buffer_size;
276int m_recycle_latency;
277std::map<std::string, std::string> m_cfg;
278NodeID m_version;
279Network* m_net_ptr;
280MachineID m_machineID;
281bool m_is_blocking;
282std::map<Address, MessageBuffer*> m_block_map;
283static ${ident}_ProfileDumper s_profileDumper;
284${ident}_Profiler m_profiler;
285static int m_num_controllers;
286
287// Internal functions
288''')
289
290        for func in self.functions:
291            proto = func.prototype
292            if proto:
293                code('$proto')
294
295        code('''
296
297// Actions
298''')
299        for action in self.actions.itervalues():
300            code('/** \\brief ${{action.desc}} */')
301            code('void ${{action.ident}}(const Address& addr);')
302
303        # the controller internal variables
304        code('''
305
306// Objects
307''')
308        for var in self.objects:
309            th = var.get("template_hack", "")
310            code('${{var.type.c_ident}}$th* m_${{var.c_ident}}_ptr;')
311
312            if var.type.ident == "MessageBuffer":
313                self.message_buffer_names.append("m_%s_ptr" % var.c_ident)
314
315        code.dedent()
316        code('};')
317        code('#endif // __${ident}_CONTROLLER_H__')
318        code.write(path, '%s.hh' % c_ident)
319
320    def printControllerCC(self, path):
321        '''Output the actions for performing the actions'''
322
323        code = self.symtab.codeFormatter()
324        ident = self.ident
325        c_ident = "%s_Controller" % self.ident
326
327        code('''
328/** \\file $c_ident.cc
329 *
330 * Auto generated C++ code started by $__file__:$__line__
331 * Created by slicc definition of Module "${{self.short}}"
332 */
333
334#include <sstream>
335#include <string>
336
337#include "base/cprintf.hh"
338#include "mem/protocol/${ident}_Controller.hh"
339#include "mem/protocol/${ident}_State.hh"
340#include "mem/protocol/${ident}_Event.hh"
341#include "mem/protocol/Types.hh"
342#include "mem/ruby/common/Global.hh"
343#include "mem/ruby/slicc_interface/RubySlicc_includes.hh"
344#include "mem/ruby/system/System.hh"
345
346using namespace std;
347''')
348
349        # include object classes
350        seen_types = set()
351        for var in self.objects:
352            if var.type.ident not in seen_types and not var.type.isPrimitive:
353                code('#include "mem/protocol/${{var.type.c_ident}}.hh"')
354            seen_types.add(var.type.ident)
355
356        code('''
357$c_ident *
358${c_ident}Params::create()
359{
360    return new $c_ident(this);
361}
362
363int $c_ident::m_num_controllers = 0;
364${ident}_ProfileDumper $c_ident::s_profileDumper;
365
366// for adding information to the protocol debug trace
367stringstream ${ident}_transitionComment;
368#define APPEND_TRANSITION_COMMENT(str) (${ident}_transitionComment << str)
369
370/** \\brief constructor */
371$c_ident::$c_ident(const Params *p)
372    : AbstractController(p)
373{
374    m_version = p->version;
375    m_transitions_per_cycle = p->transitions_per_cycle;
376    m_buffer_size = p->buffer_size;
377    m_recycle_latency = p->recycle_latency;
378    m_number_of_TBEs = p->number_of_TBEs;
379    m_is_blocking = false;
380''')
381        code.indent()
382
383        #
384        # After initializing the universal machine parameters, initialize the
385        # this machines config parameters.  Also detemine if these configuration
386        # params include a sequencer.  This information will be used later for
387        # contecting the sequencer back to the L1 cache controller.
388        #
389        contains_sequencer = False
390        for param in self.config_parameters:
391            if param.name == "sequencer" or param.name == "dma_sequencer":
392                contains_sequencer = True
393            if param.pointer:
394                code('m_${{param.name}}_ptr = p->${{param.name}};')
395            else:
396                code('m_${{param.name}} = p->${{param.name}};')
397
398        #
399        # For the l1 cache controller, add the special atomic support which
400        # includes passing the sequencer a pointer to the controller.
401        #
402        if self.ident == "L1Cache":
403            if not contains_sequencer:
404                self.error("The L1Cache controller must include the sequencer " \
405                           "configuration parameter")
406
407            code('''
408m_sequencer_ptr->setController(this);
409''')
410        #
411        # For the DMA controller, pass the sequencer a pointer to the
412        # controller.
413        #
414        if self.ident == "DMA":
415            if not contains_sequencer:
416                self.error("The DMA controller must include the sequencer " \
417                           "configuration parameter")
418
419            code('''
420m_dma_sequencer_ptr->setController(this);
421''')
422
423        code('m_num_controllers++;')
424        for var in self.objects:
425            if var.ident.find("mandatoryQueue") >= 0:
426                code('m_${{var.c_ident}}_ptr = new ${{var.type.c_ident}}();')
427
428        code.dedent()
429        code('''
430}
431
432void
433$c_ident::init()
434{
435    MachineType machine_type;
436    int base;
437
438    m_machineID.type = MachineType_${ident};
439    m_machineID.num = m_version;
440
441    // initialize objects
442    m_profiler.setVersion(m_version);
443    s_profileDumper.registerProfiler(&m_profiler);
444
445''')
446
447        code.indent()
448        for var in self.objects:
449            vtype = var.type
450            vid = "m_%s_ptr" % var.c_ident
451            if "network" not in var:
452                # Not a network port object
453                if "primitive" in vtype:
454                    code('$vid = new ${{vtype.c_ident}};')
455                    if "default" in var:
456                        code('(*$vid) = ${{var["default"]}};')
457                else:
458                    # Normal Object
459                    # added by SS
460                    if "factory" in var:
461                        code('$vid = ${{var["factory"]}};')
462                    elif var.ident.find("mandatoryQueue") < 0:
463                        th = var.get("template_hack", "")
464                        expr = "%s  = new %s%s" % (vid, vtype.c_ident, th)
465
466                        args = ""
467                        if "non_obj" not in vtype and not vtype.isEnumeration:
468                            if expr.find("TBETable") >= 0:
469                                args = "m_number_of_TBEs"
470                            else:
471                                args = var.get("constructor_hack", "")
472
473                        code('$expr($args);')
474
475                    code('assert($vid != NULL);')
476
477                    if "default" in var:
478                        code('*$vid = ${{var["default"]}}; // Object default')
479                    elif "default" in vtype:
480                        comment = "Type %s default" % vtype.ident
481                        code('*$vid = ${{vtype["default"]}}; // $comment')
482
483                    # Set ordering
484                    if "ordered" in var and "trigger_queue" not in var:
485                        # A buffer
486                        code('$vid->setOrdering(${{var["ordered"]}});')
487
488                    # Set randomization
489                    if "random" in var:
490                        # A buffer
491                        code('$vid->setRandomization(${{var["random"]}});')
492
493                    # Set Priority
494                    if vtype.isBuffer and \
495                           "rank" in var and "trigger_queue" not in var:
496                        code('$vid->setPriority(${{var["rank"]}});')
497
498            else:
499                # Network port object
500                network = var["network"]
501                ordered =  var["ordered"]
502                vnet = var["virtual_network"]
503
504                assert var.machine is not None
505                code('''
506machine_type = string_to_MachineType("${{var.machine.ident}}");
507base = MachineType_base_number(machine_type);
508$vid = m_net_ptr->get${network}NetQueue(m_version + base, $ordered, $vnet);
509''')
510
511                code('assert($vid != NULL);')
512
513                # Set ordering
514                if "ordered" in var:
515                    # A buffer
516                    code('$vid->setOrdering(${{var["ordered"]}});')
517
518                # Set randomization
519                if "random" in var:
520                    # A buffer
521                    code('$vid->setRandomization(${{var["random"]}})')
522
523                # Set Priority
524                if "rank" in var:
525                    code('$vid->setPriority(${{var["rank"]}})')
526
527                # Set buffer size
528                if vtype.isBuffer:
529                    code('''
530if (m_buffer_size > 0) {
531    $vid->resize(m_buffer_size);
532}
533''')
534
535                # set description (may be overriden later by port def)
536                code('''
537$vid->setDescription("[Version " + to_string(m_version) + ", ${ident}, name=${{var.c_ident}}]");
538
539''')
540
541            if vtype.isBuffer:
542                if "recycle_latency" in var:
543                    code('$vid->setRecycleLatency(${{var["recycle_latency"]}});')
544                else:
545                    code('$vid->setRecycleLatency(m_recycle_latency);')
546
547
548        # Set the queue consumers
549        code.insert_newline()
550        for port in self.in_ports:
551            code('${{port.code}}.setConsumer(this);')
552
553        # Set the queue descriptions
554        code.insert_newline()
555        for port in self.in_ports:
556            code('${{port.code}}.setDescription("[Version " + to_string(m_version) + ", $ident, $port]");')
557
558        # Initialize the transition profiling
559        code.insert_newline()
560        for trans in self.transitions:
561            # Figure out if we stall
562            stall = False
563            for action in trans.actions:
564                if action.ident == "z_stall":
565                    stall = True
566
567            # Only possible if it is not a 'z' case
568            if not stall:
569                state = "%s_State_%s" % (self.ident, trans.state.ident)
570                event = "%s_Event_%s" % (self.ident, trans.event.ident)
571                code('m_profiler.possibleTransition($state, $event);')
572
573        code.dedent()
574        code('}')
575
576        has_mandatory_q = False
577        for port in self.in_ports:
578            if port.code.find("mandatoryQueue_ptr") >= 0:
579                has_mandatory_q = True
580
581        if has_mandatory_q:
582            mq_ident = "m_%s_mandatoryQueue_ptr" % self.ident
583        else:
584            mq_ident = "NULL"
585
586        code('''
587int
588$c_ident::getNumControllers()
589{
590    return m_num_controllers;
591}
592
593MessageBuffer*
594$c_ident::getMandatoryQueue() const
595{
596    return $mq_ident;
597}
598
599const int &
600$c_ident::getVersion() const
601{
602    return m_version;
603}
604
605const string
606$c_ident::toString() const
607{
608    return "$c_ident";
609}
610
611const string
612$c_ident::getName() const
613{
614    return m_name;
615}
616
617const MachineType
618$c_ident::getMachineType() const
619{
620    return MachineType_${ident};
621}
622
623void
624$c_ident::blockOnQueue(Address addr, MessageBuffer* port)
625{
626    m_is_blocking = true;
627    m_block_map[addr] = port;
628}
629
630void
631$c_ident::unblock(Address addr)
632{
633    m_block_map.erase(addr);
634    if (m_block_map.size() == 0) {
635       m_is_blocking = false;
636    }
637}
638
639void
640$c_ident::print(ostream& out) const
641{
642    out << "[$c_ident " << m_version << "]";
643}
644
645void
646$c_ident::printConfig(ostream& out) const
647{
648    out << "$c_ident config: " << m_name << endl;
649    out << "  version: " << m_version << endl;
650    map<string, string>::const_iterator it;
651    for (it = m_cfg.begin(); it != m_cfg.end(); it++)
652        out << "  " << it->first << ": " << it->second << endl;
653}
654
655void
656$c_ident::printStats(ostream& out) const
657{
658''')
659        #
660        # Cache and Memory Controllers have specific profilers associated with
661        # them.  Print out these stats before dumping state transition stats.
662        #
663        for param in self.config_parameters:
664            if param.type_ast.type.ident == "CacheMemory" or \
665               param.type_ast.type.ident == "DirectoryMemory" or \
666                   param.type_ast.type.ident == "MemoryControl":
667                assert(param.pointer)
668                code('    m_${{param.ident}}_ptr->printStats(out);')
669
670        code('''
671    if (m_version == 0) {
672        s_profileDumper.dumpStats(out);
673    }
674}
675
676void $c_ident::clearStats() {
677''')
678        #
679        # Cache and Memory Controllers have specific profilers associated with
680        # them.  These stats must be cleared too.
681        #
682        for param in self.config_parameters:
683            if param.type_ast.type.ident == "CacheMemory" or \
684                   param.type_ast.type.ident == "MemoryControl":
685                assert(param.pointer)
686                code('    m_${{param.ident}}_ptr->clearStats();')
687
688        code('''
689    m_profiler.clearStats();
690}
691
692// Actions
693''')
694
695        for action in self.actions.itervalues():
696            if "c_code" not in action:
697                continue
698
699            code('''
700/** \\brief ${{action.desc}} */
701void
702$c_ident::${{action.ident}}(const Address& addr)
703{
704    DEBUG_MSG(GENERATED_COMP, HighPrio, "executing");
705    ${{action["c_code"]}}
706}
707
708''')
709        code.write(path, "%s.cc" % c_ident)
710
711    def printCWakeup(self, path):
712        '''Output the wakeup loop for the events'''
713
714        code = self.symtab.codeFormatter()
715        ident = self.ident
716
717        code('''
718// Auto generated C++ code started by $__file__:$__line__
719// ${ident}: ${{self.short}}
720
721#include "base/misc.hh"
722#include "mem/ruby/common/Global.hh"
723#include "mem/ruby/slicc_interface/RubySlicc_includes.hh"
724#include "mem/protocol/${ident}_Controller.hh"
725#include "mem/protocol/${ident}_State.hh"
726#include "mem/protocol/${ident}_Event.hh"
727#include "mem/protocol/Types.hh"
728#include "mem/ruby/system/System.hh"
729
730using namespace std;
731
732void
733${ident}_Controller::wakeup()
734{
735    // DEBUG_EXPR(GENERATED_COMP, MedPrio, *this);
736    // DEBUG_EXPR(GENERATED_COMP, MedPrio, g_eventQueue_ptr->getTime());
737
738    int counter = 0;
739    while (true) {
740        // Some cases will put us into an infinite loop without this limit
741        assert(counter <= m_transitions_per_cycle);
742        if (counter == m_transitions_per_cycle) {
743            // Count how often we are fully utilized
744            g_system_ptr->getProfiler()->controllerBusy(m_machineID);
745
746            // Wakeup in another cycle and try again
747            g_eventQueue_ptr->scheduleEvent(this, 1);
748            break;
749        }
750''')
751
752        code.indent()
753        code.indent()
754
755        # InPorts
756        #
757        for port in self.in_ports:
758            code.indent()
759            code('// ${ident}InPort $port')
760            code('${{port["c_code_in_port"]}}')
761            code.dedent()
762
763            code('')
764
765        code.dedent()
766        code.dedent()
767        code('''
768        break;  // If we got this far, we have nothing left todo
769    }
770    // g_eventQueue_ptr->scheduleEvent(this, 1);
771    // DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
772}
773''')
774
775        code.write(path, "%s_Wakeup.cc" % self.ident)
776
777    def printCSwitch(self, path):
778        '''Output switch statement for transition table'''
779
780        code = self.symtab.codeFormatter()
781        ident = self.ident
782
783        code('''
784// Auto generated C++ code started by $__file__:$__line__
785// ${ident}: ${{self.short}}
786
787#include "mem/ruby/common/Global.hh"
788#include "mem/protocol/${ident}_Controller.hh"
789#include "mem/protocol/${ident}_State.hh"
790#include "mem/protocol/${ident}_Event.hh"
791#include "mem/protocol/Types.hh"
792#include "mem/ruby/system/System.hh"
793
794#define HASH_FUN(state, event)  ((int(state)*${ident}_Event_NUM)+int(event))
795
796#define GET_TRANSITION_COMMENT() (${ident}_transitionComment.str())
797#define CLEAR_TRANSITION_COMMENT() (${ident}_transitionComment.str(""))
798
799TransitionResult
800${ident}_Controller::doTransition(${ident}_Event event,
801                                  ${ident}_State state,
802                                  const Address &addr)
803{
804    ${ident}_State next_state = state;
805
806    DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
807    DEBUG_MSG(GENERATED_COMP, MedPrio, *this);
808    DEBUG_EXPR(GENERATED_COMP, MedPrio, g_eventQueue_ptr->getTime());
809    DEBUG_EXPR(GENERATED_COMP, MedPrio,state);
810    DEBUG_EXPR(GENERATED_COMP, MedPrio,event);
811    DEBUG_EXPR(GENERATED_COMP, MedPrio,addr);
812
813    TransitionResult result =
814        doTransitionWorker(event, state, next_state, addr);
815
816    if (result == TransitionResult_Valid) {
817        DEBUG_EXPR(GENERATED_COMP, MedPrio, next_state);
818        DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
819        m_profiler.countTransition(state, event);
820        if (Debug::getProtocolTrace()) {
821            g_system_ptr->getProfiler()->profileTransition("${ident}",
822                    m_version, addr,
823                    ${ident}_State_to_string(state),
824                    ${ident}_Event_to_string(event),
825                    ${ident}_State_to_string(next_state),
826                    GET_TRANSITION_COMMENT());
827        }
828    CLEAR_TRANSITION_COMMENT();
829    ${ident}_setState(addr, next_state);
830
831    } else if (result == TransitionResult_ResourceStall) {
832        if (Debug::getProtocolTrace()) {
833            g_system_ptr->getProfiler()->profileTransition("${ident}",
834                   m_version, addr,
835                   ${ident}_State_to_string(state),
836                   ${ident}_Event_to_string(event),
837                   ${ident}_State_to_string(next_state),
838                   "Resource Stall");
839        }
840    } else if (result == TransitionResult_ProtocolStall) {
841        DEBUG_MSG(GENERATED_COMP, HighPrio, "stalling");
842        DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
843        if (Debug::getProtocolTrace()) {
844            g_system_ptr->getProfiler()->profileTransition("${ident}",
845                   m_version, addr,
846                   ${ident}_State_to_string(state),
847                   ${ident}_Event_to_string(event),
848                   ${ident}_State_to_string(next_state),
849                   "Protocol Stall");
850        }
851    }
852
853    return result;
854}
855
856TransitionResult
857${ident}_Controller::doTransitionWorker(${ident}_Event event,
858                                        ${ident}_State state,
859                                        ${ident}_State& next_state,
860                                        const Address& addr)
861{
862    switch(HASH_FUN(state, event)) {
863''')
864
865        # This map will allow suppress generating duplicate code
866        cases = orderdict()
867
868        for trans in self.transitions:
869            case_string = "%s_State_%s, %s_Event_%s" % \
870                (self.ident, trans.state.ident, self.ident, trans.event.ident)
871
872            case = self.symtab.codeFormatter()
873            # Only set next_state if it changes
874            if trans.state != trans.nextState:
875                ns_ident = trans.nextState.ident
876                case('next_state = ${ident}_State_${ns_ident};')
877
878            actions = trans.actions
879
880            # Check for resources
881            case_sorter = []
882            res = trans.resources
883            for key,val in res.iteritems():
884                if key.type.ident != "DNUCAStopTable":
885                    val = '''
886if (!%s.areNSlotsAvailable(%s))
887    return TransitionResult_ResourceStall;
888''' % (key.code, val)
889                case_sorter.append(val)
890
891
892            # Emit the code sequences in a sorted order.  This makes the
893            # output deterministic (without this the output order can vary
894            # since Map's keys() on a vector of pointers is not deterministic
895            for c in sorted(case_sorter):
896                case("$c")
897
898            # Figure out if we stall
899            stall = False
900            for action in actions:
901                if action.ident == "z_stall":
902                    stall = True
903                    break
904
905            if stall:
906                case('return TransitionResult_ProtocolStall;')
907            else:
908                for action in actions:
909                    case('${{action.ident}}(addr);')
910                case('return TransitionResult_Valid;')
911
912            case = str(case)
913
914            # Look to see if this transition code is unique.
915            if case not in cases:
916                cases[case] = []
917
918            cases[case].append(case_string)
919
920        # Walk through all of the unique code blocks and spit out the
921        # corresponding case statement elements
922        for case,transitions in cases.iteritems():
923            # Iterative over all the multiple transitions that share
924            # the same code
925            for trans in transitions:
926                code('  case HASH_FUN($trans):')
927            code('    $case')
928
929        code('''
930      default:
931        WARN_EXPR(m_version);
932        WARN_EXPR(g_eventQueue_ptr->getTime());
933        WARN_EXPR(addr);
934        WARN_EXPR(event);
935        WARN_EXPR(state);
936        ERROR_MSG(\"Invalid transition\");
937    }
938    return TransitionResult_Valid;
939}
940''')
941        code.write(path, "%s_Transitions.cc" % self.ident)
942
943    def printProfileDumperHH(self, path):
944        code = self.symtab.codeFormatter()
945        ident = self.ident
946
947        code('''
948// Auto generated C++ code started by $__file__:$__line__
949// ${ident}: ${{self.short}}
950
951#ifndef __${ident}_PROFILE_DUMPER_HH__
952#define __${ident}_PROFILE_DUMPER_HH__
953
954#include <iostream>
955#include <vector>
956
957#include "${ident}_Profiler.hh"
958#include "${ident}_Event.hh"
959
960typedef std::vector<${ident}_Profiler *> ${ident}_profilers;
961
962class ${ident}_ProfileDumper
963{
964  public:
965    ${ident}_ProfileDumper();
966    void registerProfiler(${ident}_Profiler* profiler);
967    void dumpStats(std::ostream& out) const;
968
969  private:
970    ${ident}_profilers m_profilers;
971};
972
973#endif // __${ident}_PROFILE_DUMPER_HH__
974''')
975        code.write(path, "%s_ProfileDumper.hh" % self.ident)
976
977    def printProfileDumperCC(self, path):
978        code = self.symtab.codeFormatter()
979        ident = self.ident
980
981        code('''
982// Auto generated C++ code started by $__file__:$__line__
983// ${ident}: ${{self.short}}
984
985#include "mem/protocol/${ident}_ProfileDumper.hh"
986
987${ident}_ProfileDumper::${ident}_ProfileDumper()
988{
989}
990
991void
992${ident}_ProfileDumper::registerProfiler(${ident}_Profiler* profiler)
993{
994    m_profilers.push_back(profiler);
995}
996
997void
998${ident}_ProfileDumper::dumpStats(std::ostream& out) const
999{
1000    out << " --- ${ident} ---\\n";
1001    out << " - Event Counts -\\n";
1002    for (${ident}_Event event = ${ident}_Event_FIRST;
1003         event < ${ident}_Event_NUM;
1004         ++event) {
1005        out << (${ident}_Event) event << " [";
1006        uint64 total = 0;
1007        for (int i = 0; i < m_profilers.size(); i++) {
1008             out << m_profilers[i]->getEventCount(event) << " ";
1009             total += m_profilers[i]->getEventCount(event);
1010        }
1011        out << "] " << total << "\\n";
1012    }
1013    out << "\\n";
1014    out << " - Transitions -\\n";
1015    for (${ident}_State state = ${ident}_State_FIRST;
1016         state < ${ident}_State_NUM;
1017         ++state) {
1018        for (${ident}_Event event = ${ident}_Event_FIRST;
1019             event < ${ident}_Event_NUM;
1020             ++event) {
1021            if (m_profilers[0]->isPossible(state, event)) {
1022                out << (${ident}_State) state << "  "
1023                    << (${ident}_Event) event << " [";
1024                uint64 total = 0;
1025                for (int i = 0; i < m_profilers.size(); i++) {
1026                     out << m_profilers[i]->getTransitionCount(state, event) << " ";
1027                     total += m_profilers[i]->getTransitionCount(state, event);
1028                }
1029                out << "] " << total << "\\n";
1030            }
1031        }
1032        out << "\\n";
1033    }
1034}
1035''')
1036        code.write(path, "%s_ProfileDumper.cc" % self.ident)
1037
1038    def printProfilerHH(self, path):
1039        code = self.symtab.codeFormatter()
1040        ident = self.ident
1041
1042        code('''
1043// Auto generated C++ code started by $__file__:$__line__
1044// ${ident}: ${{self.short}}
1045
1046#ifndef __${ident}_PROFILER_HH__
1047#define __${ident}_PROFILER_HH__
1048
1049#include <iostream>
1050
1051#include "mem/ruby/common/Global.hh"
1052#include "mem/protocol/${ident}_State.hh"
1053#include "mem/protocol/${ident}_Event.hh"
1054
1055class ${ident}_Profiler
1056{
1057  public:
1058    ${ident}_Profiler();
1059    void setVersion(int version);
1060    void countTransition(${ident}_State state, ${ident}_Event event);
1061    void possibleTransition(${ident}_State state, ${ident}_Event event);
1062    uint64 getEventCount(${ident}_Event event);
1063    bool isPossible(${ident}_State state, ${ident}_Event event);
1064    uint64 getTransitionCount(${ident}_State state, ${ident}_Event event);
1065    void clearStats();
1066
1067  private:
1068    int m_counters[${ident}_State_NUM][${ident}_Event_NUM];
1069    int m_event_counters[${ident}_Event_NUM];
1070    bool m_possible[${ident}_State_NUM][${ident}_Event_NUM];
1071    int m_version;
1072};
1073
1074#endif // __${ident}_PROFILER_HH__
1075''')
1076        code.write(path, "%s_Profiler.hh" % self.ident)
1077
1078    def printProfilerCC(self, path):
1079        code = self.symtab.codeFormatter()
1080        ident = self.ident
1081
1082        code('''
1083// Auto generated C++ code started by $__file__:$__line__
1084// ${ident}: ${{self.short}}
1085
1086#include "mem/protocol/${ident}_Profiler.hh"
1087
1088${ident}_Profiler::${ident}_Profiler()
1089{
1090    for (int state = 0; state < ${ident}_State_NUM; state++) {
1091        for (int event = 0; event < ${ident}_Event_NUM; event++) {
1092            m_possible[state][event] = false;
1093            m_counters[state][event] = 0;
1094        }
1095    }
1096    for (int event = 0; event < ${ident}_Event_NUM; event++) {
1097        m_event_counters[event] = 0;
1098    }
1099}
1100
1101void
1102${ident}_Profiler::setVersion(int version)
1103{
1104    m_version = version;
1105}
1106
1107void
1108${ident}_Profiler::clearStats()
1109{
1110    for (int state = 0; state < ${ident}_State_NUM; state++) {
1111        for (int event = 0; event < ${ident}_Event_NUM; event++) {
1112            m_counters[state][event] = 0;
1113        }
1114    }
1115
1116    for (int event = 0; event < ${ident}_Event_NUM; event++) {
1117        m_event_counters[event] = 0;
1118    }
1119}
1120void
1121${ident}_Profiler::countTransition(${ident}_State state, ${ident}_Event event)
1122{
1123    assert(m_possible[state][event]);
1124    m_counters[state][event]++;
1125    m_event_counters[event]++;
1126}
1127void
1128${ident}_Profiler::possibleTransition(${ident}_State state,
1129                                      ${ident}_Event event)
1130{
1131    m_possible[state][event] = true;
1132}
1133
1134uint64
1135${ident}_Profiler::getEventCount(${ident}_Event event)
1136{
1137    return m_event_counters[event];
1138}
1139
1140bool
1141${ident}_Profiler::isPossible(${ident}_State state, ${ident}_Event event)
1142{
1143    return m_possible[state][event];
1144}
1145
1146uint64
1147${ident}_Profiler::getTransitionCount(${ident}_State state,
1148                                      ${ident}_Event event)
1149{
1150    return m_counters[state][event];
1151}
1152
1153''')
1154        code.write(path, "%s_Profiler.cc" % self.ident)
1155
1156    # **************************
1157    # ******* HTML Files *******
1158    # **************************
1159    def frameRef(self, click_href, click_target, over_href, over_num, text):
1160        code = self.symtab.codeFormatter(fix_newlines=False)
1161        code("""<A href=\"$click_href\" target=\"$click_target\" onmouseover=\"
1162    if (parent.frames[$over_num].location != parent.location + '$over_href') {
1163        parent.frames[$over_num].location='$over_href'
1164    }\">
1165    ${{html.formatShorthand(text)}}
1166    </A>""")
1167        return str(code)
1168
1169    def writeHTMLFiles(self, path):
1170        # Create table with no row hilighted
1171        self.printHTMLTransitions(path, None)
1172
1173        # Generate transition tables
1174        for state in self.states.itervalues():
1175            self.printHTMLTransitions(path, state)
1176
1177        # Generate action descriptions
1178        for action in self.actions.itervalues():
1179            name = "%s_action_%s.html" % (self.ident, action.ident)
1180            code = html.createSymbol(action, "Action")
1181            code.write(path, name)
1182
1183        # Generate state descriptions
1184        for state in self.states.itervalues():
1185            name = "%s_State_%s.html" % (self.ident, state.ident)
1186            code = html.createSymbol(state, "State")
1187            code.write(path, name)
1188
1189        # Generate event descriptions
1190        for event in self.events.itervalues():
1191            name = "%s_Event_%s.html" % (self.ident, event.ident)
1192            code = html.createSymbol(event, "Event")
1193            code.write(path, name)
1194
1195    def printHTMLTransitions(self, path, active_state):
1196        code = self.symtab.codeFormatter()
1197
1198        code('''
1199<HTML>
1200<BODY link="blue" vlink="blue">
1201
1202<H1 align="center">${{html.formatShorthand(self.short)}}:
1203''')
1204        code.indent()
1205        for i,machine in enumerate(self.symtab.getAllType(StateMachine)):
1206            mid = machine.ident
1207            if i != 0:
1208                extra = " - "
1209            else:
1210                extra = ""
1211            if machine == self:
1212                code('$extra$mid')
1213            else:
1214                code('$extra<A target="Table" href="${mid}_table.html">$mid</A>')
1215        code.dedent()
1216
1217        code("""
1218</H1>
1219
1220<TABLE border=1>
1221<TR>
1222  <TH> </TH>
1223""")
1224
1225        for event in self.events.itervalues():
1226            href = "%s_Event_%s.html" % (self.ident, event.ident)
1227            ref = self.frameRef(href, "Status", href, "1", event.short)
1228            code('<TH bgcolor=white>$ref</TH>')
1229
1230        code('</TR>')
1231        # -- Body of table
1232        for state in self.states.itervalues():
1233            # -- Each row
1234            if state == active_state:
1235                color = "yellow"
1236            else:
1237                color = "white"
1238
1239            click = "%s_table_%s.html" % (self.ident, state.ident)
1240            over = "%s_State_%s.html" % (self.ident, state.ident)
1241            text = html.formatShorthand(state.short)
1242            ref = self.frameRef(click, "Table", over, "1", state.short)
1243            code('''
1244<TR>
1245  <TH bgcolor=$color>$ref</TH>
1246''')
1247
1248            # -- One column for each event
1249            for event in self.events.itervalues():
1250                trans = self.table.get((state,event), None)
1251                if trans is None:
1252                    # This is the no transition case
1253                    if state == active_state:
1254                        color = "#C0C000"
1255                    else:
1256                        color = "lightgrey"
1257
1258                    code('<TD bgcolor=$color>&nbsp;</TD>')
1259                    continue
1260
1261                next = trans.nextState
1262                stall_action = False
1263
1264                # -- Get the actions
1265                for action in trans.actions:
1266                    if action.ident == "z_stall" or \
1267                       action.ident == "zz_recycleMandatoryQueue":
1268                        stall_action = True
1269
1270                # -- Print out "actions/next-state"
1271                if stall_action:
1272                    if state == active_state:
1273                        color = "#C0C000"
1274                    else:
1275                        color = "lightgrey"
1276
1277                elif active_state and next.ident == active_state.ident:
1278                    color = "aqua"
1279                elif state == active_state:
1280                    color = "yellow"
1281                else:
1282                    color = "white"
1283
1284                code('<TD bgcolor=$color>')
1285                for action in trans.actions:
1286                    href = "%s_action_%s.html" % (self.ident, action.ident)
1287                    ref = self.frameRef(href, "Status", href, "1",
1288                                        action.short)
1289                    code('  $ref')
1290                if next != state:
1291                    if trans.actions:
1292                        code('/')
1293                    click = "%s_table_%s.html" % (self.ident, next.ident)
1294                    over = "%s_State_%s.html" % (self.ident, next.ident)
1295                    ref = self.frameRef(click, "Table", over, "1", next.short)
1296                    code("$ref")
1297                code("</TD>")
1298
1299            # -- Each row
1300            if state == active_state:
1301                color = "yellow"
1302            else:
1303                color = "white"
1304
1305            click = "%s_table_%s.html" % (self.ident, state.ident)
1306            over = "%s_State_%s.html" % (self.ident, state.ident)
1307            ref = self.frameRef(click, "Table", over, "1", state.short)
1308            code('''
1309  <TH bgcolor=$color>$ref</TH>
1310</TR>
1311''')
1312        code('''
1313<!- Column footer->
1314<TR>
1315  <TH> </TH>
1316''')
1317
1318        for event in self.events.itervalues():
1319            href = "%s_Event_%s.html" % (self.ident, event.ident)
1320            ref = self.frameRef(href, "Status", href, "1", event.short)
1321            code('<TH bgcolor=white>$ref</TH>')
1322        code('''
1323</TR>
1324</TABLE>
1325</BODY></HTML>
1326''')
1327
1328
1329        if active_state:
1330            name = "%s_table_%s.html" % (self.ident, active_state.ident)
1331        else:
1332            name = "%s_table.html" % self.ident
1333        code.write(path, name)
1334
1335__all__ = [ "StateMachine" ]
1336