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