StateMachine.py revision 8683
1# Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
2# Copyright (c) 2009 The Hewlett-Packard Development Company
3# All rights reserved.
4#
5# Redistribution and use in source and binary forms, with or without
6# modification, are permitted provided that the following conditions are
7# met: redistributions of source code must retain the above copyright
8# notice, this list of conditions and the following disclaimer;
9# redistributions in binary form must reproduce the above copyright
10# notice, this list of conditions and the following disclaimer in the
11# documentation and/or other materials provided with the distribution;
12# neither the name of the copyright holders nor the names of its
13# contributors may be used to endorse or promote products derived from
14# this software without specific prior written permission.
15#
16# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28from m5.util import orderdict
29
30from slicc.symbols.Symbol import Symbol
31from slicc.symbols.Var import Var
32import slicc.generate.html as html
33import re
34
35python_class_map = {"int": "Int",
36                    "std::string": "String",
37                    "bool": "Bool",
38                    "CacheMemory": "RubyCache",
39                    "WireBuffer": "RubyWireBuffer",
40                    "Sequencer": "RubySequencer",
41                    "DirectoryMemory": "RubyDirectoryMemory",
42                    "MemoryControl": "RubyMemoryControl",
43                    "DMASequencer": "DMASequencer"
44                    }
45
46class StateMachine(Symbol):
47    def __init__(self, symtab, ident, location, pairs, config_parameters):
48        super(StateMachine, self).__init__(symtab, ident, location, pairs)
49        self.table = None
50        self.config_parameters = config_parameters
51
52        for param in config_parameters:
53            if param.pointer:
54                var = Var(symtab, param.name, location, param.type_ast.type,
55                          "(*m_%s_ptr)" % param.name, {}, self)
56            else:
57                var = Var(symtab, param.name, location, param.type_ast.type,
58                          "m_%s" % param.name, {}, self)
59            self.symtab.registerSym(param.name, var)
60
61        self.states = orderdict()
62        self.events = orderdict()
63        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    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