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