StateMachine.py revision 8229:78bf55f23338
16145SN/A# Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
26386SN/A# Copyright (c) 2009 The Hewlett-Packard Development Company
36386SN/A# All rights reserved.
46386SN/A#
56386SN/A# Redistribution and use in source and binary forms, with or without
66386SN/A# modification, are permitted provided that the following conditions are
76386SN/A# met: redistributions of source code must retain the above copyright
86386SN/A# notice, this list of conditions and the following disclaimer;
96386SN/A# redistributions in binary form must reproduce the above copyright
106386SN/A# notice, this list of conditions and the following disclaimer in the
116386SN/A# documentation and/or other materials provided with the distribution;
126386SN/A# neither the name of the copyright holders nor the names of its
136386SN/A# contributors may be used to endorse or promote products derived from
146386SN/A# this software without specific prior written permission.
156386SN/A#
166386SN/A# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
176386SN/A# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
186386SN/A# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
196386SN/A# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
206386SN/A# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
216386SN/A# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
226386SN/A# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
236386SN/A# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
246386SN/A# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
256386SN/A# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
266386SN/A# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
276386SN/A
286145SN/Afrom m5.util import orderdict
297553SN/A
307553SN/Afrom slicc.symbols.Symbol import Symbol
317553SN/Afrom slicc.symbols.Var import Var
327553SN/Aimport slicc.generate.html as html
337553SN/Aimport re
346145SN/A
357553SN/Apython_class_map = {"int": "Int",
367553SN/A                    "std::string": "String",
376145SN/A                    "bool": "Bool",
388229Snate@binkert.org                    "CacheMemory": "RubyCache",
397632SBrad.Beckmann@amd.com                    "WireBuffer": "RubyWireBuffer",
407553SN/A                    "Sequencer": "RubySequencer",
417553SN/A                    "DirectoryMemory": "RubyDirectoryMemory",
426145SN/A                    "MemoryControl": "RubyMemoryControl",
437553SN/A                    "DMASequencer": "DMASequencer"
447553SN/A                    }
457553SN/A
467553SN/Aclass StateMachine(Symbol):
477553SN/A    def __init__(self, symtab, ident, location, pairs, config_parameters):
487553SN/A        super(StateMachine, self).__init__(symtab, ident, location, pairs)
497553SN/A        self.table = None
507553SN/A        self.config_parameters = config_parameters
517553SN/A
528655Sandreas.hansson@arm.com        for param in config_parameters:
537553SN/A            if param.pointer:
547553SN/A                var = Var(symtab, param.name, location, param.type_ast.type,
557553SN/A                          "(*m_%s_ptr)" % param.name, {}, self)
567553SN/A            else:
578655Sandreas.hansson@arm.com                var = Var(symtab, param.name, location, param.type_ast.type,
588655Sandreas.hansson@arm.com                          "m_%s" % param.name, {}, self)
598655Sandreas.hansson@arm.com            self.symtab.registerSym(param.name, var)
606145SN/A
616145SN/A        self.states = orderdict()
627553SN/A        self.events = orderdict()
636145SN/A        self.actions = orderdict()
64        self.transitions = []
65        self.in_ports = []
66        self.functions = []
67        self.objects = []
68        self.TBEType   = None
69        self.EntryType = None
70
71        self.message_buffer_names = []
72
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        for func in self.functions:
172            func.writeCodeFiles(path)
173
174    def printControllerPython(self, path):
175        code = self.symtab.codeFormatter()
176        ident = self.ident
177        py_ident = "%s_Controller" % ident
178        c_ident = "%s_Controller" % self.ident
179        code('''
180from m5.params import *
181from m5.SimObject import SimObject
182from Controller import RubyController
183
184class $py_ident(RubyController):
185    type = '$py_ident'
186''')
187        code.indent()
188        for param in self.config_parameters:
189            dflt_str = ''
190            if param.default is not None:
191                dflt_str = str(param.default) + ', '
192            if python_class_map.has_key(param.type_ast.type.c_ident):
193                python_type = python_class_map[param.type_ast.type.c_ident]
194                code('${{param.name}} = Param.${{python_type}}(${dflt_str}"")')
195            else:
196                self.error("Unknown c++ to python class conversion for c++ " \
197                           "type: '%s'. Please update the python_class_map " \
198                           "in StateMachine.py", param.type_ast.type.c_ident)
199        code.dedent()
200        code.write(path, '%s.py' % py_ident)
201
202
203    def printControllerHH(self, path):
204        '''Output the method declarations for the class declaration'''
205        code = self.symtab.codeFormatter()
206        ident = self.ident
207        c_ident = "%s_Controller" % self.ident
208
209        self.message_buffer_names = []
210
211        code('''
212/** \\file $c_ident.hh
213 *
214 * Auto generated C++ code started by $__file__:$__line__
215 * Created by slicc definition of Module "${{self.short}}"
216 */
217
218#ifndef __${ident}_CONTROLLER_HH__
219#define __${ident}_CONTROLLER_HH__
220
221#include <iostream>
222#include <sstream>
223#include <string>
224
225#include "mem/protocol/${ident}_ProfileDumper.hh"
226#include "mem/protocol/${ident}_Profiler.hh"
227#include "mem/protocol/TransitionResult.hh"
228#include "mem/protocol/Types.hh"
229#include "mem/ruby/common/Consumer.hh"
230#include "mem/ruby/common/Global.hh"
231#include "mem/ruby/slicc_interface/AbstractController.hh"
232#include "params/$c_ident.hh"
233''')
234
235        seen_types = set()
236        for var in self.objects:
237            if var.type.ident not in seen_types and not var.type.isPrimitive:
238                code('#include "mem/protocol/${{var.type.c_ident}}.hh"')
239            seen_types.add(var.type.ident)
240
241        # for adding information to the protocol debug trace
242        code('''
243extern std::stringstream ${ident}_transitionComment;
244
245class $c_ident : public AbstractController
246{
247// the coherence checker needs to call isBlockExclusive() and isBlockShared()
248// making the Chip a friend class is an easy way to do this for now
249
250public:
251    typedef ${c_ident}Params Params;
252    $c_ident(const Params *p);
253    static int getNumControllers();
254    void init();
255    MessageBuffer* getMandatoryQueue() const;
256    const int & getVersion() const;
257    const std::string toString() const;
258    const std::string getName() const;
259    const MachineType getMachineType() const;
260    void stallBuffer(MessageBuffer* buf, Address addr);
261    void wakeUpBuffers(Address addr);
262    void wakeUpAllBuffers();
263    void initNetworkPtr(Network* net_ptr) { m_net_ptr = net_ptr; }
264    void print(std::ostream& out) const;
265    void printConfig(std::ostream& out) const;
266    void wakeup();
267    void printStats(std::ostream& out) const;
268    void clearStats();
269    void blockOnQueue(Address addr, MessageBuffer* port);
270    void unblock(Address addr);
271
272private:
273''')
274
275        code.indent()
276        # added by SS
277        for param in self.config_parameters:
278            if param.pointer:
279                code('${{param.type_ast.type}}* m_${{param.ident}}_ptr;')
280            else:
281                code('${{param.type_ast.type}} m_${{param.ident}};')
282
283        code('''
284int m_number_of_TBEs;
285
286TransitionResult doTransition(${ident}_Event event,
287''')
288
289        if self.EntryType != None:
290            code('''
291                              ${{self.EntryType.c_ident}}* m_cache_entry_ptr,
292''')
293        if self.TBEType != None:
294            code('''
295                              ${{self.TBEType.c_ident}}* m_tbe_ptr,
296''')
297
298        code('''
299                              const Address& addr);
300
301TransitionResult doTransitionWorker(${ident}_Event event,
302                                    ${ident}_State state,
303                                    ${ident}_State& next_state,
304''')
305
306        if self.TBEType != None:
307            code('''
308                                    ${{self.TBEType.c_ident}}*& m_tbe_ptr,
309''')
310        if self.EntryType != None:
311            code('''
312                                    ${{self.EntryType.c_ident}}*& m_cache_entry_ptr,
313''')
314
315        code('''
316                                    const Address& addr);
317
318std::string m_name;
319int m_transitions_per_cycle;
320int m_buffer_size;
321int m_recycle_latency;
322std::map<std::string, std::string> m_cfg;
323NodeID m_version;
324Network* m_net_ptr;
325MachineID m_machineID;
326bool m_is_blocking;
327std::map<Address, MessageBuffer*> m_block_map;
328typedef std::vector<MessageBuffer*> MsgVecType;
329typedef m5::hash_map< Address, MsgVecType* > WaitingBufType;
330WaitingBufType m_waiting_buffers;
331int m_max_in_port_rank;
332int m_cur_in_port_rank;
333static ${ident}_ProfileDumper s_profileDumper;
334${ident}_Profiler m_profiler;
335static int m_num_controllers;
336
337// Internal functions
338''')
339
340        for func in self.functions:
341            proto = func.prototype
342            if proto:
343                code('$proto')
344
345        if self.EntryType != None:
346            code('''
347
348// Set and Reset for cache_entry variable
349void set_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, AbstractCacheEntry* m_new_cache_entry);
350void unset_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr);
351// Set permissions for the cache_entry
352void set_permission(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, AccessPermission perm);
353''')
354
355        if self.TBEType != None:
356            code('''
357
358// Set and Reset for tbe variable
359void set_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${ident}_TBE* m_new_tbe);
360void unset_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr);
361''')
362
363        code('''
364
365// Actions
366''')
367        if self.TBEType != None and self.EntryType != 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, ${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr);')
371        elif self.TBEType != None:
372            for action in self.actions.itervalues():
373                code('/** \\brief ${{action.desc}} */')
374                code('void ${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, const Address& addr);')
375        elif self.EntryType != None:
376            for action in self.actions.itervalues():
377                code('/** \\brief ${{action.desc}} */')
378                code('void ${{action.ident}}(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr);')
379        else:
380            for action in self.actions.itervalues():
381                code('/** \\brief ${{action.desc}} */')
382                code('void ${{action.ident}}(const Address& addr);')
383
384        # the controller internal variables
385        code('''
386
387// Objects
388''')
389        for var in self.objects:
390            th = var.get("template_hack", "")
391            code('${{var.type.c_ident}}$th* m_${{var.c_ident}}_ptr;')
392
393            if var.type.ident == "MessageBuffer":
394                self.message_buffer_names.append("m_%s_ptr" % var.c_ident)
395
396        code.dedent()
397        code('};')
398        code('#endif // __${ident}_CONTROLLER_H__')
399        code.write(path, '%s.hh' % c_ident)
400
401    def printControllerCC(self, path):
402        '''Output the actions for performing the actions'''
403
404        code = self.symtab.codeFormatter()
405        ident = self.ident
406        c_ident = "%s_Controller" % self.ident
407
408        code('''
409/** \\file $c_ident.cc
410 *
411 * Auto generated C++ code started by $__file__:$__line__
412 * Created by slicc definition of Module "${{self.short}}"
413 */
414
415#include <cassert>
416#include <sstream>
417#include <string>
418
419#include "base/cprintf.hh"
420#include "mem/protocol/${ident}_Controller.hh"
421#include "mem/protocol/${ident}_Event.hh"
422#include "mem/protocol/${ident}_State.hh"
423#include "mem/protocol/Types.hh"
424#include "mem/ruby/common/Global.hh"
425#include "mem/ruby/slicc_interface/RubySlicc_includes.hh"
426#include "mem/ruby/system/System.hh"
427
428using namespace std;
429''')
430
431        # include object classes
432        seen_types = set()
433        for var in self.objects:
434            if var.type.ident not in seen_types and not var.type.isPrimitive:
435                code('#include "mem/protocol/${{var.type.c_ident}}.hh"')
436            seen_types.add(var.type.ident)
437
438        code('''
439$c_ident *
440${c_ident}Params::create()
441{
442    return new $c_ident(this);
443}
444
445int $c_ident::m_num_controllers = 0;
446${ident}_ProfileDumper $c_ident::s_profileDumper;
447
448// for adding information to the protocol debug trace
449stringstream ${ident}_transitionComment;
450#define APPEND_TRANSITION_COMMENT(str) (${ident}_transitionComment << str)
451
452/** \\brief constructor */
453$c_ident::$c_ident(const Params *p)
454    : AbstractController(p)
455{
456    m_version = p->version;
457    m_transitions_per_cycle = p->transitions_per_cycle;
458    m_buffer_size = p->buffer_size;
459    m_recycle_latency = p->recycle_latency;
460    m_number_of_TBEs = p->number_of_TBEs;
461    m_is_blocking = false;
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
596                assert var.machine is not None
597                code('''
598machine_type = string_to_MachineType("${{var.machine.ident}}");
599base = MachineType_base_number(machine_type);
600$vid = m_net_ptr->get${network}NetQueue(m_version + base, $ordered, $vnet);
601''')
602
603                code('assert($vid != NULL);')
604
605                # Set ordering
606                if "ordered" in var:
607                    # A buffer
608                    code('$vid->setOrdering(${{var["ordered"]}});')
609
610                # Set randomization
611                if "random" in var:
612                    # A buffer
613                    code('$vid->setRandomization(${{var["random"]}});')
614
615                # Set Priority
616                if "rank" in var:
617                    code('$vid->setPriority(${{var["rank"]}})')
618
619                # Set buffer size
620                if vtype.isBuffer:
621                    code('''
622if (m_buffer_size > 0) {
623    $vid->resize(m_buffer_size);
624}
625''')
626
627                # set description (may be overriden later by port def)
628                code('''
629$vid->setDescription("[Version " + to_string(m_version) + ", ${ident}, name=${{var.c_ident}}]");
630
631''')
632
633            if vtype.isBuffer:
634                if "recycle_latency" in var:
635                    code('$vid->setRecycleLatency(${{var["recycle_latency"]}});')
636                else:
637                    code('$vid->setRecycleLatency(m_recycle_latency);')
638
639
640        # Set the queue consumers
641        code()
642        for port in self.in_ports:
643            code('${{port.code}}.setConsumer(this);')
644
645        # Set the queue descriptions
646        code()
647        for port in self.in_ports:
648            code('${{port.code}}.setDescription("[Version " + to_string(m_version) + ", $ident, $port]");')
649
650        # Initialize the transition profiling
651        code()
652        for trans in self.transitions:
653            # Figure out if we stall
654            stall = False
655            for action in trans.actions:
656                if action.ident == "z_stall":
657                    stall = True
658
659            # Only possible if it is not a 'z' case
660            if not stall:
661                state = "%s_State_%s" % (self.ident, trans.state.ident)
662                event = "%s_Event_%s" % (self.ident, trans.event.ident)
663                code('m_profiler.possibleTransition($state, $event);')
664
665        code.dedent()
666        code('}')
667
668        has_mandatory_q = False
669        for port in self.in_ports:
670            if port.code.find("mandatoryQueue_ptr") >= 0:
671                has_mandatory_q = True
672
673        if has_mandatory_q:
674            mq_ident = "m_%s_mandatoryQueue_ptr" % self.ident
675        else:
676            mq_ident = "NULL"
677
678        code('''
679int
680$c_ident::getNumControllers()
681{
682    return m_num_controllers;
683}
684
685MessageBuffer*
686$c_ident::getMandatoryQueue() const
687{
688    return $mq_ident;
689}
690
691const int &
692$c_ident::getVersion() const
693{
694    return m_version;
695}
696
697const string
698$c_ident::toString() const
699{
700    return "$c_ident";
701}
702
703const string
704$c_ident::getName() const
705{
706    return m_name;
707}
708
709const MachineType
710$c_ident::getMachineType() const
711{
712    return MachineType_${ident};
713}
714
715void
716$c_ident::stallBuffer(MessageBuffer* buf, Address addr)
717{
718    if (m_waiting_buffers.count(addr) == 0) {
719        MsgVecType* msgVec = new MsgVecType;
720        msgVec->resize(m_max_in_port_rank, NULL);
721        m_waiting_buffers[addr] = msgVec;
722    }
723    (*(m_waiting_buffers[addr]))[m_cur_in_port_rank] = buf;
724}
725
726void
727$c_ident::wakeUpBuffers(Address addr)
728{
729    if (m_waiting_buffers.count(addr) > 0) {
730        //
731        // Wake up all possible lower rank (i.e. lower priority) buffers that could
732        // be waiting on this message.
733        //
734        for (int in_port_rank = m_cur_in_port_rank - 1;
735             in_port_rank >= 0;
736             in_port_rank--) {
737            if ((*(m_waiting_buffers[addr]))[in_port_rank] != NULL) {
738                (*(m_waiting_buffers[addr]))[in_port_rank]->reanalyzeMessages(addr);
739            }
740        }
741        delete m_waiting_buffers[addr];
742        m_waiting_buffers.erase(addr);
743    }
744}
745
746void
747$c_ident::wakeUpAllBuffers()
748{
749    //
750    // Wake up all possible buffers that could be waiting on any message.
751    //
752
753    std::vector<MsgVecType*> wokeUpMsgVecs;
754
755    if(m_waiting_buffers.size() > 0) {
756        for (WaitingBufType::iterator buf_iter = m_waiting_buffers.begin();
757             buf_iter != m_waiting_buffers.end();
758             ++buf_iter) {
759             for (MsgVecType::iterator vec_iter = buf_iter->second->begin();
760                  vec_iter != buf_iter->second->end();
761                  ++vec_iter) {
762                  if (*vec_iter != NULL) {
763                      (*vec_iter)->reanalyzeAllMessages();
764                  }
765             }
766             wokeUpMsgVecs.push_back(buf_iter->second);
767        }
768
769        for (std::vector<MsgVecType*>::iterator wb_iter = wokeUpMsgVecs.begin();
770             wb_iter != wokeUpMsgVecs.end();
771             ++wb_iter) {
772             delete (*wb_iter);
773        }
774
775        m_waiting_buffers.clear();
776    }
777}
778
779void
780$c_ident::blockOnQueue(Address addr, MessageBuffer* port)
781{
782    m_is_blocking = true;
783    m_block_map[addr] = port;
784}
785
786void
787$c_ident::unblock(Address addr)
788{
789    m_block_map.erase(addr);
790    if (m_block_map.size() == 0) {
791       m_is_blocking = false;
792    }
793}
794
795void
796$c_ident::print(ostream& out) const
797{
798    out << "[$c_ident " << m_version << "]";
799}
800
801void
802$c_ident::printConfig(ostream& out) const
803{
804    out << "$c_ident config: " << m_name << endl;
805    out << "  version: " << m_version << endl;
806    map<string, string>::const_iterator it;
807    for (it = m_cfg.begin(); it != m_cfg.end(); it++)
808        out << "  " << it->first << ": " << it->second << endl;
809}
810
811void
812$c_ident::printStats(ostream& out) const
813{
814''')
815        #
816        # Cache and Memory Controllers have specific profilers associated with
817        # them.  Print out these stats before dumping state transition stats.
818        #
819        for param in self.config_parameters:
820            if param.type_ast.type.ident == "CacheMemory" or \
821               param.type_ast.type.ident == "DirectoryMemory" or \
822                   param.type_ast.type.ident == "MemoryControl":
823                assert(param.pointer)
824                code('    m_${{param.ident}}_ptr->printStats(out);')
825
826        code('''
827    if (m_version == 0) {
828        s_profileDumper.dumpStats(out);
829    }
830}
831
832void $c_ident::clearStats() {
833''')
834        #
835        # Cache and Memory Controllers have specific profilers associated with
836        # them.  These stats must be cleared too.
837        #
838        for param in self.config_parameters:
839            if param.type_ast.type.ident == "CacheMemory" or \
840                   param.type_ast.type.ident == "MemoryControl":
841                assert(param.pointer)
842                code('    m_${{param.ident}}_ptr->clearStats();')
843
844        code('''
845    m_profiler.clearStats();
846}
847''')
848
849        if self.EntryType != None:
850            code('''
851
852// Set and Reset for cache_entry variable
853void
854$c_ident::set_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, AbstractCacheEntry* m_new_cache_entry)
855{
856  m_cache_entry_ptr = (${{self.EntryType.c_ident}}*)m_new_cache_entry;
857}
858
859void
860$c_ident::unset_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr)
861{
862  m_cache_entry_ptr = 0;
863}
864
865void
866$c_ident::set_permission(${{self.EntryType.c_ident}}*& m_cache_entry_ptr,
867                         AccessPermission perm)
868{
869    if (m_cache_entry_ptr != NULL) {
870       m_cache_entry_ptr->changePermission(perm);
871    }
872}
873''')
874
875        if self.TBEType != None:
876            code('''
877
878// Set and Reset for tbe variable
879void
880$c_ident::set_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.TBEType.c_ident}}* m_new_tbe)
881{
882  m_tbe_ptr = m_new_tbe;
883}
884
885void
886$c_ident::unset_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr)
887{
888  m_tbe_ptr = NULL;
889}
890''')
891
892        code('''
893
894// Actions
895''')
896        if self.TBEType != None and self.EntryType != None:
897            for action in self.actions.itervalues():
898                if "c_code" not in action:
899                 continue
900
901                code('''
902/** \\brief ${{action.desc}} */
903void
904$c_ident::${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr)
905{
906    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
907    ${{action["c_code"]}}
908}
909
910''')
911        elif self.TBEType != 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, const Address& addr)
920{
921    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
922    ${{action["c_code"]}}
923}
924
925''')
926        elif 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.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        else:
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}}(const Address& addr)
950{
951    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
952    ${{action["c_code"]}}
953}
954
955''')
956        code.write(path, "%s.cc" % c_ident)
957
958    def printCWakeup(self, path):
959        '''Output the wakeup loop for the events'''
960
961        code = self.symtab.codeFormatter()
962        ident = self.ident
963
964        code('''
965// Auto generated C++ code started by $__file__:$__line__
966// ${ident}: ${{self.short}}
967
968#include <cassert>
969
970#include "base/misc.hh"
971#include "mem/protocol/${ident}_Controller.hh"
972#include "mem/protocol/${ident}_Event.hh"
973#include "mem/protocol/${ident}_State.hh"
974#include "mem/protocol/Types.hh"
975#include "mem/ruby/common/Global.hh"
976#include "mem/ruby/slicc_interface/RubySlicc_includes.hh"
977#include "mem/ruby/system/System.hh"
978
979using namespace std;
980
981void
982${ident}_Controller::wakeup()
983{
984    int counter = 0;
985    while (true) {
986        // Some cases will put us into an infinite loop without this limit
987        assert(counter <= m_transitions_per_cycle);
988        if (counter == m_transitions_per_cycle) {
989            // Count how often we are fully utilized
990            g_system_ptr->getProfiler()->controllerBusy(m_machineID);
991
992            // Wakeup in another cycle and try again
993            g_eventQueue_ptr->scheduleEvent(this, 1);
994            break;
995        }
996''')
997
998        code.indent()
999        code.indent()
1000
1001        # InPorts
1002        #
1003        for port in self.in_ports:
1004            code.indent()
1005            code('// ${ident}InPort $port')
1006            if port.pairs.has_key("rank"):
1007                code('m_cur_in_port_rank = ${{port.pairs["rank"]}};')
1008            else:
1009                code('m_cur_in_port_rank = 0;')
1010            code('${{port["c_code_in_port"]}}')
1011            code.dedent()
1012
1013            code('')
1014
1015        code.dedent()
1016        code.dedent()
1017        code('''
1018        break;  // If we got this far, we have nothing left todo
1019    }
1020    // g_eventQueue_ptr->scheduleEvent(this, 1);
1021}
1022''')
1023
1024        code.write(path, "%s_Wakeup.cc" % self.ident)
1025
1026    def printCSwitch(self, path):
1027        '''Output switch statement for transition table'''
1028
1029        code = self.symtab.codeFormatter()
1030        ident = self.ident
1031
1032        code('''
1033// Auto generated C++ code started by $__file__:$__line__
1034// ${ident}: ${{self.short}}
1035
1036#include <cassert>
1037
1038#include "base/misc.hh"
1039#include "base/trace.hh"
1040#include "mem/protocol/${ident}_Controller.hh"
1041#include "mem/protocol/${ident}_Event.hh"
1042#include "mem/protocol/${ident}_State.hh"
1043#include "mem/protocol/Types.hh"
1044#include "mem/ruby/common/Global.hh"
1045#include "mem/ruby/system/System.hh"
1046
1047#define HASH_FUN(state, event)  ((int(state)*${ident}_Event_NUM)+int(event))
1048
1049#define GET_TRANSITION_COMMENT() (${ident}_transitionComment.str())
1050#define CLEAR_TRANSITION_COMMENT() (${ident}_transitionComment.str(""))
1051
1052TransitionResult
1053${ident}_Controller::doTransition(${ident}_Event event,
1054''')
1055        if self.EntryType != None:
1056            code('''
1057                                  ${{self.EntryType.c_ident}}* m_cache_entry_ptr,
1058''')
1059        if self.TBEType != None:
1060            code('''
1061                                  ${{self.TBEType.c_ident}}* m_tbe_ptr,
1062''')
1063        code('''
1064                                  const Address &addr)
1065{
1066''')
1067        if self.TBEType != None and self.EntryType != None:
1068            code('${ident}_State state = ${ident}_getState(m_tbe_ptr, m_cache_entry_ptr, addr);')
1069        elif self.TBEType != None:
1070            code('${ident}_State state = ${ident}_getState(m_tbe_ptr, addr);')
1071        elif self.EntryType != None:
1072            code('${ident}_State state = ${ident}_getState(m_cache_entry_ptr, addr);')
1073        else:
1074            code('${ident}_State state = ${ident}_getState(addr);')
1075
1076        code('''
1077    ${ident}_State next_state = state;
1078
1079    DPRINTF(RubyGenerated, "%s, Time: %lld, state: %s, event: %s, addr: %s\\n",
1080            *this,
1081            g_eventQueue_ptr->getTime(),
1082            ${ident}_State_to_string(state),
1083            ${ident}_Event_to_string(event),
1084            addr);
1085
1086    TransitionResult result =
1087''')
1088        if self.TBEType != None and self.EntryType != None:
1089            code('doTransitionWorker(event, state, next_state, m_tbe_ptr, m_cache_entry_ptr, addr);')
1090        elif self.TBEType != None:
1091            code('doTransitionWorker(event, state, next_state, m_tbe_ptr, addr);')
1092        elif self.EntryType != None:
1093            code('doTransitionWorker(event, state, next_state, m_cache_entry_ptr, addr);')
1094        else:
1095            code('doTransitionWorker(event, state, next_state, addr);')
1096
1097        code('''
1098    if (result == TransitionResult_Valid) {
1099        DPRINTF(RubyGenerated, "next_state: %s\\n",
1100                ${ident}_State_to_string(next_state));
1101        m_profiler.countTransition(state, event);
1102        DPRINTFR(ProtocolTrace, "%7d %3s %10s%20s %6s>%-6s %s %s\\n",
1103            g_eventQueue_ptr->getTime(), m_version, "${ident}",
1104            ${ident}_Event_to_string(event),
1105            ${ident}_State_to_string(state),
1106            ${ident}_State_to_string(next_state),
1107            addr, GET_TRANSITION_COMMENT());
1108
1109        CLEAR_TRANSITION_COMMENT();
1110''')
1111        if self.TBEType != None and self.EntryType != None:
1112            code('${ident}_setState(m_tbe_ptr, m_cache_entry_ptr, addr, next_state);')
1113            code('set_permission(m_cache_entry_ptr, ${ident}_State_to_permission(next_state));')
1114        elif self.TBEType != None:
1115            code('${ident}_setState(m_tbe_ptr, addr, next_state);')
1116        elif self.EntryType != None:
1117            code('${ident}_setState(m_cache_entry_ptr, addr, next_state);')
1118            code('set_permission(m_cache_entry_ptr, ${ident}_State_to_permission(next_state));')
1119        else:
1120            code('${ident}_setState(addr, next_state);')
1121
1122        code('''
1123    } else if (result == TransitionResult_ResourceStall) {
1124        DPRINTFR(ProtocolTrace, "%7s %3s %10s%20s %6s>%-6s %s %s\\n",
1125            g_eventQueue_ptr->getTime(), m_version, "${ident}",
1126            ${ident}_Event_to_string(event),
1127            ${ident}_State_to_string(state),
1128            ${ident}_State_to_string(next_state),
1129            addr, "Resource Stall");
1130    } else if (result == TransitionResult_ProtocolStall) {
1131        DPRINTF(RubyGenerated, "stalling\\n");
1132        DPRINTFR(ProtocolTrace, "%7s %3s %10s%20s %6s>%-6s %s %s\\n",
1133            g_eventQueue_ptr->getTime(), m_version, "${ident}",
1134            ${ident}_Event_to_string(event),
1135            ${ident}_State_to_string(state),
1136            ${ident}_State_to_string(next_state),
1137            addr, "Protocol Stall");
1138    }
1139
1140    return result;
1141}
1142
1143TransitionResult
1144${ident}_Controller::doTransitionWorker(${ident}_Event event,
1145                                        ${ident}_State state,
1146                                        ${ident}_State& next_state,
1147''')
1148
1149        if self.TBEType != None:
1150            code('''
1151                                        ${{self.TBEType.c_ident}}*& m_tbe_ptr,
1152''')
1153        if self.EntryType != None:
1154                  code('''
1155                                        ${{self.EntryType.c_ident}}*& m_cache_entry_ptr,
1156''')
1157        code('''
1158                                        const Address& addr)
1159{
1160    switch(HASH_FUN(state, event)) {
1161''')
1162
1163        # This map will allow suppress generating duplicate code
1164        cases = orderdict()
1165
1166        for trans in self.transitions:
1167            case_string = "%s_State_%s, %s_Event_%s" % \
1168                (self.ident, trans.state.ident, self.ident, trans.event.ident)
1169
1170            case = self.symtab.codeFormatter()
1171            # Only set next_state if it changes
1172            if trans.state != trans.nextState:
1173                ns_ident = trans.nextState.ident
1174                case('next_state = ${ident}_State_${ns_ident};')
1175
1176            actions = trans.actions
1177
1178            # Check for resources
1179            case_sorter = []
1180            res = trans.resources
1181            for key,val in res.iteritems():
1182                if key.type.ident != "DNUCAStopTable":
1183                    val = '''
1184if (!%s.areNSlotsAvailable(%s))
1185    return TransitionResult_ResourceStall;
1186''' % (key.code, val)
1187                case_sorter.append(val)
1188
1189
1190            # Emit the code sequences in a sorted order.  This makes the
1191            # output deterministic (without this the output order can vary
1192            # since Map's keys() on a vector of pointers is not deterministic
1193            for c in sorted(case_sorter):
1194                case("$c")
1195
1196            # Figure out if we stall
1197            stall = False
1198            for action in actions:
1199                if action.ident == "z_stall":
1200                    stall = True
1201                    break
1202
1203            if stall:
1204                case('return TransitionResult_ProtocolStall;')
1205            else:
1206                if self.TBEType != None and self.EntryType != None:
1207                    for action in actions:
1208                        case('${{action.ident}}(m_tbe_ptr, m_cache_entry_ptr, addr);')
1209                elif self.TBEType != None:
1210                    for action in actions:
1211                        case('${{action.ident}}(m_tbe_ptr, addr);')
1212                elif self.EntryType != None:
1213                    for action in actions:
1214                        case('${{action.ident}}(m_cache_entry_ptr, addr);')
1215                else:
1216                    for action in actions:
1217                        case('${{action.ident}}(addr);')
1218                case('return TransitionResult_Valid;')
1219
1220            case = str(case)
1221
1222            # Look to see if this transition code is unique.
1223            if case not in cases:
1224                cases[case] = []
1225
1226            cases[case].append(case_string)
1227
1228        # Walk through all of the unique code blocks and spit out the
1229        # corresponding case statement elements
1230        for case,transitions in cases.iteritems():
1231            # Iterative over all the multiple transitions that share
1232            # the same code
1233            for trans in transitions:
1234                code('  case HASH_FUN($trans):')
1235            code('    $case')
1236
1237        code('''
1238      default:
1239        fatal("Invalid transition\\n"
1240              "%s time: %d addr: %s event: %s state: %s\\n",
1241              name(), g_eventQueue_ptr->getTime(), addr, event, state);
1242    }
1243    return TransitionResult_Valid;
1244}
1245''')
1246        code.write(path, "%s_Transitions.cc" % self.ident)
1247
1248    def printProfileDumperHH(self, path):
1249        code = self.symtab.codeFormatter()
1250        ident = self.ident
1251
1252        code('''
1253// Auto generated C++ code started by $__file__:$__line__
1254// ${ident}: ${{self.short}}
1255
1256#ifndef __${ident}_PROFILE_DUMPER_HH__
1257#define __${ident}_PROFILE_DUMPER_HH__
1258
1259#include <cassert>
1260#include <iostream>
1261#include <vector>
1262
1263#include "${ident}_Event.hh"
1264#include "${ident}_Profiler.hh"
1265
1266typedef std::vector<${ident}_Profiler *> ${ident}_profilers;
1267
1268class ${ident}_ProfileDumper
1269{
1270  public:
1271    ${ident}_ProfileDumper();
1272    void registerProfiler(${ident}_Profiler* profiler);
1273    void dumpStats(std::ostream& out) const;
1274
1275  private:
1276    ${ident}_profilers m_profilers;
1277};
1278
1279#endif // __${ident}_PROFILE_DUMPER_HH__
1280''')
1281        code.write(path, "%s_ProfileDumper.hh" % self.ident)
1282
1283    def printProfileDumperCC(self, path):
1284        code = self.symtab.codeFormatter()
1285        ident = self.ident
1286
1287        code('''
1288// Auto generated C++ code started by $__file__:$__line__
1289// ${ident}: ${{self.short}}
1290
1291#include "mem/protocol/${ident}_ProfileDumper.hh"
1292
1293${ident}_ProfileDumper::${ident}_ProfileDumper()
1294{
1295}
1296
1297void
1298${ident}_ProfileDumper::registerProfiler(${ident}_Profiler* profiler)
1299{
1300    m_profilers.push_back(profiler);
1301}
1302
1303void
1304${ident}_ProfileDumper::dumpStats(std::ostream& out) const
1305{
1306    out << " --- ${ident} ---\\n";
1307    out << " - Event Counts -\\n";
1308    for (${ident}_Event event = ${ident}_Event_FIRST;
1309         event < ${ident}_Event_NUM;
1310         ++event) {
1311        out << (${ident}_Event) event << " [";
1312        uint64 total = 0;
1313        for (int i = 0; i < m_profilers.size(); i++) {
1314             out << m_profilers[i]->getEventCount(event) << " ";
1315             total += m_profilers[i]->getEventCount(event);
1316        }
1317        out << "] " << total << "\\n";
1318    }
1319    out << "\\n";
1320    out << " - Transitions -\\n";
1321    for (${ident}_State state = ${ident}_State_FIRST;
1322         state < ${ident}_State_NUM;
1323         ++state) {
1324        for (${ident}_Event event = ${ident}_Event_FIRST;
1325             event < ${ident}_Event_NUM;
1326             ++event) {
1327            if (m_profilers[0]->isPossible(state, event)) {
1328                out << (${ident}_State) state << "  "
1329                    << (${ident}_Event) event << " [";
1330                uint64 total = 0;
1331                for (int i = 0; i < m_profilers.size(); i++) {
1332                     out << m_profilers[i]->getTransitionCount(state, event) << " ";
1333                     total += m_profilers[i]->getTransitionCount(state, event);
1334                }
1335                out << "] " << total << "\\n";
1336            }
1337        }
1338        out << "\\n";
1339    }
1340}
1341''')
1342        code.write(path, "%s_ProfileDumper.cc" % self.ident)
1343
1344    def printProfilerHH(self, path):
1345        code = self.symtab.codeFormatter()
1346        ident = self.ident
1347
1348        code('''
1349// Auto generated C++ code started by $__file__:$__line__
1350// ${ident}: ${{self.short}}
1351
1352#ifndef __${ident}_PROFILER_HH__
1353#define __${ident}_PROFILER_HH__
1354
1355#include <cassert>
1356#include <iostream>
1357
1358#include "mem/protocol/${ident}_Event.hh"
1359#include "mem/protocol/${ident}_State.hh"
1360#include "mem/ruby/common/Global.hh"
1361
1362class ${ident}_Profiler
1363{
1364  public:
1365    ${ident}_Profiler();
1366    void setVersion(int version);
1367    void countTransition(${ident}_State state, ${ident}_Event event);
1368    void possibleTransition(${ident}_State state, ${ident}_Event event);
1369    uint64 getEventCount(${ident}_Event event);
1370    bool isPossible(${ident}_State state, ${ident}_Event event);
1371    uint64 getTransitionCount(${ident}_State state, ${ident}_Event event);
1372    void clearStats();
1373
1374  private:
1375    int m_counters[${ident}_State_NUM][${ident}_Event_NUM];
1376    int m_event_counters[${ident}_Event_NUM];
1377    bool m_possible[${ident}_State_NUM][${ident}_Event_NUM];
1378    int m_version;
1379};
1380
1381#endif // __${ident}_PROFILER_HH__
1382''')
1383        code.write(path, "%s_Profiler.hh" % self.ident)
1384
1385    def printProfilerCC(self, path):
1386        code = self.symtab.codeFormatter()
1387        ident = self.ident
1388
1389        code('''
1390// Auto generated C++ code started by $__file__:$__line__
1391// ${ident}: ${{self.short}}
1392
1393#include <cassert>
1394
1395#include "mem/protocol/${ident}_Profiler.hh"
1396
1397${ident}_Profiler::${ident}_Profiler()
1398{
1399    for (int state = 0; state < ${ident}_State_NUM; state++) {
1400        for (int event = 0; event < ${ident}_Event_NUM; event++) {
1401            m_possible[state][event] = false;
1402            m_counters[state][event] = 0;
1403        }
1404    }
1405    for (int event = 0; event < ${ident}_Event_NUM; event++) {
1406        m_event_counters[event] = 0;
1407    }
1408}
1409
1410void
1411${ident}_Profiler::setVersion(int version)
1412{
1413    m_version = version;
1414}
1415
1416void
1417${ident}_Profiler::clearStats()
1418{
1419    for (int state = 0; state < ${ident}_State_NUM; state++) {
1420        for (int event = 0; event < ${ident}_Event_NUM; event++) {
1421            m_counters[state][event] = 0;
1422        }
1423    }
1424
1425    for (int event = 0; event < ${ident}_Event_NUM; event++) {
1426        m_event_counters[event] = 0;
1427    }
1428}
1429void
1430${ident}_Profiler::countTransition(${ident}_State state, ${ident}_Event event)
1431{
1432    assert(m_possible[state][event]);
1433    m_counters[state][event]++;
1434    m_event_counters[event]++;
1435}
1436void
1437${ident}_Profiler::possibleTransition(${ident}_State state,
1438                                      ${ident}_Event event)
1439{
1440    m_possible[state][event] = true;
1441}
1442
1443uint64
1444${ident}_Profiler::getEventCount(${ident}_Event event)
1445{
1446    return m_event_counters[event];
1447}
1448
1449bool
1450${ident}_Profiler::isPossible(${ident}_State state, ${ident}_Event event)
1451{
1452    return m_possible[state][event];
1453}
1454
1455uint64
1456${ident}_Profiler::getTransitionCount(${ident}_State state,
1457                                      ${ident}_Event event)
1458{
1459    return m_counters[state][event];
1460}
1461
1462''')
1463        code.write(path, "%s_Profiler.cc" % self.ident)
1464
1465    # **************************
1466    # ******* HTML Files *******
1467    # **************************
1468    def frameRef(self, click_href, click_target, over_href, over_num, text):
1469        code = self.symtab.codeFormatter(fix_newlines=False)
1470        code("""<A href=\"$click_href\" target=\"$click_target\" onmouseover=\"
1471    if (parent.frames[$over_num].location != parent.location + '$over_href') {
1472        parent.frames[$over_num].location='$over_href'
1473    }\">
1474    ${{html.formatShorthand(text)}}
1475    </A>""")
1476        return str(code)
1477
1478    def writeHTMLFiles(self, path):
1479        # Create table with no row hilighted
1480        self.printHTMLTransitions(path, None)
1481
1482        # Generate transition tables
1483        for state in self.states.itervalues():
1484            self.printHTMLTransitions(path, state)
1485
1486        # Generate action descriptions
1487        for action in self.actions.itervalues():
1488            name = "%s_action_%s.html" % (self.ident, action.ident)
1489            code = html.createSymbol(action, "Action")
1490            code.write(path, name)
1491
1492        # Generate state descriptions
1493        for state in self.states.itervalues():
1494            name = "%s_State_%s.html" % (self.ident, state.ident)
1495            code = html.createSymbol(state, "State")
1496            code.write(path, name)
1497
1498        # Generate event descriptions
1499        for event in self.events.itervalues():
1500            name = "%s_Event_%s.html" % (self.ident, event.ident)
1501            code = html.createSymbol(event, "Event")
1502            code.write(path, name)
1503
1504    def printHTMLTransitions(self, path, active_state):
1505        code = self.symtab.codeFormatter()
1506
1507        code('''
1508<HTML>
1509<BODY link="blue" vlink="blue">
1510
1511<H1 align="center">${{html.formatShorthand(self.short)}}:
1512''')
1513        code.indent()
1514        for i,machine in enumerate(self.symtab.getAllType(StateMachine)):
1515            mid = machine.ident
1516            if i != 0:
1517                extra = " - "
1518            else:
1519                extra = ""
1520            if machine == self:
1521                code('$extra$mid')
1522            else:
1523                code('$extra<A target="Table" href="${mid}_table.html">$mid</A>')
1524        code.dedent()
1525
1526        code("""
1527</H1>
1528
1529<TABLE border=1>
1530<TR>
1531  <TH> </TH>
1532""")
1533
1534        for event in self.events.itervalues():
1535            href = "%s_Event_%s.html" % (self.ident, event.ident)
1536            ref = self.frameRef(href, "Status", href, "1", event.short)
1537            code('<TH bgcolor=white>$ref</TH>')
1538
1539        code('</TR>')
1540        # -- Body of table
1541        for state in self.states.itervalues():
1542            # -- Each row
1543            if state == active_state:
1544                color = "yellow"
1545            else:
1546                color = "white"
1547
1548            click = "%s_table_%s.html" % (self.ident, state.ident)
1549            over = "%s_State_%s.html" % (self.ident, state.ident)
1550            text = html.formatShorthand(state.short)
1551            ref = self.frameRef(click, "Table", over, "1", state.short)
1552            code('''
1553<TR>
1554  <TH bgcolor=$color>$ref</TH>
1555''')
1556
1557            # -- One column for each event
1558            for event in self.events.itervalues():
1559                trans = self.table.get((state,event), None)
1560                if trans is None:
1561                    # This is the no transition case
1562                    if state == active_state:
1563                        color = "#C0C000"
1564                    else:
1565                        color = "lightgrey"
1566
1567                    code('<TD bgcolor=$color>&nbsp;</TD>')
1568                    continue
1569
1570                next = trans.nextState
1571                stall_action = False
1572
1573                # -- Get the actions
1574                for action in trans.actions:
1575                    if action.ident == "z_stall" or \
1576                       action.ident == "zz_recycleMandatoryQueue":
1577                        stall_action = True
1578
1579                # -- Print out "actions/next-state"
1580                if stall_action:
1581                    if state == active_state:
1582                        color = "#C0C000"
1583                    else:
1584                        color = "lightgrey"
1585
1586                elif active_state and next.ident == active_state.ident:
1587                    color = "aqua"
1588                elif state == active_state:
1589                    color = "yellow"
1590                else:
1591                    color = "white"
1592
1593                code('<TD bgcolor=$color>')
1594                for action in trans.actions:
1595                    href = "%s_action_%s.html" % (self.ident, action.ident)
1596                    ref = self.frameRef(href, "Status", href, "1",
1597                                        action.short)
1598                    code('  $ref')
1599                if next != state:
1600                    if trans.actions:
1601                        code('/')
1602                    click = "%s_table_%s.html" % (self.ident, next.ident)
1603                    over = "%s_State_%s.html" % (self.ident, next.ident)
1604                    ref = self.frameRef(click, "Table", over, "1", next.short)
1605                    code("$ref")
1606                code("</TD>")
1607
1608            # -- Each row
1609            if state == active_state:
1610                color = "yellow"
1611            else:
1612                color = "white"
1613
1614            click = "%s_table_%s.html" % (self.ident, state.ident)
1615            over = "%s_State_%s.html" % (self.ident, state.ident)
1616            ref = self.frameRef(click, "Table", over, "1", state.short)
1617            code('''
1618  <TH bgcolor=$color>$ref</TH>
1619</TR>
1620''')
1621        code('''
1622<!- Column footer->
1623<TR>
1624  <TH> </TH>
1625''')
1626
1627        for event in self.events.itervalues():
1628            href = "%s_Event_%s.html" % (self.ident, event.ident)
1629            ref = self.frameRef(href, "Status", href, "1", event.short)
1630            code('<TH bgcolor=white>$ref</TH>')
1631        code('''
1632</TR>
1633</TABLE>
1634</BODY></HTML>
1635''')
1636
1637
1638        if active_state:
1639            name = "%s_table_%s.html" % (self.ident, active_state.ident)
1640        else:
1641            name = "%s_table.html" % self.ident
1642        code.write(path, name)
1643
1644__all__ = [ "StateMachine" ]
1645