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