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