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