StateMachine.py revision 9692:67d9da312ef0
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 == "DirectoryMemory" or \
772                   param.type_ast.type.ident == "MemoryControl":
773                assert(param.pointer)
774                code('    m_${{param.ident}}_ptr->printStats(out);')
775
776        code('''
777    if (m_version == 0) {
778        s_profileDumper.dumpStats(out);
779    }
780}
781
782void $c_ident::clearStats() {
783''')
784        #
785        # Cache and Memory Controllers have specific profilers associated with
786        # them.  These stats must be cleared too.
787        #
788        for param in self.config_parameters:
789            if param.type_ast.type.ident == "MemoryControl":
790                assert(param.pointer)
791                code('    m_${{param.ident}}_ptr->clearStats();')
792
793        code('''
794    m_profiler.clearStats();
795    AbstractController::clearStats();
796}
797''')
798
799        if self.EntryType != None:
800            code('''
801
802// Set and Reset for cache_entry variable
803void
804$c_ident::set_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, AbstractCacheEntry* m_new_cache_entry)
805{
806  m_cache_entry_ptr = (${{self.EntryType.c_ident}}*)m_new_cache_entry;
807}
808
809void
810$c_ident::unset_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr)
811{
812  m_cache_entry_ptr = 0;
813}
814''')
815
816        if self.TBEType != None:
817            code('''
818
819// Set and Reset for tbe variable
820void
821$c_ident::set_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.TBEType.c_ident}}* m_new_tbe)
822{
823  m_tbe_ptr = m_new_tbe;
824}
825
826void
827$c_ident::unset_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr)
828{
829  m_tbe_ptr = NULL;
830}
831''')
832
833        code('''
834
835void
836$c_ident::recordCacheTrace(int cntrl, CacheRecorder* tr)
837{
838''')
839        #
840        # Record cache contents for all associated caches.
841        #
842        code.indent()
843        for param in self.config_parameters:
844            if param.type_ast.type.ident == "CacheMemory":
845                assert(param.pointer)
846                code('m_${{param.ident}}_ptr->recordCacheContents(cntrl, tr);')
847
848        code.dedent()
849        code('''
850}
851
852// Actions
853''')
854        if self.TBEType != None and self.EntryType != None:
855            for action in self.actions.itervalues():
856                if "c_code" not in action:
857                 continue
858
859                code('''
860/** \\brief ${{action.desc}} */
861void
862$c_ident::${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr)
863{
864    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
865    ${{action["c_code"]}}
866}
867
868''')
869        elif self.TBEType != None:
870            for action in self.actions.itervalues():
871                if "c_code" not in action:
872                 continue
873
874                code('''
875/** \\brief ${{action.desc}} */
876void
877$c_ident::${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, const Address& addr)
878{
879    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
880    ${{action["c_code"]}}
881}
882
883''')
884        elif self.EntryType != None:
885            for action in self.actions.itervalues():
886                if "c_code" not in action:
887                 continue
888
889                code('''
890/** \\brief ${{action.desc}} */
891void
892$c_ident::${{action.ident}}(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr)
893{
894    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
895    ${{action["c_code"]}}
896}
897
898''')
899        else:
900            for action in self.actions.itervalues():
901                if "c_code" not in action:
902                 continue
903
904                code('''
905/** \\brief ${{action.desc}} */
906void
907$c_ident::${{action.ident}}(const Address& addr)
908{
909    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
910    ${{action["c_code"]}}
911}
912
913''')
914        for func in self.functions:
915            code(func.generateCode())
916
917        # Function for functional reads from messages buffered in the controller
918        code('''
919bool
920$c_ident::functionalReadBuffers(PacketPtr& pkt)
921{
922''')
923        for var in self.objects:
924            vtype = var.type
925            if vtype.isBuffer:
926                vid = "m_%s_ptr" % var.c_ident
927                code('if ($vid->functionalRead(pkt)) { return true; }')
928        code('''
929                return false;
930}
931''')
932
933        # Function for functional writes to messages buffered in the controller
934        code('''
935uint32_t
936$c_ident::functionalWriteBuffers(PacketPtr& pkt)
937{
938    uint32_t num_functional_writes = 0;
939''')
940        for var in self.objects:
941            vtype = var.type
942            if vtype.isBuffer:
943                vid = "m_%s_ptr" % var.c_ident
944                code('num_functional_writes += $vid->functionalWrite(pkt);')
945        code('''
946    return num_functional_writes;
947}
948''')
949
950        # Check if this controller has a peer, if yes then write the
951        # function for connecting to the peer.
952        if has_peer:
953            code('''
954
955void
956$c_ident::getQueuesFromPeer(AbstractController *peer)
957{
958''')
959            for var in self.objects:
960                if "network" in var and "physical_network" in var and \
961                   var["network"] == "From":
962                    code('''
963m_${{var.c_ident}}_ptr = peer->getPeerQueue(${{var["physical_network"]}});
964assert(m_${{var.c_ident}}_ptr != NULL);
965m_${{var.c_ident}}_ptr->setReceiver(this);
966
967''')
968            code('}')
969
970        code.write(path, "%s.cc" % c_ident)
971
972    def printCWakeup(self, path, includes):
973        '''Output the wakeup loop for the events'''
974
975        code = self.symtab.codeFormatter()
976        ident = self.ident
977
978        outputRequest_types = True
979        if len(self.request_types) == 0:
980            outputRequest_types = False
981
982        code('''
983// Auto generated C++ code started by $__file__:$__line__
984// ${ident}: ${{self.short}}
985
986#include <sys/types.h>
987#include <unistd.h>
988
989#include <cassert>
990
991#include "base/misc.hh"
992#include "debug/RubySlicc.hh"
993#include "mem/protocol/${ident}_Controller.hh"
994#include "mem/protocol/${ident}_Event.hh"
995#include "mem/protocol/${ident}_State.hh"
996''')
997
998        if outputRequest_types:
999            code('''#include "mem/protocol/${ident}_RequestType.hh"''')
1000
1001        code('''
1002#include "mem/protocol/Types.hh"
1003#include "mem/ruby/common/Global.hh"
1004#include "mem/ruby/system/System.hh"
1005''')
1006
1007
1008        for include_path in includes:
1009            code('#include "${{include_path}}"')
1010
1011        code('''
1012
1013using namespace std;
1014
1015void
1016${ident}_Controller::wakeup()
1017{
1018    int counter = 0;
1019    while (true) {
1020        // Some cases will put us into an infinite loop without this limit
1021        assert(counter <= m_transitions_per_cycle);
1022        if (counter == m_transitions_per_cycle) {
1023            // Count how often we are fully utilized
1024            m_fully_busy_cycles++;
1025
1026            // Wakeup in another cycle and try again
1027            scheduleEvent(Cycles(1));
1028            break;
1029        }
1030''')
1031
1032        code.indent()
1033        code.indent()
1034
1035        # InPorts
1036        #
1037        for port in self.in_ports:
1038            code.indent()
1039            code('// ${ident}InPort $port')
1040            if port.pairs.has_key("rank"):
1041                code('m_cur_in_port_rank = ${{port.pairs["rank"]}};')
1042            else:
1043                code('m_cur_in_port_rank = 0;')
1044            code('${{port["c_code_in_port"]}}')
1045            code.dedent()
1046
1047            code('')
1048
1049        code.dedent()
1050        code.dedent()
1051        code('''
1052        break;  // If we got this far, we have nothing left todo
1053    }
1054}
1055''')
1056
1057        code.write(path, "%s_Wakeup.cc" % self.ident)
1058
1059    def printCSwitch(self, path):
1060        '''Output switch statement for transition table'''
1061
1062        code = self.symtab.codeFormatter()
1063        ident = self.ident
1064
1065        code('''
1066// Auto generated C++ code started by $__file__:$__line__
1067// ${ident}: ${{self.short}}
1068
1069#include <cassert>
1070
1071#include "base/misc.hh"
1072#include "base/trace.hh"
1073#include "debug/ProtocolTrace.hh"
1074#include "debug/RubyGenerated.hh"
1075#include "mem/protocol/${ident}_Controller.hh"
1076#include "mem/protocol/${ident}_Event.hh"
1077#include "mem/protocol/${ident}_State.hh"
1078#include "mem/protocol/Types.hh"
1079#include "mem/ruby/common/Global.hh"
1080#include "mem/ruby/system/System.hh"
1081
1082#define HASH_FUN(state, event)  ((int(state)*${ident}_Event_NUM)+int(event))
1083
1084#define GET_TRANSITION_COMMENT() (${ident}_transitionComment.str())
1085#define CLEAR_TRANSITION_COMMENT() (${ident}_transitionComment.str(""))
1086
1087TransitionResult
1088${ident}_Controller::doTransition(${ident}_Event event,
1089''')
1090        if self.EntryType != None:
1091            code('''
1092                                  ${{self.EntryType.c_ident}}* m_cache_entry_ptr,
1093''')
1094        if self.TBEType != None:
1095            code('''
1096                                  ${{self.TBEType.c_ident}}* m_tbe_ptr,
1097''')
1098        code('''
1099                                  const Address &addr)
1100{
1101''')
1102        if self.TBEType != None and self.EntryType != None:
1103            code('${ident}_State state = getState(m_tbe_ptr, m_cache_entry_ptr, addr);')
1104        elif self.TBEType != None:
1105            code('${ident}_State state = getState(m_tbe_ptr, addr);')
1106        elif self.EntryType != None:
1107            code('${ident}_State state = getState(m_cache_entry_ptr, addr);')
1108        else:
1109            code('${ident}_State state = getState(addr);')
1110
1111        code('''
1112    ${ident}_State next_state = state;
1113
1114    DPRINTF(RubyGenerated, "%s, Time: %lld, state: %s, event: %s, addr: %s\\n",
1115            *this, curCycle(), ${ident}_State_to_string(state),
1116            ${ident}_Event_to_string(event), addr);
1117
1118    TransitionResult result =
1119''')
1120        if self.TBEType != None and self.EntryType != None:
1121            code('doTransitionWorker(event, state, next_state, m_tbe_ptr, m_cache_entry_ptr, addr);')
1122        elif self.TBEType != None:
1123            code('doTransitionWorker(event, state, next_state, m_tbe_ptr, addr);')
1124        elif self.EntryType != None:
1125            code('doTransitionWorker(event, state, next_state, m_cache_entry_ptr, addr);')
1126        else:
1127            code('doTransitionWorker(event, state, next_state, addr);')
1128
1129        code('''
1130    if (result == TransitionResult_Valid) {
1131        DPRINTF(RubyGenerated, "next_state: %s\\n",
1132                ${ident}_State_to_string(next_state));
1133        m_profiler.countTransition(state, event);
1134        DPRINTFR(ProtocolTrace, "%15d %3s %10s%20s %6s>%-6s %s %s\\n",
1135                 curTick(), m_version, "${ident}",
1136                 ${ident}_Event_to_string(event),
1137                 ${ident}_State_to_string(state),
1138                 ${ident}_State_to_string(next_state),
1139                 addr, GET_TRANSITION_COMMENT());
1140
1141        CLEAR_TRANSITION_COMMENT();
1142''')
1143        if self.TBEType != None and self.EntryType != None:
1144            code('setState(m_tbe_ptr, m_cache_entry_ptr, addr, next_state);')
1145            code('setAccessPermission(m_cache_entry_ptr, addr, next_state);')
1146        elif self.TBEType != None:
1147            code('setState(m_tbe_ptr, addr, next_state);')
1148            code('setAccessPermission(addr, next_state);')
1149        elif self.EntryType != None:
1150            code('setState(m_cache_entry_ptr, addr, next_state);')
1151            code('setAccessPermission(m_cache_entry_ptr, addr, next_state);')
1152        else:
1153            code('setState(addr, next_state);')
1154            code('setAccessPermission(addr, next_state);')
1155
1156        code('''
1157    } else if (result == TransitionResult_ResourceStall) {
1158        DPRINTFR(ProtocolTrace, "%15s %3s %10s%20s %6s>%-6s %s %s\\n",
1159                 curTick(), m_version, "${ident}",
1160                 ${ident}_Event_to_string(event),
1161                 ${ident}_State_to_string(state),
1162                 ${ident}_State_to_string(next_state),
1163                 addr, "Resource Stall");
1164    } else if (result == TransitionResult_ProtocolStall) {
1165        DPRINTF(RubyGenerated, "stalling\\n");
1166        DPRINTFR(ProtocolTrace, "%15s %3s %10s%20s %6s>%-6s %s %s\\n",
1167                 curTick(), m_version, "${ident}",
1168                 ${ident}_Event_to_string(event),
1169                 ${ident}_State_to_string(state),
1170                 ${ident}_State_to_string(next_state),
1171                 addr, "Protocol Stall");
1172    }
1173
1174    return result;
1175}
1176
1177TransitionResult
1178${ident}_Controller::doTransitionWorker(${ident}_Event event,
1179                                        ${ident}_State state,
1180                                        ${ident}_State& next_state,
1181''')
1182
1183        if self.TBEType != None:
1184            code('''
1185                                        ${{self.TBEType.c_ident}}*& m_tbe_ptr,
1186''')
1187        if self.EntryType != None:
1188                  code('''
1189                                        ${{self.EntryType.c_ident}}*& m_cache_entry_ptr,
1190''')
1191        code('''
1192                                        const Address& addr)
1193{
1194    switch(HASH_FUN(state, event)) {
1195''')
1196
1197        # This map will allow suppress generating duplicate code
1198        cases = orderdict()
1199
1200        for trans in self.transitions:
1201            case_string = "%s_State_%s, %s_Event_%s" % \
1202                (self.ident, trans.state.ident, self.ident, trans.event.ident)
1203
1204            case = self.symtab.codeFormatter()
1205            # Only set next_state if it changes
1206            if trans.state != trans.nextState:
1207                ns_ident = trans.nextState.ident
1208                case('next_state = ${ident}_State_${ns_ident};')
1209
1210            actions = trans.actions
1211            request_types = trans.request_types
1212
1213            # Check for resources
1214            case_sorter = []
1215            res = trans.resources
1216            for key,val in res.iteritems():
1217                if key.type.ident != "DNUCAStopTable":
1218                    val = '''
1219if (!%s.areNSlotsAvailable(%s))
1220    return TransitionResult_ResourceStall;
1221''' % (key.code, val)
1222                case_sorter.append(val)
1223
1224            # Check all of the request_types for resource constraints
1225            for request_type in request_types:
1226                val = '''
1227if (!checkResourceAvailable(%s_RequestType_%s, addr)) {
1228    return TransitionResult_ResourceStall;
1229}
1230''' % (self.ident, request_type.ident)
1231                case_sorter.append(val)
1232
1233            # Emit the code sequences in a sorted order.  This makes the
1234            # output deterministic (without this the output order can vary
1235            # since Map's keys() on a vector of pointers is not deterministic
1236            for c in sorted(case_sorter):
1237                case("$c")
1238
1239            # Record access types for this transition
1240            for request_type in request_types:
1241                case('recordRequestType(${ident}_RequestType_${{request_type.ident}}, addr);')
1242
1243            # Figure out if we stall
1244            stall = False
1245            for action in actions:
1246                if action.ident == "z_stall":
1247                    stall = True
1248                    break
1249
1250            if stall:
1251                case('return TransitionResult_ProtocolStall;')
1252            else:
1253                if self.TBEType != None and self.EntryType != None:
1254                    for action in actions:
1255                        case('${{action.ident}}(m_tbe_ptr, m_cache_entry_ptr, addr);')
1256                elif self.TBEType != None:
1257                    for action in actions:
1258                        case('${{action.ident}}(m_tbe_ptr, addr);')
1259                elif self.EntryType != None:
1260                    for action in actions:
1261                        case('${{action.ident}}(m_cache_entry_ptr, addr);')
1262                else:
1263                    for action in actions:
1264                        case('${{action.ident}}(addr);')
1265                case('return TransitionResult_Valid;')
1266
1267            case = str(case)
1268
1269            # Look to see if this transition code is unique.
1270            if case not in cases:
1271                cases[case] = []
1272
1273            cases[case].append(case_string)
1274
1275        # Walk through all of the unique code blocks and spit out the
1276        # corresponding case statement elements
1277        for case,transitions in cases.iteritems():
1278            # Iterative over all the multiple transitions that share
1279            # the same code
1280            for trans in transitions:
1281                code('  case HASH_FUN($trans):')
1282            code('    $case')
1283
1284        code('''
1285      default:
1286        fatal("Invalid transition\\n"
1287              "%s time: %d addr: %s event: %s state: %s\\n",
1288              name(), curCycle(), addr, event, state);
1289    }
1290    return TransitionResult_Valid;
1291}
1292''')
1293        code.write(path, "%s_Transitions.cc" % self.ident)
1294
1295    def printProfileDumperHH(self, path):
1296        code = self.symtab.codeFormatter()
1297        ident = self.ident
1298
1299        code('''
1300// Auto generated C++ code started by $__file__:$__line__
1301// ${ident}: ${{self.short}}
1302
1303#ifndef __${ident}_PROFILE_DUMPER_HH__
1304#define __${ident}_PROFILE_DUMPER_HH__
1305
1306#include <cassert>
1307#include <iostream>
1308#include <vector>
1309
1310#include "${ident}_Event.hh"
1311#include "${ident}_Profiler.hh"
1312
1313typedef std::vector<${ident}_Profiler *> ${ident}_profilers;
1314
1315class ${ident}_ProfileDumper
1316{
1317  public:
1318    ${ident}_ProfileDumper();
1319    void registerProfiler(${ident}_Profiler* profiler);
1320    void dumpStats(std::ostream& out) const;
1321
1322  private:
1323    ${ident}_profilers m_profilers;
1324};
1325
1326#endif // __${ident}_PROFILE_DUMPER_HH__
1327''')
1328        code.write(path, "%s_ProfileDumper.hh" % self.ident)
1329
1330    def printProfileDumperCC(self, path):
1331        code = self.symtab.codeFormatter()
1332        ident = self.ident
1333
1334        code('''
1335// Auto generated C++ code started by $__file__:$__line__
1336// ${ident}: ${{self.short}}
1337
1338#include "mem/protocol/${ident}_ProfileDumper.hh"
1339
1340${ident}_ProfileDumper::${ident}_ProfileDumper()
1341{
1342}
1343
1344void
1345${ident}_ProfileDumper::registerProfiler(${ident}_Profiler* profiler)
1346{
1347    if (profiler->getVersion() >= m_profilers.size())
1348        m_profilers.resize(profiler->getVersion() + 1);
1349    m_profilers[profiler->getVersion()] = profiler;
1350}
1351
1352void
1353${ident}_ProfileDumper::dumpStats(std::ostream& out) const
1354{
1355    out << " --- ${ident} ---\\n";
1356    out << " - Event Counts -\\n";
1357    for (${ident}_Event event = ${ident}_Event_FIRST;
1358         event < ${ident}_Event_NUM;
1359         ++event) {
1360        out << (${ident}_Event) event << " [";
1361        uint64 total = 0;
1362        for (int i = 0; i < m_profilers.size(); i++) {
1363             out << m_profilers[i]->getEventCount(event) << " ";
1364             total += m_profilers[i]->getEventCount(event);
1365        }
1366        out << "] " << total << "\\n";
1367    }
1368    out << "\\n";
1369    out << " - Transitions -\\n";
1370    for (${ident}_State state = ${ident}_State_FIRST;
1371         state < ${ident}_State_NUM;
1372         ++state) {
1373        for (${ident}_Event event = ${ident}_Event_FIRST;
1374             event < ${ident}_Event_NUM;
1375             ++event) {
1376            if (m_profilers[0]->isPossible(state, event)) {
1377                out << (${ident}_State) state << "  "
1378                    << (${ident}_Event) event << " [";
1379                uint64 total = 0;
1380                for (int i = 0; i < m_profilers.size(); i++) {
1381                     out << m_profilers[i]->getTransitionCount(state, event) << " ";
1382                     total += m_profilers[i]->getTransitionCount(state, event);
1383                }
1384                out << "] " << total << "\\n";
1385            }
1386        }
1387        out << "\\n";
1388    }
1389}
1390''')
1391        code.write(path, "%s_ProfileDumper.cc" % self.ident)
1392
1393    def printProfilerHH(self, path):
1394        code = self.symtab.codeFormatter()
1395        ident = self.ident
1396
1397        code('''
1398// Auto generated C++ code started by $__file__:$__line__
1399// ${ident}: ${{self.short}}
1400
1401#ifndef __${ident}_PROFILER_HH__
1402#define __${ident}_PROFILER_HH__
1403
1404#include <cassert>
1405#include <iostream>
1406
1407#include "mem/protocol/${ident}_Event.hh"
1408#include "mem/protocol/${ident}_State.hh"
1409#include "mem/ruby/common/TypeDefines.hh"
1410
1411class ${ident}_Profiler
1412{
1413  public:
1414    ${ident}_Profiler();
1415    void setVersion(int version);
1416    int getVersion();
1417    void countTransition(${ident}_State state, ${ident}_Event event);
1418    void possibleTransition(${ident}_State state, ${ident}_Event event);
1419    uint64 getEventCount(${ident}_Event event);
1420    bool isPossible(${ident}_State state, ${ident}_Event event);
1421    uint64 getTransitionCount(${ident}_State state, ${ident}_Event event);
1422    void clearStats();
1423
1424  private:
1425    int m_counters[${ident}_State_NUM][${ident}_Event_NUM];
1426    int m_event_counters[${ident}_Event_NUM];
1427    bool m_possible[${ident}_State_NUM][${ident}_Event_NUM];
1428    int m_version;
1429};
1430
1431#endif // __${ident}_PROFILER_HH__
1432''')
1433        code.write(path, "%s_Profiler.hh" % self.ident)
1434
1435    def printProfilerCC(self, path):
1436        code = self.symtab.codeFormatter()
1437        ident = self.ident
1438
1439        code('''
1440// Auto generated C++ code started by $__file__:$__line__
1441// ${ident}: ${{self.short}}
1442
1443#include <cassert>
1444
1445#include "mem/protocol/${ident}_Profiler.hh"
1446
1447${ident}_Profiler::${ident}_Profiler()
1448{
1449    for (int state = 0; state < ${ident}_State_NUM; state++) {
1450        for (int event = 0; event < ${ident}_Event_NUM; event++) {
1451            m_possible[state][event] = false;
1452            m_counters[state][event] = 0;
1453        }
1454    }
1455    for (int event = 0; event < ${ident}_Event_NUM; event++) {
1456        m_event_counters[event] = 0;
1457    }
1458}
1459
1460void
1461${ident}_Profiler::setVersion(int version)
1462{
1463    m_version = version;
1464}
1465
1466int
1467${ident}_Profiler::getVersion()
1468{
1469    return m_version;
1470}
1471
1472void
1473${ident}_Profiler::clearStats()
1474{
1475    for (int state = 0; state < ${ident}_State_NUM; state++) {
1476        for (int event = 0; event < ${ident}_Event_NUM; event++) {
1477            m_counters[state][event] = 0;
1478        }
1479    }
1480
1481    for (int event = 0; event < ${ident}_Event_NUM; event++) {
1482        m_event_counters[event] = 0;
1483    }
1484}
1485void
1486${ident}_Profiler::countTransition(${ident}_State state, ${ident}_Event event)
1487{
1488    assert(m_possible[state][event]);
1489    m_counters[state][event]++;
1490    m_event_counters[event]++;
1491}
1492void
1493${ident}_Profiler::possibleTransition(${ident}_State state,
1494                                      ${ident}_Event event)
1495{
1496    m_possible[state][event] = true;
1497}
1498
1499uint64
1500${ident}_Profiler::getEventCount(${ident}_Event event)
1501{
1502    return m_event_counters[event];
1503}
1504
1505bool
1506${ident}_Profiler::isPossible(${ident}_State state, ${ident}_Event event)
1507{
1508    return m_possible[state][event];
1509}
1510
1511uint64
1512${ident}_Profiler::getTransitionCount(${ident}_State state,
1513                                      ${ident}_Event event)
1514{
1515    return m_counters[state][event];
1516}
1517
1518''')
1519        code.write(path, "%s_Profiler.cc" % self.ident)
1520
1521    # **************************
1522    # ******* HTML Files *******
1523    # **************************
1524    def frameRef(self, click_href, click_target, over_href, over_num, text):
1525        code = self.symtab.codeFormatter(fix_newlines=False)
1526        code("""<A href=\"$click_href\" target=\"$click_target\" onmouseover=\"
1527    if (parent.frames[$over_num].location != parent.location + '$over_href') {
1528        parent.frames[$over_num].location='$over_href'
1529    }\">
1530    ${{html.formatShorthand(text)}}
1531    </A>""")
1532        return str(code)
1533
1534    def writeHTMLFiles(self, path):
1535        # Create table with no row hilighted
1536        self.printHTMLTransitions(path, None)
1537
1538        # Generate transition tables
1539        for state in self.states.itervalues():
1540            self.printHTMLTransitions(path, state)
1541
1542        # Generate action descriptions
1543        for action in self.actions.itervalues():
1544            name = "%s_action_%s.html" % (self.ident, action.ident)
1545            code = html.createSymbol(action, "Action")
1546            code.write(path, name)
1547
1548        # Generate state descriptions
1549        for state in self.states.itervalues():
1550            name = "%s_State_%s.html" % (self.ident, state.ident)
1551            code = html.createSymbol(state, "State")
1552            code.write(path, name)
1553
1554        # Generate event descriptions
1555        for event in self.events.itervalues():
1556            name = "%s_Event_%s.html" % (self.ident, event.ident)
1557            code = html.createSymbol(event, "Event")
1558            code.write(path, name)
1559
1560    def printHTMLTransitions(self, path, active_state):
1561        code = self.symtab.codeFormatter()
1562
1563        code('''
1564<HTML>
1565<BODY link="blue" vlink="blue">
1566
1567<H1 align="center">${{html.formatShorthand(self.short)}}:
1568''')
1569        code.indent()
1570        for i,machine in enumerate(self.symtab.getAllType(StateMachine)):
1571            mid = machine.ident
1572            if i != 0:
1573                extra = " - "
1574            else:
1575                extra = ""
1576            if machine == self:
1577                code('$extra$mid')
1578            else:
1579                code('$extra<A target="Table" href="${mid}_table.html">$mid</A>')
1580        code.dedent()
1581
1582        code("""
1583</H1>
1584
1585<TABLE border=1>
1586<TR>
1587  <TH> </TH>
1588""")
1589
1590        for event in self.events.itervalues():
1591            href = "%s_Event_%s.html" % (self.ident, event.ident)
1592            ref = self.frameRef(href, "Status", href, "1", event.short)
1593            code('<TH bgcolor=white>$ref</TH>')
1594
1595        code('</TR>')
1596        # -- Body of table
1597        for state in self.states.itervalues():
1598            # -- Each row
1599            if state == active_state:
1600                color = "yellow"
1601            else:
1602                color = "white"
1603
1604            click = "%s_table_%s.html" % (self.ident, state.ident)
1605            over = "%s_State_%s.html" % (self.ident, state.ident)
1606            text = html.formatShorthand(state.short)
1607            ref = self.frameRef(click, "Table", over, "1", state.short)
1608            code('''
1609<TR>
1610  <TH bgcolor=$color>$ref</TH>
1611''')
1612
1613            # -- One column for each event
1614            for event in self.events.itervalues():
1615                trans = self.table.get((state,event), None)
1616                if trans is None:
1617                    # This is the no transition case
1618                    if state == active_state:
1619                        color = "#C0C000"
1620                    else:
1621                        color = "lightgrey"
1622
1623                    code('<TD bgcolor=$color>&nbsp;</TD>')
1624                    continue
1625
1626                next = trans.nextState
1627                stall_action = False
1628
1629                # -- Get the actions
1630                for action in trans.actions:
1631                    if action.ident == "z_stall" or \
1632                       action.ident == "zz_recycleMandatoryQueue":
1633                        stall_action = True
1634
1635                # -- Print out "actions/next-state"
1636                if stall_action:
1637                    if state == active_state:
1638                        color = "#C0C000"
1639                    else:
1640                        color = "lightgrey"
1641
1642                elif active_state and next.ident == active_state.ident:
1643                    color = "aqua"
1644                elif state == active_state:
1645                    color = "yellow"
1646                else:
1647                    color = "white"
1648
1649                code('<TD bgcolor=$color>')
1650                for action in trans.actions:
1651                    href = "%s_action_%s.html" % (self.ident, action.ident)
1652                    ref = self.frameRef(href, "Status", href, "1",
1653                                        action.short)
1654                    code('  $ref')
1655                if next != state:
1656                    if trans.actions:
1657                        code('/')
1658                    click = "%s_table_%s.html" % (self.ident, next.ident)
1659                    over = "%s_State_%s.html" % (self.ident, next.ident)
1660                    ref = self.frameRef(click, "Table", over, "1", next.short)
1661                    code("$ref")
1662                code("</TD>")
1663
1664            # -- Each row
1665            if state == active_state:
1666                color = "yellow"
1667            else:
1668                color = "white"
1669
1670            click = "%s_table_%s.html" % (self.ident, state.ident)
1671            over = "%s_State_%s.html" % (self.ident, state.ident)
1672            ref = self.frameRef(click, "Table", over, "1", state.short)
1673            code('''
1674  <TH bgcolor=$color>$ref</TH>
1675</TR>
1676''')
1677        code('''
1678<!- Column footer->
1679<TR>
1680  <TH> </TH>
1681''')
1682
1683        for event in self.events.itervalues():
1684            href = "%s_Event_%s.html" % (self.ident, event.ident)
1685            ref = self.frameRef(href, "Status", href, "1", event.short)
1686            code('<TH bgcolor=white>$ref</TH>')
1687        code('''
1688</TR>
1689</TABLE>
1690</BODY></HTML>
1691''')
1692
1693
1694        if active_state:
1695            name = "%s_table_%s.html" % (self.ident, active_state.ident)
1696        else:
1697            name = "%s_table.html" % self.ident
1698        code.write(path, name)
1699
1700__all__ = [ "StateMachine" ]
1701