StateMachine.py revision 9105
112967Smatteo.andreozzi@arm.com# Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
212967Smatteo.andreozzi@arm.com# Copyright (c) 2009 The Hewlett-Packard Development Company
312967Smatteo.andreozzi@arm.com# All rights reserved.
412967Smatteo.andreozzi@arm.com#
512967Smatteo.andreozzi@arm.com# Redistribution and use in source and binary forms, with or without
612967Smatteo.andreozzi@arm.com# modification, are permitted provided that the following conditions are
712967Smatteo.andreozzi@arm.com# met: redistributions of source code must retain the above copyright
812967Smatteo.andreozzi@arm.com# notice, this list of conditions and the following disclaimer;
912967Smatteo.andreozzi@arm.com# redistributions in binary form must reproduce the above copyright
1012967Smatteo.andreozzi@arm.com# notice, this list of conditions and the following disclaimer in the
1112967Smatteo.andreozzi@arm.com# documentation and/or other materials provided with the distribution;
1212967Smatteo.andreozzi@arm.com# neither the name of the copyright holders nor the names of its
1312967Smatteo.andreozzi@arm.com# contributors may be used to endorse or promote products derived from
1412967Smatteo.andreozzi@arm.com# this software without specific prior written permission.
1512967Smatteo.andreozzi@arm.com#
1612967Smatteo.andreozzi@arm.com# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1712967Smatteo.andreozzi@arm.com# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1812967Smatteo.andreozzi@arm.com# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
1912967Smatteo.andreozzi@arm.com# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2012967Smatteo.andreozzi@arm.com# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2112967Smatteo.andreozzi@arm.com# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2212967Smatteo.andreozzi@arm.com# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2312967Smatteo.andreozzi@arm.com# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2412967Smatteo.andreozzi@arm.com# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2512967Smatteo.andreozzi@arm.com# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
2612967Smatteo.andreozzi@arm.com# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2712967Smatteo.andreozzi@arm.com
2812967Smatteo.andreozzi@arm.comfrom m5.util import orderdict
2912967Smatteo.andreozzi@arm.com
3012967Smatteo.andreozzi@arm.comfrom slicc.symbols.Symbol import Symbol
3112967Smatteo.andreozzi@arm.comfrom slicc.symbols.Var import Var
3212967Smatteo.andreozzi@arm.comimport slicc.generate.html as html
3312967Smatteo.andreozzi@arm.comimport re
3412967Smatteo.andreozzi@arm.com
3512967Smatteo.andreozzi@arm.compython_class_map = {"int": "Int",
3612967Smatteo.andreozzi@arm.com                    "std::string": "String",
3712967Smatteo.andreozzi@arm.com                    "bool": "Bool",
3812967Smatteo.andreozzi@arm.com                    "CacheMemory": "RubyCache",
3912967Smatteo.andreozzi@arm.com                    "WireBuffer": "RubyWireBuffer",
4012967Smatteo.andreozzi@arm.com                    "Sequencer": "RubySequencer",
4112967Smatteo.andreozzi@arm.com                    "DirectoryMemory": "RubyDirectoryMemory",
4212967Smatteo.andreozzi@arm.com                    "MemoryControl": "MemoryControl",
4312967Smatteo.andreozzi@arm.com                    "DMASequencer": "DMASequencer"
4412967Smatteo.andreozzi@arm.com                    }
4512967Smatteo.andreozzi@arm.com
4612967Smatteo.andreozzi@arm.comclass StateMachine(Symbol):
4712967Smatteo.andreozzi@arm.com    def __init__(self, symtab, ident, location, pairs, config_parameters):
4812967Smatteo.andreozzi@arm.com        super(StateMachine, self).__init__(symtab, ident, location, pairs)
4912967Smatteo.andreozzi@arm.com        self.table = None
5012967Smatteo.andreozzi@arm.com        self.config_parameters = config_parameters
5112967Smatteo.andreozzi@arm.com
5212967Smatteo.andreozzi@arm.com        for param in config_parameters:
5312967Smatteo.andreozzi@arm.com            if param.pointer:
5412967Smatteo.andreozzi@arm.com                var = Var(symtab, param.name, location, param.type_ast.type,
5512967Smatteo.andreozzi@arm.com                          "(*m_%s_ptr)" % param.name, {}, self)
5612967Smatteo.andreozzi@arm.com            else:
5712967Smatteo.andreozzi@arm.com                var = Var(symtab, param.name, location, param.type_ast.type,
5812967Smatteo.andreozzi@arm.com                          "m_%s" % param.name, {}, self)
5912967Smatteo.andreozzi@arm.com            self.symtab.registerSym(param.name, var)
6012967Smatteo.andreozzi@arm.com
6112967Smatteo.andreozzi@arm.com        self.states = orderdict()
6212967Smatteo.andreozzi@arm.com        self.events = orderdict()
6312967Smatteo.andreozzi@arm.com        self.actions = orderdict()
64        self.request_types = orderdict()
65        self.transitions = []
66        self.in_ports = []
67        self.functions = []
68        self.objects = []
69        self.TBEType   = None
70        self.EntryType = None
71
72        self.message_buffer_names = []
73
74    def __repr__(self):
75        return "[StateMachine: %s]" % self.ident
76
77    def addState(self, state):
78        assert self.table is None
79        self.states[state.ident] = state
80
81    def addEvent(self, event):
82        assert self.table is None
83        self.events[event.ident] = event
84
85    def addAction(self, action):
86        assert self.table is None
87
88        # Check for duplicate action
89        for other in self.actions.itervalues():
90            if action.ident == other.ident:
91                action.warning("Duplicate action definition: %s" % action.ident)
92                action.error("Duplicate action definition: %s" % action.ident)
93            if action.short == other.short:
94                other.warning("Duplicate action shorthand: %s" % other.ident)
95                other.warning("    shorthand = %s" % other.short)
96                action.warning("Duplicate action shorthand: %s" % action.ident)
97                action.error("    shorthand = %s" % action.short)
98
99        self.actions[action.ident] = action
100
101    def addRequestType(self, request_type):
102        assert self.table is None
103        self.request_types[request_type.ident] = request_type
104
105    def addTransition(self, trans):
106        assert self.table is None
107        self.transitions.append(trans)
108
109    def addInPort(self, var):
110        self.in_ports.append(var)
111
112    def addFunc(self, func):
113        # register func in the symbol table
114        self.symtab.registerSym(str(func), func)
115        self.functions.append(func)
116
117    def addObject(self, obj):
118        self.objects.append(obj)
119
120    def addType(self, type):
121        type_ident = '%s' % type.c_ident
122
123        if type_ident == "%s_TBE" %self.ident:
124            if self.TBEType != None:
125                self.error("Multiple Transaction Buffer types in a " \
126                           "single machine.");
127            self.TBEType = type
128
129        elif "interface" in type and "AbstractCacheEntry" == type["interface"]:
130            if self.EntryType != None:
131                self.error("Multiple AbstractCacheEntry types in a " \
132                           "single machine.");
133            self.EntryType = type
134
135    # Needs to be called before accessing the table
136    def buildTable(self):
137        assert self.table is None
138
139        table = {}
140
141        for trans in self.transitions:
142            # Track which actions we touch so we know if we use them
143            # all -- really this should be done for all symbols as
144            # part of the symbol table, then only trigger it for
145            # Actions, States, Events, etc.
146
147            for action in trans.actions:
148                action.used = True
149
150            index = (trans.state, trans.event)
151            if index in table:
152                table[index].warning("Duplicate transition: %s" % table[index])
153                trans.error("Duplicate transition: %s" % trans)
154            table[index] = trans
155
156        # Look at all actions to make sure we used them all
157        for action in self.actions.itervalues():
158            if not action.used:
159                error_msg = "Unused action: %s" % action.ident
160                if "desc" in action:
161                    error_msg += ", "  + action.desc
162                action.warning(error_msg)
163        self.table = table
164
165    def writeCodeFiles(self, path):
166        self.printControllerPython(path)
167        self.printControllerHH(path)
168        self.printControllerCC(path)
169        self.printCSwitch(path)
170        self.printCWakeup(path)
171        self.printProfilerCC(path)
172        self.printProfilerHH(path)
173        self.printProfileDumperCC(path)
174        self.printProfileDumperHH(path)
175
176    def printControllerPython(self, path):
177        code = self.symtab.codeFormatter()
178        ident = self.ident
179        py_ident = "%s_Controller" % ident
180        c_ident = "%s_Controller" % self.ident
181        code('''
182from m5.params import *
183from m5.SimObject import SimObject
184from Controller import RubyController
185
186class $py_ident(RubyController):
187    type = '$py_ident'
188''')
189        code.indent()
190        for param in self.config_parameters:
191            dflt_str = ''
192            if param.default is not None:
193                dflt_str = str(param.default) + ', '
194            if python_class_map.has_key(param.type_ast.type.c_ident):
195                python_type = python_class_map[param.type_ast.type.c_ident]
196                code('${{param.name}} = Param.${{python_type}}(${dflt_str}"")')
197            else:
198                self.error("Unknown c++ to python class conversion for c++ " \
199                           "type: '%s'. Please update the python_class_map " \
200                           "in StateMachine.py", param.type_ast.type.c_ident)
201        code.dedent()
202        code.write(path, '%s.py' % py_ident)
203
204
205    def printControllerHH(self, path):
206        '''Output the method declarations for the class declaration'''
207        code = self.symtab.codeFormatter()
208        ident = self.ident
209        c_ident = "%s_Controller" % self.ident
210
211        self.message_buffer_names = []
212
213        code('''
214/** \\file $c_ident.hh
215 *
216 * Auto generated C++ code started by $__file__:$__line__
217 * Created by slicc definition of Module "${{self.short}}"
218 */
219
220#ifndef __${ident}_CONTROLLER_HH__
221#define __${ident}_CONTROLLER_HH__
222
223#include <iostream>
224#include <sstream>
225#include <string>
226
227#include "mem/protocol/${ident}_ProfileDumper.hh"
228#include "mem/protocol/${ident}_Profiler.hh"
229#include "mem/protocol/TransitionResult.hh"
230#include "mem/protocol/Types.hh"
231#include "mem/ruby/common/Consumer.hh"
232#include "mem/ruby/common/Global.hh"
233#include "mem/ruby/slicc_interface/AbstractController.hh"
234#include "params/$c_ident.hh"
235''')
236
237        seen_types = set()
238        for var in self.objects:
239            if var.type.ident not in seen_types and not var.type.isPrimitive:
240                code('#include "mem/protocol/${{var.type.c_ident}}.hh"')
241            seen_types.add(var.type.ident)
242
243        # for adding information to the protocol debug trace
244        code('''
245extern std::stringstream ${ident}_transitionComment;
246
247class $c_ident : public AbstractController
248{
249// the coherence checker needs to call isBlockExclusive() and isBlockShared()
250// making the Chip a friend class is an easy way to do this for now
251
252public:
253    typedef ${c_ident}Params Params;
254    $c_ident(const Params *p);
255    static int getNumControllers();
256    void init();
257    MessageBuffer* getMandatoryQueue() const;
258    const int & getVersion() const;
259    const std::string toString() const;
260    const std::string getName() const;
261    void stallBuffer(MessageBuffer* buf, Address addr);
262    void wakeUpBuffers(Address addr);
263    void wakeUpAllBuffers();
264    void initNetworkPtr(Network* net_ptr) { m_net_ptr = net_ptr; }
265    void print(std::ostream& out) const;
266    void printConfig(std::ostream& out) const;
267    void wakeup();
268    void printStats(std::ostream& out) const;
269    void clearStats();
270    void blockOnQueue(Address addr, MessageBuffer* port);
271    void unblock(Address addr);
272    void recordCacheTrace(int cntrl, CacheRecorder* tr);
273    Sequencer* getSequencer() const;
274
275private:
276''')
277
278        code.indent()
279        # added by SS
280        for param in self.config_parameters:
281            if param.pointer:
282                code('${{param.type_ast.type}}* m_${{param.ident}}_ptr;')
283            else:
284                code('${{param.type_ast.type}} m_${{param.ident}};')
285
286        code('''
287int m_number_of_TBEs;
288
289TransitionResult doTransition(${ident}_Event event,
290''')
291
292        if self.EntryType != None:
293            code('''
294                              ${{self.EntryType.c_ident}}* m_cache_entry_ptr,
295''')
296        if self.TBEType != None:
297            code('''
298                              ${{self.TBEType.c_ident}}* m_tbe_ptr,
299''')
300
301        code('''
302                              const Address& addr);
303
304TransitionResult doTransitionWorker(${ident}_Event event,
305                                    ${ident}_State state,
306                                    ${ident}_State& next_state,
307''')
308
309        if self.TBEType != None:
310            code('''
311                                    ${{self.TBEType.c_ident}}*& m_tbe_ptr,
312''')
313        if self.EntryType != None:
314            code('''
315                                    ${{self.EntryType.c_ident}}*& m_cache_entry_ptr,
316''')
317
318        code('''
319                                    const Address& addr);
320
321std::string m_name;
322int m_transitions_per_cycle;
323int m_buffer_size;
324int m_recycle_latency;
325std::map<std::string, std::string> m_cfg;
326NodeID m_version;
327Network* m_net_ptr;
328MachineID m_machineID;
329bool m_is_blocking;
330std::map<Address, MessageBuffer*> m_block_map;
331typedef std::vector<MessageBuffer*> MsgVecType;
332typedef std::map< Address, MsgVecType* > WaitingBufType;
333WaitingBufType m_waiting_buffers;
334int m_max_in_port_rank;
335int m_cur_in_port_rank;
336static ${ident}_ProfileDumper s_profileDumper;
337${ident}_Profiler m_profiler;
338static int m_num_controllers;
339
340// Internal functions
341''')
342
343        for func in self.functions:
344            proto = func.prototype
345            if proto:
346                code('$proto')
347
348        if self.EntryType != None:
349            code('''
350
351// Set and Reset for cache_entry variable
352void set_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, AbstractCacheEntry* m_new_cache_entry);
353void unset_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr);
354''')
355
356        if self.TBEType != None:
357            code('''
358
359// Set and Reset for tbe variable
360void set_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${ident}_TBE* m_new_tbe);
361void unset_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr);
362''')
363
364        code('''
365
366// Actions
367''')
368        if self.TBEType != None and self.EntryType != None:
369            for action in self.actions.itervalues():
370                code('/** \\brief ${{action.desc}} */')
371                code('void ${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr);')
372        elif self.TBEType != None:
373            for action in self.actions.itervalues():
374                code('/** \\brief ${{action.desc}} */')
375                code('void ${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, const Address& addr);')
376        elif self.EntryType != None:
377            for action in self.actions.itervalues():
378                code('/** \\brief ${{action.desc}} */')
379                code('void ${{action.ident}}(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr);')
380        else:
381            for action in self.actions.itervalues():
382                code('/** \\brief ${{action.desc}} */')
383                code('void ${{action.ident}}(const Address& addr);')
384
385        # the controller internal variables
386        code('''
387
388// Objects
389''')
390        for var in self.objects:
391            th = var.get("template_hack", "")
392            code('${{var.type.c_ident}}$th* m_${{var.c_ident}}_ptr;')
393
394            if var.type.ident == "MessageBuffer":
395                self.message_buffer_names.append("m_%s_ptr" % var.c_ident)
396
397        code.dedent()
398        code('};')
399        code('#endif // __${ident}_CONTROLLER_H__')
400        code.write(path, '%s.hh' % c_ident)
401
402    def printControllerCC(self, path):
403        '''Output the actions for performing the actions'''
404
405        code = self.symtab.codeFormatter()
406        ident = self.ident
407        c_ident = "%s_Controller" % self.ident
408
409        code('''
410/** \\file $c_ident.cc
411 *
412 * Auto generated C++ code started by $__file__:$__line__
413 * Created by slicc definition of Module "${{self.short}}"
414 */
415
416#include <sys/types.h>
417#include <unistd.h>
418
419#include <cassert>
420#include <sstream>
421#include <string>
422
423#include "base/compiler.hh"
424#include "base/cprintf.hh"
425#include "debug/RubyGenerated.hh"
426#include "debug/RubySlicc.hh"
427#include "mem/protocol/${ident}_Controller.hh"
428#include "mem/protocol/${ident}_Event.hh"
429#include "mem/protocol/${ident}_State.hh"
430#include "mem/protocol/Types.hh"
431#include "mem/ruby/common/Global.hh"
432#include "mem/ruby/slicc_interface/RubySlicc_includes.hh"
433#include "mem/ruby/system/System.hh"
434
435using namespace std;
436''')
437
438        # include object classes
439        seen_types = set()
440        for var in self.objects:
441            if var.type.ident not in seen_types and not var.type.isPrimitive:
442                code('#include "mem/protocol/${{var.type.c_ident}}.hh"')
443            seen_types.add(var.type.ident)
444
445        code('''
446$c_ident *
447${c_ident}Params::create()
448{
449    return new $c_ident(this);
450}
451
452int $c_ident::m_num_controllers = 0;
453${ident}_ProfileDumper $c_ident::s_profileDumper;
454
455// for adding information to the protocol debug trace
456stringstream ${ident}_transitionComment;
457#define APPEND_TRANSITION_COMMENT(str) (${ident}_transitionComment << str)
458
459/** \\brief constructor */
460$c_ident::$c_ident(const Params *p)
461    : AbstractController(p)
462{
463    m_version = p->version;
464    m_transitions_per_cycle = p->transitions_per_cycle;
465    m_buffer_size = p->buffer_size;
466    m_recycle_latency = p->recycle_latency;
467    m_number_of_TBEs = p->number_of_TBEs;
468    m_is_blocking = false;
469    m_name = "${ident}";
470''')
471        #
472        # max_port_rank is used to size vectors and thus should be one plus the
473        # largest port rank
474        #
475        max_port_rank = self.in_ports[0].pairs["max_port_rank"] + 1
476        code('    m_max_in_port_rank = $max_port_rank;')
477        code.indent()
478
479        #
480        # After initializing the universal machine parameters, initialize the
481        # this machines config parameters.  Also detemine if these configuration
482        # params include a sequencer.  This information will be used later for
483        # contecting the sequencer back to the L1 cache controller.
484        #
485        contains_dma_sequencer = False
486        sequencers = []
487        for param in self.config_parameters:
488            if param.name == "dma_sequencer":
489                contains_dma_sequencer = True
490            elif re.compile("sequencer").search(param.name):
491                sequencers.append(param.name)
492            if param.pointer:
493                code('m_${{param.name}}_ptr = p->${{param.name}};')
494            else:
495                code('m_${{param.name}} = p->${{param.name}};')
496
497        #
498        # For the l1 cache controller, add the special atomic support which
499        # includes passing the sequencer a pointer to the controller.
500        #
501        if self.ident == "L1Cache":
502            if not sequencers:
503                self.error("The L1Cache controller must include the sequencer " \
504                           "configuration parameter")
505
506            for seq in sequencers:
507                code('''
508m_${{seq}}_ptr->setController(this);
509    ''')
510
511        else:
512            for seq in sequencers:
513                code('''
514m_${{seq}}_ptr->setController(this);
515    ''')
516
517        #
518        # For the DMA controller, pass the sequencer a pointer to the
519        # controller.
520        #
521        if self.ident == "DMA":
522            if not contains_dma_sequencer:
523                self.error("The DMA controller must include the sequencer " \
524                           "configuration parameter")
525
526            code('''
527m_dma_sequencer_ptr->setController(this);
528''')
529
530        code('m_num_controllers++;')
531        for var in self.objects:
532            if var.ident.find("mandatoryQueue") >= 0:
533                code('m_${{var.c_ident}}_ptr = new ${{var.type.c_ident}}();')
534
535        code.dedent()
536        code('''
537}
538
539void
540$c_ident::init()
541{
542    MachineType machine_type;
543    int base;
544
545    m_machineID.type = MachineType_${ident};
546    m_machineID.num = m_version;
547
548    // initialize objects
549    m_profiler.setVersion(m_version);
550    s_profileDumper.registerProfiler(&m_profiler);
551
552''')
553
554        code.indent()
555        for var in self.objects:
556            vtype = var.type
557            vid = "m_%s_ptr" % var.c_ident
558            if "network" not in var:
559                # Not a network port object
560                if "primitive" in vtype:
561                    code('$vid = new ${{vtype.c_ident}};')
562                    if "default" in var:
563                        code('(*$vid) = ${{var["default"]}};')
564                else:
565                    # Normal Object
566                    # added by SS
567                    if "factory" in var:
568                        code('$vid = ${{var["factory"]}};')
569                    elif var.ident.find("mandatoryQueue") < 0:
570                        th = var.get("template_hack", "")
571                        expr = "%s  = new %s%s" % (vid, vtype.c_ident, th)
572
573                        args = ""
574                        if "non_obj" not in vtype and not vtype.isEnumeration:
575                            if expr.find("TBETable") >= 0:
576                                args = "m_number_of_TBEs"
577                            else:
578                                args = var.get("constructor_hack", "")
579
580                        code('$expr($args);')
581
582                    code('assert($vid != NULL);')
583
584                    if "default" in var:
585                        code('*$vid = ${{var["default"]}}; // Object default')
586                    elif "default" in vtype:
587                        comment = "Type %s default" % vtype.ident
588                        code('*$vid = ${{vtype["default"]}}; // $comment')
589
590                    # Set ordering
591                    if "ordered" in var and "trigger_queue" not in var:
592                        # A buffer
593                        code('$vid->setOrdering(${{var["ordered"]}});')
594
595                    # Set randomization
596                    if "random" in var:
597                        # A buffer
598                        code('$vid->setRandomization(${{var["random"]}});')
599
600                    # Set Priority
601                    if vtype.isBuffer and \
602                           "rank" in var and "trigger_queue" not in var:
603                        code('$vid->setPriority(${{var["rank"]}});')
604
605            else:
606                # Network port object
607                network = var["network"]
608                ordered =  var["ordered"]
609                vnet = var["virtual_network"]
610                vnet_type = var["vnet_type"]
611
612                assert var.machine is not None
613                code('''
614machine_type = string_to_MachineType("${{var.machine.ident}}");
615base = MachineType_base_number(machine_type);
616$vid = m_net_ptr->get${network}NetQueue(m_version + base, $ordered, $vnet, "$vnet_type");
617''')
618
619                code('assert($vid != NULL);')
620
621                # Set ordering
622                if "ordered" in var:
623                    # A buffer
624                    code('$vid->setOrdering(${{var["ordered"]}});')
625
626                # Set randomization
627                if "random" in var:
628                    # A buffer
629                    code('$vid->setRandomization(${{var["random"]}});')
630
631                # Set Priority
632                if "rank" in var:
633                    code('$vid->setPriority(${{var["rank"]}})')
634
635                # Set buffer size
636                if vtype.isBuffer:
637                    code('''
638if (m_buffer_size > 0) {
639    $vid->resize(m_buffer_size);
640}
641''')
642
643                # set description (may be overriden later by port def)
644                code('''
645$vid->setDescription("[Version " + to_string(m_version) + ", ${ident}, name=${{var.c_ident}}]");
646
647''')
648
649            if vtype.isBuffer:
650                if "recycle_latency" in var:
651                    code('$vid->setRecycleLatency(${{var["recycle_latency"]}});')
652                else:
653                    code('$vid->setRecycleLatency(m_recycle_latency);')
654
655
656        # Set the queue consumers
657        code()
658        for port in self.in_ports:
659            code('${{port.code}}.setConsumer(this);')
660
661        # Set the queue descriptions
662        code()
663        for port in self.in_ports:
664            code('${{port.code}}.setDescription("[Version " + to_string(m_version) + ", $ident, $port]");')
665
666        # Initialize the transition profiling
667        code()
668        for trans in self.transitions:
669            # Figure out if we stall
670            stall = False
671            for action in trans.actions:
672                if action.ident == "z_stall":
673                    stall = True
674
675            # Only possible if it is not a 'z' case
676            if not stall:
677                state = "%s_State_%s" % (self.ident, trans.state.ident)
678                event = "%s_Event_%s" % (self.ident, trans.event.ident)
679                code('m_profiler.possibleTransition($state, $event);')
680
681        code.dedent()
682        code('}')
683
684        has_mandatory_q = False
685        for port in self.in_ports:
686            if port.code.find("mandatoryQueue_ptr") >= 0:
687                has_mandatory_q = True
688
689        if has_mandatory_q:
690            mq_ident = "m_%s_mandatoryQueue_ptr" % self.ident
691        else:
692            mq_ident = "NULL"
693
694        seq_ident = "NULL"
695        for param in self.config_parameters:
696            if param.name == "sequencer":
697                assert(param.pointer)
698                seq_ident = "m_%s_ptr" % param.name
699
700        code('''
701int
702$c_ident::getNumControllers()
703{
704    return m_num_controllers;
705}
706
707MessageBuffer*
708$c_ident::getMandatoryQueue() const
709{
710    return $mq_ident;
711}
712
713Sequencer*
714$c_ident::getSequencer() const
715{
716    return $seq_ident;
717}
718
719const int &
720$c_ident::getVersion() const
721{
722    return m_version;
723}
724
725const string
726$c_ident::toString() const
727{
728    return "$c_ident";
729}
730
731const string
732$c_ident::getName() const
733{
734    return m_name;
735}
736
737void
738$c_ident::stallBuffer(MessageBuffer* buf, Address addr)
739{
740    if (m_waiting_buffers.count(addr) == 0) {
741        MsgVecType* msgVec = new MsgVecType;
742        msgVec->resize(m_max_in_port_rank, NULL);
743        m_waiting_buffers[addr] = msgVec;
744    }
745    (*(m_waiting_buffers[addr]))[m_cur_in_port_rank] = buf;
746}
747
748void
749$c_ident::wakeUpBuffers(Address addr)
750{
751    if (m_waiting_buffers.count(addr) > 0) {
752        //
753        // Wake up all possible lower rank (i.e. lower priority) buffers that could
754        // be waiting on this message.
755        //
756        for (int in_port_rank = m_cur_in_port_rank - 1;
757             in_port_rank >= 0;
758             in_port_rank--) {
759            if ((*(m_waiting_buffers[addr]))[in_port_rank] != NULL) {
760                (*(m_waiting_buffers[addr]))[in_port_rank]->reanalyzeMessages(addr);
761            }
762        }
763        delete m_waiting_buffers[addr];
764        m_waiting_buffers.erase(addr);
765    }
766}
767
768void
769$c_ident::wakeUpAllBuffers()
770{
771    //
772    // Wake up all possible buffers that could be waiting on any message.
773    //
774
775    std::vector<MsgVecType*> wokeUpMsgVecs;
776
777    if(m_waiting_buffers.size() > 0) {
778        for (WaitingBufType::iterator buf_iter = m_waiting_buffers.begin();
779             buf_iter != m_waiting_buffers.end();
780             ++buf_iter) {
781             for (MsgVecType::iterator vec_iter = buf_iter->second->begin();
782                  vec_iter != buf_iter->second->end();
783                  ++vec_iter) {
784                  if (*vec_iter != NULL) {
785                      (*vec_iter)->reanalyzeAllMessages();
786                  }
787             }
788             wokeUpMsgVecs.push_back(buf_iter->second);
789        }
790
791        for (std::vector<MsgVecType*>::iterator wb_iter = wokeUpMsgVecs.begin();
792             wb_iter != wokeUpMsgVecs.end();
793             ++wb_iter) {
794             delete (*wb_iter);
795        }
796
797        m_waiting_buffers.clear();
798    }
799}
800
801void
802$c_ident::blockOnQueue(Address addr, MessageBuffer* port)
803{
804    m_is_blocking = true;
805    m_block_map[addr] = port;
806}
807
808void
809$c_ident::unblock(Address addr)
810{
811    m_block_map.erase(addr);
812    if (m_block_map.size() == 0) {
813       m_is_blocking = false;
814    }
815}
816
817void
818$c_ident::print(ostream& out) const
819{
820    out << "[$c_ident " << m_version << "]";
821}
822
823void
824$c_ident::printConfig(ostream& out) const
825{
826    out << "$c_ident config: " << m_name << endl;
827    out << "  version: " << m_version << endl;
828    map<string, string>::const_iterator it;
829    for (it = m_cfg.begin(); it != m_cfg.end(); it++)
830        out << "  " << it->first << ": " << it->second << endl;
831}
832
833void
834$c_ident::printStats(ostream& out) const
835{
836''')
837        #
838        # Cache and Memory Controllers have specific profilers associated with
839        # them.  Print out these stats before dumping state transition stats.
840        #
841        for param in self.config_parameters:
842            if param.type_ast.type.ident == "CacheMemory" or \
843               param.type_ast.type.ident == "DirectoryMemory" or \
844                   param.type_ast.type.ident == "MemoryControl":
845                assert(param.pointer)
846                code('    m_${{param.ident}}_ptr->printStats(out);')
847
848        code('''
849    if (m_version == 0) {
850        s_profileDumper.dumpStats(out);
851    }
852}
853
854void $c_ident::clearStats() {
855''')
856        #
857        # Cache and Memory Controllers have specific profilers associated with
858        # them.  These stats must be cleared too.
859        #
860        for param in self.config_parameters:
861            if param.type_ast.type.ident == "CacheMemory" or \
862                   param.type_ast.type.ident == "MemoryControl":
863                assert(param.pointer)
864                code('    m_${{param.ident}}_ptr->clearStats();')
865
866        code('''
867    m_profiler.clearStats();
868}
869''')
870
871        if self.EntryType != None:
872            code('''
873
874// Set and Reset for cache_entry variable
875void
876$c_ident::set_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, AbstractCacheEntry* m_new_cache_entry)
877{
878  m_cache_entry_ptr = (${{self.EntryType.c_ident}}*)m_new_cache_entry;
879}
880
881void
882$c_ident::unset_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr)
883{
884  m_cache_entry_ptr = 0;
885}
886''')
887
888        if self.TBEType != None:
889            code('''
890
891// Set and Reset for tbe variable
892void
893$c_ident::set_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.TBEType.c_ident}}* m_new_tbe)
894{
895  m_tbe_ptr = m_new_tbe;
896}
897
898void
899$c_ident::unset_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr)
900{
901  m_tbe_ptr = NULL;
902}
903''')
904
905        code('''
906
907void
908$c_ident::recordCacheTrace(int cntrl, CacheRecorder* tr)
909{
910''')
911        #
912        # Record cache contents for all associated caches.
913        #
914        code.indent()
915        for param in self.config_parameters:
916            if param.type_ast.type.ident == "CacheMemory":
917                assert(param.pointer)
918                code('m_${{param.ident}}_ptr->recordCacheContents(cntrl, tr);')
919
920        code.dedent()
921        code('''
922}
923
924// Actions
925''')
926        if self.TBEType != None and self.EntryType != None:
927            for action in self.actions.itervalues():
928                if "c_code" not in action:
929                 continue
930
931                code('''
932/** \\brief ${{action.desc}} */
933void
934$c_ident::${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr)
935{
936    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
937    ${{action["c_code"]}}
938}
939
940''')
941        elif self.TBEType != None:
942            for action in self.actions.itervalues():
943                if "c_code" not in action:
944                 continue
945
946                code('''
947/** \\brief ${{action.desc}} */
948void
949$c_ident::${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, const Address& addr)
950{
951    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
952    ${{action["c_code"]}}
953}
954
955''')
956        elif self.EntryType != None:
957            for action in self.actions.itervalues():
958                if "c_code" not in action:
959                 continue
960
961                code('''
962/** \\brief ${{action.desc}} */
963void
964$c_ident::${{action.ident}}(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr)
965{
966    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
967    ${{action["c_code"]}}
968}
969
970''')
971        else:
972            for action in self.actions.itervalues():
973                if "c_code" not in action:
974                 continue
975
976                code('''
977/** \\brief ${{action.desc}} */
978void
979$c_ident::${{action.ident}}(const Address& addr)
980{
981    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
982    ${{action["c_code"]}}
983}
984
985''')
986        for func in self.functions:
987            code(func.generateCode())
988
989        code.write(path, "%s.cc" % c_ident)
990
991    def printCWakeup(self, path):
992        '''Output the wakeup loop for the events'''
993
994        code = self.symtab.codeFormatter()
995        ident = self.ident
996
997        outputRequest_types = True
998        if len(self.request_types) == 0:
999            outputRequest_types = False
1000
1001        code('''
1002// Auto generated C++ code started by $__file__:$__line__
1003// ${ident}: ${{self.short}}
1004
1005#include <sys/types.h>
1006#include <unistd.h>
1007
1008#include <cassert>
1009
1010#include "base/misc.hh"
1011#include "debug/RubySlicc.hh"
1012#include "mem/protocol/${ident}_Controller.hh"
1013#include "mem/protocol/${ident}_Event.hh"
1014#include "mem/protocol/${ident}_State.hh"
1015''')
1016
1017        if outputRequest_types:
1018            code('''#include "mem/protocol/${ident}_RequestType.hh"''')
1019
1020        code('''
1021#include "mem/protocol/Types.hh"
1022#include "mem/ruby/common/Global.hh"
1023#include "mem/ruby/slicc_interface/RubySlicc_includes.hh"
1024#include "mem/ruby/system/System.hh"
1025
1026using namespace std;
1027
1028void
1029${ident}_Controller::wakeup()
1030{
1031    int counter = 0;
1032    while (true) {
1033        // Some cases will put us into an infinite loop without this limit
1034        assert(counter <= m_transitions_per_cycle);
1035        if (counter == m_transitions_per_cycle) {
1036            // Count how often we are fully utilized
1037            g_system_ptr->getProfiler()->controllerBusy(m_machineID);
1038
1039            // Wakeup in another cycle and try again
1040            g_eventQueue_ptr->scheduleEvent(this, 1);
1041            break;
1042        }
1043''')
1044
1045        code.indent()
1046        code.indent()
1047
1048        # InPorts
1049        #
1050        for port in self.in_ports:
1051            code.indent()
1052            code('// ${ident}InPort $port')
1053            if port.pairs.has_key("rank"):
1054                code('m_cur_in_port_rank = ${{port.pairs["rank"]}};')
1055            else:
1056                code('m_cur_in_port_rank = 0;')
1057            code('${{port["c_code_in_port"]}}')
1058            code.dedent()
1059
1060            code('')
1061
1062        code.dedent()
1063        code.dedent()
1064        code('''
1065        break;  // If we got this far, we have nothing left todo
1066    }
1067    // g_eventQueue_ptr->scheduleEvent(this, 1);
1068}
1069''')
1070
1071        code.write(path, "%s_Wakeup.cc" % self.ident)
1072
1073    def printCSwitch(self, path):
1074        '''Output switch statement for transition table'''
1075
1076        code = self.symtab.codeFormatter()
1077        ident = self.ident
1078
1079        code('''
1080// Auto generated C++ code started by $__file__:$__line__
1081// ${ident}: ${{self.short}}
1082
1083#include <cassert>
1084
1085#include "base/misc.hh"
1086#include "base/trace.hh"
1087#include "debug/ProtocolTrace.hh"
1088#include "debug/RubyGenerated.hh"
1089#include "mem/protocol/${ident}_Controller.hh"
1090#include "mem/protocol/${ident}_Event.hh"
1091#include "mem/protocol/${ident}_State.hh"
1092#include "mem/protocol/Types.hh"
1093#include "mem/ruby/common/Global.hh"
1094#include "mem/ruby/system/System.hh"
1095
1096#define HASH_FUN(state, event)  ((int(state)*${ident}_Event_NUM)+int(event))
1097
1098#define GET_TRANSITION_COMMENT() (${ident}_transitionComment.str())
1099#define CLEAR_TRANSITION_COMMENT() (${ident}_transitionComment.str(""))
1100
1101TransitionResult
1102${ident}_Controller::doTransition(${ident}_Event event,
1103''')
1104        if self.EntryType != None:
1105            code('''
1106                                  ${{self.EntryType.c_ident}}* m_cache_entry_ptr,
1107''')
1108        if self.TBEType != None:
1109            code('''
1110                                  ${{self.TBEType.c_ident}}* m_tbe_ptr,
1111''')
1112        code('''
1113                                  const Address &addr)
1114{
1115''')
1116        if self.TBEType != None and self.EntryType != None:
1117            code('${ident}_State state = getState(m_tbe_ptr, m_cache_entry_ptr, addr);')
1118        elif self.TBEType != None:
1119            code('${ident}_State state = getState(m_tbe_ptr, addr);')
1120        elif self.EntryType != None:
1121            code('${ident}_State state = getState(m_cache_entry_ptr, addr);')
1122        else:
1123            code('${ident}_State state = getState(addr);')
1124
1125        code('''
1126    ${ident}_State next_state = state;
1127
1128    DPRINTF(RubyGenerated, "%s, Time: %lld, state: %s, event: %s, addr: %s\\n",
1129            *this,
1130            g_eventQueue_ptr->getTime(),
1131            ${ident}_State_to_string(state),
1132            ${ident}_Event_to_string(event),
1133            addr);
1134
1135    TransitionResult result =
1136''')
1137        if self.TBEType != None and self.EntryType != None:
1138            code('doTransitionWorker(event, state, next_state, m_tbe_ptr, m_cache_entry_ptr, addr);')
1139        elif self.TBEType != None:
1140            code('doTransitionWorker(event, state, next_state, m_tbe_ptr, addr);')
1141        elif self.EntryType != None:
1142            code('doTransitionWorker(event, state, next_state, m_cache_entry_ptr, addr);')
1143        else:
1144            code('doTransitionWorker(event, state, next_state, addr);')
1145
1146        code('''
1147    if (result == TransitionResult_Valid) {
1148        DPRINTF(RubyGenerated, "next_state: %s\\n",
1149                ${ident}_State_to_string(next_state));
1150        m_profiler.countTransition(state, event);
1151        DPRINTFR(ProtocolTrace, "%15d %3s %10s%20s %6s>%-6s %s %s\\n",
1152                 curTick(), m_version, "${ident}",
1153                 ${ident}_Event_to_string(event),
1154                 ${ident}_State_to_string(state),
1155                 ${ident}_State_to_string(next_state),
1156                 addr, GET_TRANSITION_COMMENT());
1157
1158        CLEAR_TRANSITION_COMMENT();
1159''')
1160        if self.TBEType != None and self.EntryType != None:
1161            code('setState(m_tbe_ptr, m_cache_entry_ptr, addr, next_state);')
1162            code('setAccessPermission(m_cache_entry_ptr, addr, next_state);')
1163        elif self.TBEType != None:
1164            code('setState(m_tbe_ptr, addr, next_state);')
1165            code('setAccessPermission(addr, next_state);')
1166        elif self.EntryType != None:
1167            code('setState(m_cache_entry_ptr, addr, next_state);')
1168            code('setAccessPermission(m_cache_entry_ptr, addr, next_state);')
1169        else:
1170            code('setState(addr, next_state);')
1171            code('setAccessPermission(addr, next_state);')
1172
1173        code('''
1174    } else if (result == TransitionResult_ResourceStall) {
1175        DPRINTFR(ProtocolTrace, "%15s %3s %10s%20s %6s>%-6s %s %s\\n",
1176                 curTick(), m_version, "${ident}",
1177                 ${ident}_Event_to_string(event),
1178                 ${ident}_State_to_string(state),
1179                 ${ident}_State_to_string(next_state),
1180                 addr, "Resource Stall");
1181    } else if (result == TransitionResult_ProtocolStall) {
1182        DPRINTF(RubyGenerated, "stalling\\n");
1183        DPRINTFR(ProtocolTrace, "%15s %3s %10s%20s %6s>%-6s %s %s\\n",
1184                 curTick(), m_version, "${ident}",
1185                 ${ident}_Event_to_string(event),
1186                 ${ident}_State_to_string(state),
1187                 ${ident}_State_to_string(next_state),
1188                 addr, "Protocol Stall");
1189    }
1190
1191    return result;
1192}
1193
1194TransitionResult
1195${ident}_Controller::doTransitionWorker(${ident}_Event event,
1196                                        ${ident}_State state,
1197                                        ${ident}_State& next_state,
1198''')
1199
1200        if self.TBEType != None:
1201            code('''
1202                                        ${{self.TBEType.c_ident}}*& m_tbe_ptr,
1203''')
1204        if self.EntryType != None:
1205                  code('''
1206                                        ${{self.EntryType.c_ident}}*& m_cache_entry_ptr,
1207''')
1208        code('''
1209                                        const Address& addr)
1210{
1211    switch(HASH_FUN(state, event)) {
1212''')
1213
1214        # This map will allow suppress generating duplicate code
1215        cases = orderdict()
1216
1217        for trans in self.transitions:
1218            case_string = "%s_State_%s, %s_Event_%s" % \
1219                (self.ident, trans.state.ident, self.ident, trans.event.ident)
1220
1221            case = self.symtab.codeFormatter()
1222            # Only set next_state if it changes
1223            if trans.state != trans.nextState:
1224                ns_ident = trans.nextState.ident
1225                case('next_state = ${ident}_State_${ns_ident};')
1226
1227            actions = trans.actions
1228            request_types = trans.request_types
1229
1230            # Check for resources
1231            case_sorter = []
1232            res = trans.resources
1233            for key,val in res.iteritems():
1234                if key.type.ident != "DNUCAStopTable":
1235                    val = '''
1236if (!%s.areNSlotsAvailable(%s))
1237    return TransitionResult_ResourceStall;
1238''' % (key.code, val)
1239                case_sorter.append(val)
1240
1241            # Check all of the request_types for resource constraints
1242            for request_type in request_types:
1243                val = '''
1244if (!checkResourceAvailable(%s_RequestType_%s, addr)) {
1245    return TransitionResult_ResourceStall;
1246}
1247''' % (self.ident, request_type.ident)
1248                case_sorter.append(val)
1249
1250            # Emit the code sequences in a sorted order.  This makes the
1251            # output deterministic (without this the output order can vary
1252            # since Map's keys() on a vector of pointers is not deterministic
1253            for c in sorted(case_sorter):
1254                case("$c")
1255
1256            # Record access types for this transition
1257            for request_type in request_types:
1258                case('recordRequestType(${ident}_RequestType_${{request_type.ident}}, addr);')
1259
1260            # Figure out if we stall
1261            stall = False
1262            for action in actions:
1263                if action.ident == "z_stall":
1264                    stall = True
1265                    break
1266
1267            if stall:
1268                case('return TransitionResult_ProtocolStall;')
1269            else:
1270                if self.TBEType != None and self.EntryType != None:
1271                    for action in actions:
1272                        case('${{action.ident}}(m_tbe_ptr, m_cache_entry_ptr, addr);')
1273                elif self.TBEType != None:
1274                    for action in actions:
1275                        case('${{action.ident}}(m_tbe_ptr, addr);')
1276                elif self.EntryType != None:
1277                    for action in actions:
1278                        case('${{action.ident}}(m_cache_entry_ptr, addr);')
1279                else:
1280                    for action in actions:
1281                        case('${{action.ident}}(addr);')
1282                case('return TransitionResult_Valid;')
1283
1284            case = str(case)
1285
1286            # Look to see if this transition code is unique.
1287            if case not in cases:
1288                cases[case] = []
1289
1290            cases[case].append(case_string)
1291
1292        # Walk through all of the unique code blocks and spit out the
1293        # corresponding case statement elements
1294        for case,transitions in cases.iteritems():
1295            # Iterative over all the multiple transitions that share
1296            # the same code
1297            for trans in transitions:
1298                code('  case HASH_FUN($trans):')
1299            code('    $case')
1300
1301        code('''
1302      default:
1303        fatal("Invalid transition\\n"
1304              "%s time: %d addr: %s event: %s state: %s\\n",
1305              name(), g_eventQueue_ptr->getTime(), addr, event, state);
1306    }
1307    return TransitionResult_Valid;
1308}
1309''')
1310        code.write(path, "%s_Transitions.cc" % self.ident)
1311
1312    def printProfileDumperHH(self, path):
1313        code = self.symtab.codeFormatter()
1314        ident = self.ident
1315
1316        code('''
1317// Auto generated C++ code started by $__file__:$__line__
1318// ${ident}: ${{self.short}}
1319
1320#ifndef __${ident}_PROFILE_DUMPER_HH__
1321#define __${ident}_PROFILE_DUMPER_HH__
1322
1323#include <cassert>
1324#include <iostream>
1325#include <vector>
1326
1327#include "${ident}_Event.hh"
1328#include "${ident}_Profiler.hh"
1329
1330typedef std::vector<${ident}_Profiler *> ${ident}_profilers;
1331
1332class ${ident}_ProfileDumper
1333{
1334  public:
1335    ${ident}_ProfileDumper();
1336    void registerProfiler(${ident}_Profiler* profiler);
1337    void dumpStats(std::ostream& out) const;
1338
1339  private:
1340    ${ident}_profilers m_profilers;
1341};
1342
1343#endif // __${ident}_PROFILE_DUMPER_HH__
1344''')
1345        code.write(path, "%s_ProfileDumper.hh" % self.ident)
1346
1347    def printProfileDumperCC(self, path):
1348        code = self.symtab.codeFormatter()
1349        ident = self.ident
1350
1351        code('''
1352// Auto generated C++ code started by $__file__:$__line__
1353// ${ident}: ${{self.short}}
1354
1355#include "mem/protocol/${ident}_ProfileDumper.hh"
1356
1357${ident}_ProfileDumper::${ident}_ProfileDumper()
1358{
1359}
1360
1361void
1362${ident}_ProfileDumper::registerProfiler(${ident}_Profiler* profiler)
1363{
1364    m_profilers.push_back(profiler);
1365}
1366
1367void
1368${ident}_ProfileDumper::dumpStats(std::ostream& out) const
1369{
1370    out << " --- ${ident} ---\\n";
1371    out << " - Event Counts -\\n";
1372    for (${ident}_Event event = ${ident}_Event_FIRST;
1373         event < ${ident}_Event_NUM;
1374         ++event) {
1375        out << (${ident}_Event) event << " [";
1376        uint64 total = 0;
1377        for (int i = 0; i < m_profilers.size(); i++) {
1378             out << m_profilers[i]->getEventCount(event) << " ";
1379             total += m_profilers[i]->getEventCount(event);
1380        }
1381        out << "] " << total << "\\n";
1382    }
1383    out << "\\n";
1384    out << " - Transitions -\\n";
1385    for (${ident}_State state = ${ident}_State_FIRST;
1386         state < ${ident}_State_NUM;
1387         ++state) {
1388        for (${ident}_Event event = ${ident}_Event_FIRST;
1389             event < ${ident}_Event_NUM;
1390             ++event) {
1391            if (m_profilers[0]->isPossible(state, event)) {
1392                out << (${ident}_State) state << "  "
1393                    << (${ident}_Event) event << " [";
1394                uint64 total = 0;
1395                for (int i = 0; i < m_profilers.size(); i++) {
1396                     out << m_profilers[i]->getTransitionCount(state, event) << " ";
1397                     total += m_profilers[i]->getTransitionCount(state, event);
1398                }
1399                out << "] " << total << "\\n";
1400            }
1401        }
1402        out << "\\n";
1403    }
1404}
1405''')
1406        code.write(path, "%s_ProfileDumper.cc" % self.ident)
1407
1408    def printProfilerHH(self, path):
1409        code = self.symtab.codeFormatter()
1410        ident = self.ident
1411
1412        code('''
1413// Auto generated C++ code started by $__file__:$__line__
1414// ${ident}: ${{self.short}}
1415
1416#ifndef __${ident}_PROFILER_HH__
1417#define __${ident}_PROFILER_HH__
1418
1419#include <cassert>
1420#include <iostream>
1421
1422#include "mem/protocol/${ident}_Event.hh"
1423#include "mem/protocol/${ident}_State.hh"
1424#include "mem/ruby/common/TypeDefines.hh"
1425
1426class ${ident}_Profiler
1427{
1428  public:
1429    ${ident}_Profiler();
1430    void setVersion(int version);
1431    void countTransition(${ident}_State state, ${ident}_Event event);
1432    void possibleTransition(${ident}_State state, ${ident}_Event event);
1433    uint64 getEventCount(${ident}_Event event);
1434    bool isPossible(${ident}_State state, ${ident}_Event event);
1435    uint64 getTransitionCount(${ident}_State state, ${ident}_Event event);
1436    void clearStats();
1437
1438  private:
1439    int m_counters[${ident}_State_NUM][${ident}_Event_NUM];
1440    int m_event_counters[${ident}_Event_NUM];
1441    bool m_possible[${ident}_State_NUM][${ident}_Event_NUM];
1442    int m_version;
1443};
1444
1445#endif // __${ident}_PROFILER_HH__
1446''')
1447        code.write(path, "%s_Profiler.hh" % self.ident)
1448
1449    def printProfilerCC(self, path):
1450        code = self.symtab.codeFormatter()
1451        ident = self.ident
1452
1453        code('''
1454// Auto generated C++ code started by $__file__:$__line__
1455// ${ident}: ${{self.short}}
1456
1457#include <cassert>
1458
1459#include "mem/protocol/${ident}_Profiler.hh"
1460
1461${ident}_Profiler::${ident}_Profiler()
1462{
1463    for (int state = 0; state < ${ident}_State_NUM; state++) {
1464        for (int event = 0; event < ${ident}_Event_NUM; event++) {
1465            m_possible[state][event] = false;
1466            m_counters[state][event] = 0;
1467        }
1468    }
1469    for (int event = 0; event < ${ident}_Event_NUM; event++) {
1470        m_event_counters[event] = 0;
1471    }
1472}
1473
1474void
1475${ident}_Profiler::setVersion(int version)
1476{
1477    m_version = version;
1478}
1479
1480void
1481${ident}_Profiler::clearStats()
1482{
1483    for (int state = 0; state < ${ident}_State_NUM; state++) {
1484        for (int event = 0; event < ${ident}_Event_NUM; event++) {
1485            m_counters[state][event] = 0;
1486        }
1487    }
1488
1489    for (int event = 0; event < ${ident}_Event_NUM; event++) {
1490        m_event_counters[event] = 0;
1491    }
1492}
1493void
1494${ident}_Profiler::countTransition(${ident}_State state, ${ident}_Event event)
1495{
1496    assert(m_possible[state][event]);
1497    m_counters[state][event]++;
1498    m_event_counters[event]++;
1499}
1500void
1501${ident}_Profiler::possibleTransition(${ident}_State state,
1502                                      ${ident}_Event event)
1503{
1504    m_possible[state][event] = true;
1505}
1506
1507uint64
1508${ident}_Profiler::getEventCount(${ident}_Event event)
1509{
1510    return m_event_counters[event];
1511}
1512
1513bool
1514${ident}_Profiler::isPossible(${ident}_State state, ${ident}_Event event)
1515{
1516    return m_possible[state][event];
1517}
1518
1519uint64
1520${ident}_Profiler::getTransitionCount(${ident}_State state,
1521                                      ${ident}_Event event)
1522{
1523    return m_counters[state][event];
1524}
1525
1526''')
1527        code.write(path, "%s_Profiler.cc" % self.ident)
1528
1529    # **************************
1530    # ******* HTML Files *******
1531    # **************************
1532    def frameRef(self, click_href, click_target, over_href, over_num, text):
1533        code = self.symtab.codeFormatter(fix_newlines=False)
1534        code("""<A href=\"$click_href\" target=\"$click_target\" onmouseover=\"
1535    if (parent.frames[$over_num].location != parent.location + '$over_href') {
1536        parent.frames[$over_num].location='$over_href'
1537    }\">
1538    ${{html.formatShorthand(text)}}
1539    </A>""")
1540        return str(code)
1541
1542    def writeHTMLFiles(self, path):
1543        # Create table with no row hilighted
1544        self.printHTMLTransitions(path, None)
1545
1546        # Generate transition tables
1547        for state in self.states.itervalues():
1548            self.printHTMLTransitions(path, state)
1549
1550        # Generate action descriptions
1551        for action in self.actions.itervalues():
1552            name = "%s_action_%s.html" % (self.ident, action.ident)
1553            code = html.createSymbol(action, "Action")
1554            code.write(path, name)
1555
1556        # Generate state descriptions
1557        for state in self.states.itervalues():
1558            name = "%s_State_%s.html" % (self.ident, state.ident)
1559            code = html.createSymbol(state, "State")
1560            code.write(path, name)
1561
1562        # Generate event descriptions
1563        for event in self.events.itervalues():
1564            name = "%s_Event_%s.html" % (self.ident, event.ident)
1565            code = html.createSymbol(event, "Event")
1566            code.write(path, name)
1567
1568    def printHTMLTransitions(self, path, active_state):
1569        code = self.symtab.codeFormatter()
1570
1571        code('''
1572<HTML>
1573<BODY link="blue" vlink="blue">
1574
1575<H1 align="center">${{html.formatShorthand(self.short)}}:
1576''')
1577        code.indent()
1578        for i,machine in enumerate(self.symtab.getAllType(StateMachine)):
1579            mid = machine.ident
1580            if i != 0:
1581                extra = " - "
1582            else:
1583                extra = ""
1584            if machine == self:
1585                code('$extra$mid')
1586            else:
1587                code('$extra<A target="Table" href="${mid}_table.html">$mid</A>')
1588        code.dedent()
1589
1590        code("""
1591</H1>
1592
1593<TABLE border=1>
1594<TR>
1595  <TH> </TH>
1596""")
1597
1598        for event in self.events.itervalues():
1599            href = "%s_Event_%s.html" % (self.ident, event.ident)
1600            ref = self.frameRef(href, "Status", href, "1", event.short)
1601            code('<TH bgcolor=white>$ref</TH>')
1602
1603        code('</TR>')
1604        # -- Body of table
1605        for state in self.states.itervalues():
1606            # -- Each row
1607            if state == active_state:
1608                color = "yellow"
1609            else:
1610                color = "white"
1611
1612            click = "%s_table_%s.html" % (self.ident, state.ident)
1613            over = "%s_State_%s.html" % (self.ident, state.ident)
1614            text = html.formatShorthand(state.short)
1615            ref = self.frameRef(click, "Table", over, "1", state.short)
1616            code('''
1617<TR>
1618  <TH bgcolor=$color>$ref</TH>
1619''')
1620
1621            # -- One column for each event
1622            for event in self.events.itervalues():
1623                trans = self.table.get((state,event), None)
1624                if trans is None:
1625                    # This is the no transition case
1626                    if state == active_state:
1627                        color = "#C0C000"
1628                    else:
1629                        color = "lightgrey"
1630
1631                    code('<TD bgcolor=$color>&nbsp;</TD>')
1632                    continue
1633
1634                next = trans.nextState
1635                stall_action = False
1636
1637                # -- Get the actions
1638                for action in trans.actions:
1639                    if action.ident == "z_stall" or \
1640                       action.ident == "zz_recycleMandatoryQueue":
1641                        stall_action = True
1642
1643                # -- Print out "actions/next-state"
1644                if stall_action:
1645                    if state == active_state:
1646                        color = "#C0C000"
1647                    else:
1648                        color = "lightgrey"
1649
1650                elif active_state and next.ident == active_state.ident:
1651                    color = "aqua"
1652                elif state == active_state:
1653                    color = "yellow"
1654                else:
1655                    color = "white"
1656
1657                code('<TD bgcolor=$color>')
1658                for action in trans.actions:
1659                    href = "%s_action_%s.html" % (self.ident, action.ident)
1660                    ref = self.frameRef(href, "Status", href, "1",
1661                                        action.short)
1662                    code('  $ref')
1663                if next != state:
1664                    if trans.actions:
1665                        code('/')
1666                    click = "%s_table_%s.html" % (self.ident, next.ident)
1667                    over = "%s_State_%s.html" % (self.ident, next.ident)
1668                    ref = self.frameRef(click, "Table", over, "1", next.short)
1669                    code("$ref")
1670                code("</TD>")
1671
1672            # -- Each row
1673            if state == active_state:
1674                color = "yellow"
1675            else:
1676                color = "white"
1677
1678            click = "%s_table_%s.html" % (self.ident, state.ident)
1679            over = "%s_State_%s.html" % (self.ident, state.ident)
1680            ref = self.frameRef(click, "Table", over, "1", state.short)
1681            code('''
1682  <TH bgcolor=$color>$ref</TH>
1683</TR>
1684''')
1685        code('''
1686<!- Column footer->
1687<TR>
1688  <TH> </TH>
1689''')
1690
1691        for event in self.events.itervalues():
1692            href = "%s_Event_%s.html" % (self.ident, event.ident)
1693            ref = self.frameRef(href, "Status", href, "1", event.short)
1694            code('<TH bgcolor=white>$ref</TH>')
1695        code('''
1696</TR>
1697</TABLE>
1698</BODY></HTML>
1699''')
1700
1701
1702        if active_state:
1703            name = "%s_table_%s.html" % (self.ident, active_state.ident)
1704        else:
1705            name = "%s_table.html" % self.ident
1706        code.write(path, name)
1707
1708__all__ = [ "StateMachine" ]
1709