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