StateMachine.py revision 11025
16166Ssteve.reinhardt@amd.com# Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
26166Ssteve.reinhardt@amd.com# Copyright (c) 2009 The Hewlett-Packard Development Company
36166Ssteve.reinhardt@amd.com# Copyright (c) 2013 Advanced Micro Devices, Inc.
46166Ssteve.reinhardt@amd.com# All rights reserved.
56166Ssteve.reinhardt@amd.com#
66166Ssteve.reinhardt@amd.com# Redistribution and use in source and binary forms, with or without
76166Ssteve.reinhardt@amd.com# modification, are permitted provided that the following conditions are
86166Ssteve.reinhardt@amd.com# met: redistributions of source code must retain the above copyright
96166Ssteve.reinhardt@amd.com# notice, this list of conditions and the following disclaimer;
106166Ssteve.reinhardt@amd.com# redistributions in binary form must reproduce the above copyright
116166Ssteve.reinhardt@amd.com# notice, this list of conditions and the following disclaimer in the
126166Ssteve.reinhardt@amd.com# documentation and/or other materials provided with the distribution;
136166Ssteve.reinhardt@amd.com# neither the name of the copyright holders nor the names of its
146166Ssteve.reinhardt@amd.com# contributors may be used to endorse or promote products derived from
156166Ssteve.reinhardt@amd.com# this software without specific prior written permission.
166166Ssteve.reinhardt@amd.com#
176166Ssteve.reinhardt@amd.com# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
186166Ssteve.reinhardt@amd.com# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
196166Ssteve.reinhardt@amd.com# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
206166Ssteve.reinhardt@amd.com# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
216166Ssteve.reinhardt@amd.com# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
226166Ssteve.reinhardt@amd.com# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
236166Ssteve.reinhardt@amd.com# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
246166Ssteve.reinhardt@amd.com# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
256166Ssteve.reinhardt@amd.com# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
266166Ssteve.reinhardt@amd.com# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
276166Ssteve.reinhardt@amd.com# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
286166Ssteve.reinhardt@amd.com
296166Ssteve.reinhardt@amd.comfrom m5.util import orderdict
306166Ssteve.reinhardt@amd.com
316928SBrad.Beckmann@amd.comfrom slicc.symbols.Symbol import Symbol
326928SBrad.Beckmann@amd.comfrom slicc.symbols.Var import Var
336928SBrad.Beckmann@amd.comimport slicc.generate.html as html
346166Ssteve.reinhardt@amd.comimport re
356928SBrad.Beckmann@amd.com
366928SBrad.Beckmann@amd.compython_class_map = {
376928SBrad.Beckmann@amd.com                    "int": "Int",
386928SBrad.Beckmann@amd.com                    "uint32_t" : "UInt32",
396928SBrad.Beckmann@amd.com                    "std::string": "String",
406928SBrad.Beckmann@amd.com                    "bool": "Bool",
416928SBrad.Beckmann@amd.com                    "CacheMemory": "RubyCache",
426928SBrad.Beckmann@amd.com                    "WireBuffer": "RubyWireBuffer",
436928SBrad.Beckmann@amd.com                    "Sequencer": "RubySequencer",
446928SBrad.Beckmann@amd.com                    "DirectoryMemory": "RubyDirectoryMemory",
456928SBrad.Beckmann@amd.com                    "MemoryControl": "MemoryControl",
466928SBrad.Beckmann@amd.com                    "DMASequencer": "DMASequencer",
476928SBrad.Beckmann@amd.com                    "Prefetcher":"Prefetcher",
486928SBrad.Beckmann@amd.com                    "Cycles":"Cycles",
496928SBrad.Beckmann@amd.com                   }
507570SBrad.Beckmann@amd.com
516928SBrad.Beckmann@amd.comclass StateMachine(Symbol):
527570SBrad.Beckmann@amd.com    def __init__(self, symtab, ident, location, pairs, config_parameters):
536928SBrad.Beckmann@amd.com        super(StateMachine, self).__init__(symtab, ident, location, pairs)
546928SBrad.Beckmann@amd.com        self.table = None
556928SBrad.Beckmann@amd.com
566928SBrad.Beckmann@amd.com        # Data members in the State Machine that have been declared before
576928SBrad.Beckmann@amd.com        # the opening brace '{'  of the machine.  Note that these along with
587570SBrad.Beckmann@amd.com        # the members in self.objects form the entire set of data members.
597570SBrad.Beckmann@amd.com        self.config_parameters = config_parameters
607570SBrad.Beckmann@amd.com
617570SBrad.Beckmann@amd.com        self.prefetchers = []
627570SBrad.Beckmann@amd.com
637570SBrad.Beckmann@amd.com        for param in config_parameters:
647570SBrad.Beckmann@amd.com            if param.pointer:
657570SBrad.Beckmann@amd.com                var = Var(symtab, param.ident, location, param.type_ast.type,
667570SBrad.Beckmann@amd.com                          "(*m_%s_ptr)" % param.ident, {}, self)
677570SBrad.Beckmann@amd.com            else:
687570SBrad.Beckmann@amd.com                var = Var(symtab, param.ident, location, param.type_ast.type,
697570SBrad.Beckmann@amd.com                          "m_%s" % param.ident, {}, self)
707570SBrad.Beckmann@amd.com
716928SBrad.Beckmann@amd.com            self.symtab.registerSym(param.ident, var)
726928SBrad.Beckmann@amd.com
736289Snate@binkert.org            if str(param.type_ast.type) == "Prefetcher":
746166Ssteve.reinhardt@amd.com                self.prefetchers.append(var)
757570SBrad.Beckmann@amd.com
766928SBrad.Beckmann@amd.com        self.states = orderdict()
778436SBrad.Beckmann@amd.com        self.events = orderdict()
786928SBrad.Beckmann@amd.com        self.actions = orderdict()
798322Ssteve.reinhardt@amd.com        self.request_types = orderdict()
806928SBrad.Beckmann@amd.com        self.transitions = []
816928SBrad.Beckmann@amd.com        self.in_ports = []
826928SBrad.Beckmann@amd.com        self.functions = []
836928SBrad.Beckmann@amd.com
846928SBrad.Beckmann@amd.com        # Data members in the State Machine that have been declared inside
858744Sgblack@eecs.umich.edu        # the {} machine.  Note that these along with the config params
866928SBrad.Beckmann@amd.com        # form the entire set of data members of the machine.
878706Sandreas.hansson@arm.com        self.objects = []
888706Sandreas.hansson@arm.com        self.TBEType   = None
898706Sandreas.hansson@arm.com        self.EntryType = None
906928SBrad.Beckmann@amd.com        self.debug_flags = set()
916928SBrad.Beckmann@amd.com        self.debug_flags.add('RubyGenerated')
926928SBrad.Beckmann@amd.com        self.debug_flags.add('RubySlicc')
936166Ssteve.reinhardt@amd.com
948801Sgblack@eecs.umich.edu    def __repr__(self):
956928SBrad.Beckmann@amd.com        return "[StateMachine: %s]" % self.ident
966928SBrad.Beckmann@amd.com
976928SBrad.Beckmann@amd.com    def addState(self, state):
986928SBrad.Beckmann@amd.com        assert self.table is None
99        self.states[state.ident] = state
100
101    def addEvent(self, event):
102        assert self.table is None
103        self.events[event.ident] = event
104
105    def addAction(self, action):
106        assert self.table is None
107
108        # Check for duplicate action
109        for other in self.actions.itervalues():
110            if action.ident == other.ident:
111                action.warning("Duplicate action definition: %s" % action.ident)
112                action.error("Duplicate action definition: %s" % action.ident)
113            if action.short == other.short:
114                other.warning("Duplicate action shorthand: %s" % other.ident)
115                other.warning("    shorthand = %s" % other.short)
116                action.warning("Duplicate action shorthand: %s" % action.ident)
117                action.error("    shorthand = %s" % action.short)
118
119        self.actions[action.ident] = action
120
121    def addDebugFlag(self, flag):
122        self.debug_flags.add(flag)
123
124    def addRequestType(self, request_type):
125        assert self.table is None
126        self.request_types[request_type.ident] = request_type
127
128    def addTransition(self, trans):
129        assert self.table is None
130        self.transitions.append(trans)
131
132    def addInPort(self, var):
133        self.in_ports.append(var)
134
135    def addFunc(self, func):
136        # register func in the symbol table
137        self.symtab.registerSym(str(func), func)
138        self.functions.append(func)
139
140    def addObject(self, obj):
141        self.symtab.registerSym(str(obj), obj)
142        self.objects.append(obj)
143
144    def addType(self, type):
145        type_ident = '%s' % type.c_ident
146
147        if type_ident == "%s_TBE" %self.ident:
148            if self.TBEType != None:
149                self.error("Multiple Transaction Buffer types in a " \
150                           "single machine.");
151            self.TBEType = type
152
153        elif "interface" in type and "AbstractCacheEntry" == type["interface"]:
154            if "main" in type and "false" == type["main"].lower():
155                pass # this isn't the EntryType
156            else:
157                if self.EntryType != None:
158                    self.error("Multiple AbstractCacheEntry types in a " \
159                               "single machine.");
160                self.EntryType = type
161
162    # Needs to be called before accessing the table
163    def buildTable(self):
164        assert self.table is None
165
166        table = {}
167
168        for trans in self.transitions:
169            # Track which actions we touch so we know if we use them
170            # all -- really this should be done for all symbols as
171            # part of the symbol table, then only trigger it for
172            # Actions, States, Events, etc.
173
174            for action in trans.actions:
175                action.used = True
176
177            index = (trans.state, trans.event)
178            if index in table:
179                table[index].warning("Duplicate transition: %s" % table[index])
180                trans.error("Duplicate transition: %s" % trans)
181            table[index] = trans
182
183        # Look at all actions to make sure we used them all
184        for action in self.actions.itervalues():
185            if not action.used:
186                error_msg = "Unused action: %s" % action.ident
187                if "desc" in action:
188                    error_msg += ", "  + action.desc
189                action.warning(error_msg)
190        self.table = table
191
192    # determine the port->msg buffer mappings
193    def getBufferMaps(self, ident):
194        msg_bufs = []
195        port_to_buf_map = {}
196        in_msg_bufs = {}
197        for port in self.in_ports:
198            buf_name = "m_%s_ptr" % port.buffer_expr.name
199            msg_bufs.append(buf_name)
200            port_to_buf_map[port] = msg_bufs.index(buf_name)
201            if buf_name not in in_msg_bufs:
202                in_msg_bufs[buf_name] = [port]
203            else:
204                in_msg_bufs[buf_name].append(port)
205        return port_to_buf_map, in_msg_bufs, msg_bufs
206
207    def writeCodeFiles(self, path, includes):
208        self.printControllerPython(path)
209        self.printControllerHH(path)
210        self.printControllerCC(path, includes)
211        self.printCSwitch(path)
212        self.printCWakeup(path, includes)
213
214    def printControllerPython(self, path):
215        code = self.symtab.codeFormatter()
216        ident = self.ident
217
218        py_ident = "%s_Controller" % ident
219        c_ident = "%s_Controller" % self.ident
220
221        code('''
222from m5.params import *
223from m5.SimObject import SimObject
224from Controller import RubyController
225
226class $py_ident(RubyController):
227    type = '$py_ident'
228    cxx_header = 'mem/protocol/${c_ident}.hh'
229''')
230        code.indent()
231        for param in self.config_parameters:
232            dflt_str = ''
233
234            if param.rvalue is not None:
235                dflt_str = str(param.rvalue.inline()) + ', '
236
237            if param.type_ast.type.c_ident == "MessageBuffer":
238                # The MessageBuffer MUST be instantiated in the protocol config
239                code('${{param.ident}} = Param.MessageBuffer("")')
240
241            elif python_class_map.has_key(param.type_ast.type.c_ident):
242                python_type = python_class_map[param.type_ast.type.c_ident]
243                code('${{param.ident}} = Param.${{python_type}}(${dflt_str}"")')
244
245            else:
246                self.error("Unknown c++ to python class conversion for c++ " \
247                           "type: '%s'. Please update the python_class_map " \
248                           "in StateMachine.py", param.type_ast.type.c_ident)
249
250        # Also add any MessageBuffers declared internally to the controller
251        # Note: This includes mandatory and memory queues
252        for var in self.objects:
253            if var.type.c_ident == "MessageBuffer":
254                code('${{var.ident}} = Param.MessageBuffer("")')
255
256        code.dedent()
257        code.write(path, '%s.py' % py_ident)
258
259
260    def printControllerHH(self, path):
261        '''Output the method declarations for the class declaration'''
262        code = self.symtab.codeFormatter()
263        ident = self.ident
264        c_ident = "%s_Controller" % self.ident
265
266        code('''
267/** \\file $c_ident.hh
268 *
269 * Auto generated C++ code started by $__file__:$__line__
270 * Created by slicc definition of Module "${{self.short}}"
271 */
272
273#ifndef __${ident}_CONTROLLER_HH__
274#define __${ident}_CONTROLLER_HH__
275
276#include <iostream>
277#include <sstream>
278#include <string>
279
280#include "mem/protocol/TransitionResult.hh"
281#include "mem/protocol/Types.hh"
282#include "mem/ruby/common/Consumer.hh"
283#include "mem/ruby/slicc_interface/AbstractController.hh"
284#include "params/$c_ident.hh"
285
286''')
287
288        seen_types = set()
289        for var in self.objects:
290            if var.type.ident not in seen_types and not var.type.isPrimitive:
291                code('#include "mem/protocol/${{var.type.c_ident}}.hh"')
292                seen_types.add(var.type.ident)
293
294        # for adding information to the protocol debug trace
295        code('''
296extern std::stringstream ${ident}_transitionComment;
297
298class $c_ident : public AbstractController
299{
300  public:
301    typedef ${c_ident}Params Params;
302    $c_ident(const Params *p);
303    static int getNumControllers();
304    void init();
305
306    MessageBuffer* getMandatoryQueue() const;
307    MessageBuffer* getMemoryQueue() const;
308    void initNetQueues();
309
310    void print(std::ostream& out) const;
311    void wakeup();
312    void resetStats();
313    void regStats();
314    void collateStats();
315
316    void recordCacheTrace(int cntrl, CacheRecorder* tr);
317    Sequencer* getSequencer() const;
318
319    int functionalWriteBuffers(PacketPtr&);
320
321    void countTransition(${ident}_State state, ${ident}_Event event);
322    void possibleTransition(${ident}_State state, ${ident}_Event event);
323    uint64 getEventCount(${ident}_Event event);
324    bool isPossible(${ident}_State state, ${ident}_Event event);
325    uint64 getTransitionCount(${ident}_State state, ${ident}_Event event);
326
327private:
328''')
329
330        code.indent()
331        # added by SS
332        for param in self.config_parameters:
333            if param.pointer:
334                code('${{param.type_ast.type}}* m_${{param.ident}}_ptr;')
335            else:
336                code('${{param.type_ast.type}} m_${{param.ident}};')
337
338        code('''
339TransitionResult doTransition(${ident}_Event event,
340''')
341
342        if self.EntryType != None:
343            code('''
344                              ${{self.EntryType.c_ident}}* m_cache_entry_ptr,
345''')
346        if self.TBEType != None:
347            code('''
348                              ${{self.TBEType.c_ident}}* m_tbe_ptr,
349''')
350
351        code('''
352                              Addr addr);
353
354TransitionResult doTransitionWorker(${ident}_Event event,
355                                    ${ident}_State state,
356                                    ${ident}_State& next_state,
357''')
358
359        if self.TBEType != None:
360            code('''
361                                    ${{self.TBEType.c_ident}}*& m_tbe_ptr,
362''')
363        if self.EntryType != None:
364            code('''
365                                    ${{self.EntryType.c_ident}}*& m_cache_entry_ptr,
366''')
367
368        code('''
369                                    Addr addr);
370
371int m_counters[${ident}_State_NUM][${ident}_Event_NUM];
372int m_event_counters[${ident}_Event_NUM];
373bool m_possible[${ident}_State_NUM][${ident}_Event_NUM];
374
375static std::vector<Stats::Vector *> eventVec;
376static std::vector<std::vector<Stats::Vector *> > transVec;
377static int m_num_controllers;
378
379// Internal functions
380''')
381
382        for func in self.functions:
383            proto = func.prototype
384            if proto:
385                code('$proto')
386
387        if self.EntryType != None:
388            code('''
389
390// Set and Reset for cache_entry variable
391void set_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, AbstractCacheEntry* m_new_cache_entry);
392void unset_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr);
393''')
394
395        if self.TBEType != None:
396            code('''
397
398// Set and Reset for tbe variable
399void set_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${ident}_TBE* m_new_tbe);
400void unset_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr);
401''')
402
403        # Prototype the actions that the controller can take
404        code('''
405
406// Actions
407''')
408        if self.TBEType != None and self.EntryType != None:
409            for action in self.actions.itervalues():
410                code('/** \\brief ${{action.desc}} */')
411                code('void ${{action.ident}}(${{self.TBEType.c_ident}}*& '
412                     'm_tbe_ptr, ${{self.EntryType.c_ident}}*& '
413                     'm_cache_entry_ptr, Addr addr);')
414        elif self.TBEType != None:
415            for action in self.actions.itervalues():
416                code('/** \\brief ${{action.desc}} */')
417                code('void ${{action.ident}}(${{self.TBEType.c_ident}}*& '
418                     'm_tbe_ptr, Addr addr);')
419        elif self.EntryType != None:
420            for action in self.actions.itervalues():
421                code('/** \\brief ${{action.desc}} */')
422                code('void ${{action.ident}}(${{self.EntryType.c_ident}}*& '
423                     'm_cache_entry_ptr, Addr addr);')
424        else:
425            for action in self.actions.itervalues():
426                code('/** \\brief ${{action.desc}} */')
427                code('void ${{action.ident}}(Addr addr);')
428
429        # the controller internal variables
430        code('''
431
432// Objects
433''')
434        for var in self.objects:
435            th = var.get("template", "")
436            code('${{var.type.c_ident}}$th* m_${{var.ident}}_ptr;')
437
438        code.dedent()
439        code('};')
440        code('#endif // __${ident}_CONTROLLER_H__')
441        code.write(path, '%s.hh' % c_ident)
442
443    def printControllerCC(self, path, includes):
444        '''Output the actions for performing the actions'''
445
446        code = self.symtab.codeFormatter()
447        ident = self.ident
448        c_ident = "%s_Controller" % self.ident
449
450        code('''
451/** \\file $c_ident.cc
452 *
453 * Auto generated C++ code started by $__file__:$__line__
454 * Created by slicc definition of Module "${{self.short}}"
455 */
456
457#include <sys/types.h>
458#include <unistd.h>
459
460#include <cassert>
461#include <sstream>
462#include <string>
463#include <typeinfo>
464
465#include "base/compiler.hh"
466#include "base/cprintf.hh"
467
468''')
469        for f in self.debug_flags:
470            code('#include "debug/${{f}}.hh"')
471        code('''
472#include "mem/protocol/${ident}_Controller.hh"
473#include "mem/protocol/${ident}_Event.hh"
474#include "mem/protocol/${ident}_State.hh"
475#include "mem/protocol/Types.hh"
476#include "mem/ruby/system/System.hh"
477
478''')
479        for include_path in includes:
480            code('#include "${{include_path}}"')
481
482        code('''
483
484using namespace std;
485''')
486
487        # include object classes
488        seen_types = set()
489        for var in self.objects:
490            if var.type.ident not in seen_types and not var.type.isPrimitive:
491                code('#include "mem/protocol/${{var.type.c_ident}}.hh"')
492            seen_types.add(var.type.ident)
493
494        num_in_ports = len(self.in_ports)
495
496        code('''
497$c_ident *
498${c_ident}Params::create()
499{
500    return new $c_ident(this);
501}
502
503int $c_ident::m_num_controllers = 0;
504std::vector<Stats::Vector *>  $c_ident::eventVec;
505std::vector<std::vector<Stats::Vector *> >  $c_ident::transVec;
506
507// for adding information to the protocol debug trace
508stringstream ${ident}_transitionComment;
509
510#ifndef NDEBUG
511#define APPEND_TRANSITION_COMMENT(str) (${ident}_transitionComment << str)
512#else
513#define APPEND_TRANSITION_COMMENT(str) do {} while (0)
514#endif
515
516/** \\brief constructor */
517$c_ident::$c_ident(const Params *p)
518    : AbstractController(p)
519{
520    m_machineID.type = MachineType_${ident};
521    m_machineID.num = m_version;
522    m_num_controllers++;
523
524    m_in_ports = $num_in_ports;
525''')
526        code.indent()
527
528        #
529        # After initializing the universal machine parameters, initialize the
530        # this machines config parameters.  Also if these configuration params
531        # include a sequencer, connect the it to the controller.
532        #
533        for param in self.config_parameters:
534            if param.pointer:
535                code('m_${{param.ident}}_ptr = p->${{param.ident}};')
536            else:
537                code('m_${{param.ident}} = p->${{param.ident}};')
538
539            if re.compile("sequencer").search(param.ident):
540                code('m_${{param.ident}}_ptr->setController(this);')
541
542        for var in self.objects:
543            # Some MessageBuffers (e.g. mandatory and memory queues) are
544            # instantiated internally to StateMachines but exposed to
545            # components outside SLICC, so make sure to set up this
546            # controller as their receivers
547            if var.type.c_ident == "MessageBuffer":
548                code('''
549m_${{var.ident}}_ptr = p->${{var.ident}};
550m_${{var.ident}}_ptr->setReceiver(this);
551''')
552
553        code('''
554
555for (int state = 0; state < ${ident}_State_NUM; state++) {
556    for (int event = 0; event < ${ident}_Event_NUM; event++) {
557        m_possible[state][event] = false;
558        m_counters[state][event] = 0;
559    }
560}
561for (int event = 0; event < ${ident}_Event_NUM; event++) {
562    m_event_counters[event] = 0;
563}
564''')
565        code.dedent()
566        code('''
567}
568
569void
570$c_ident::initNetQueues()
571{
572    MachineType machine_type = string_to_MachineType("${{self.ident}}");
573    int base M5_VAR_USED = MachineType_base_number(machine_type);
574
575''')
576        code.indent()
577
578        # set for maintaining the vnet, direction pairs already seen for this
579        # machine.  This map helps in implementing the check for avoiding
580        # multiple message buffers being mapped to the same vnet.
581        vnet_dir_set = set()
582
583        for var in self.config_parameters:
584            if "network" in var:
585                vtype = var.type_ast.type
586                vid = "m_%s_ptr" % var.ident
587
588                code('assert($vid != NULL);')
589
590                # Network port object
591                network = var["network"]
592
593                if "virtual_network" in var:
594                    vnet = var["virtual_network"]
595                    vnet_type = var["vnet_type"]
596
597                    assert (vnet, network) not in vnet_dir_set
598                    vnet_dir_set.add((vnet,network))
599
600                    code('''
601m_net_ptr->set${network}NetQueue(m_version + base, $vid->getOrdered(), $vnet,
602                                 "$vnet_type", $vid);
603''')
604                # Set the end
605                if network == "To":
606                    code('$vid->setSender(this);')
607                else:
608                    code('$vid->setReceiver(this);')
609
610                # Set Priority
611                if "rank" in var:
612                    code('$vid->setPriority(${{var["rank"]}})')
613
614        code.dedent()
615        code('''
616}
617
618void
619$c_ident::init()
620{
621    // initialize objects
622    initNetQueues();
623''')
624
625        code.indent()
626
627        for var in self.objects:
628            vtype = var.type
629            vid = "m_%s_ptr" % var.ident
630            if "network" not in var:
631                # Not a network port object
632                if "primitive" in vtype:
633                    code('$vid = new ${{vtype.c_ident}};')
634                    if "default" in var:
635                        code('(*$vid) = ${{var["default"]}};')
636                else:
637                    # Normal Object
638                    if var.type.c_ident != "MessageBuffer":
639                        th = var.get("template", "")
640                        expr = "%s  = new %s%s" % (vid, vtype.c_ident, th)
641                        args = ""
642                        if "non_obj" not in vtype and not vtype.isEnumeration:
643                            args = var.get("constructor", "")
644                        code('$expr($args);')
645
646                    code('assert($vid != NULL);')
647
648                    if "default" in var:
649                        code('*$vid = ${{var["default"]}}; // Object default')
650                    elif "default" in vtype:
651                        comment = "Type %s default" % vtype.ident
652                        code('*$vid = ${{vtype["default"]}}; // $comment')
653
654                    # Set Priority
655                    if vtype.isBuffer and "rank" in var:
656                        code('$vid->setPriority(${{var["rank"]}});')
657
658                    # Set sender and receiver for trigger queue
659                    if var.ident.find("triggerQueue") >= 0:
660                        code('$vid->setSender(this);')
661                        code('$vid->setReceiver(this);')
662                    elif vtype.c_ident == "TimerTable":
663                        code('$vid->setClockObj(this);')
664                    elif var.ident.find("optionalQueue") >= 0:
665                        code('$vid->setSender(this);')
666                        code('$vid->setReceiver(this);')
667
668        # Set the prefetchers
669        code()
670        for prefetcher in self.prefetchers:
671            code('${{prefetcher.code}}.setController(this);')
672
673        code()
674        for port in self.in_ports:
675            # Set the queue consumers
676            code('${{port.code}}.setConsumer(this);')
677
678        # Initialize the transition profiling
679        code()
680        for trans in self.transitions:
681            # Figure out if we stall
682            stall = False
683            for action in trans.actions:
684                if action.ident == "z_stall":
685                    stall = True
686
687            # Only possible if it is not a 'z' case
688            if not stall:
689                state = "%s_State_%s" % (self.ident, trans.state.ident)
690                event = "%s_Event_%s" % (self.ident, trans.event.ident)
691                code('possibleTransition($state, $event);')
692
693        code.dedent()
694        code('''
695    AbstractController::init();
696    resetStats();
697}
698''')
699
700        mq_ident = "NULL"
701        for port in self.in_ports:
702            if port.code.find("mandatoryQueue_ptr") >= 0:
703                mq_ident = "m_mandatoryQueue_ptr"
704
705        memq_ident = "NULL"
706        for port in self.in_ports:
707            if port.code.find("responseFromMemory_ptr") >= 0:
708                memq_ident = "m_responseFromMemory_ptr"
709
710        seq_ident = "NULL"
711        for param in self.config_parameters:
712            if param.ident == "sequencer":
713                assert(param.pointer)
714                seq_ident = "m_%s_ptr" % param.ident
715
716        code('''
717
718void
719$c_ident::regStats()
720{
721    AbstractController::regStats();
722
723    if (m_version == 0) {
724        for (${ident}_Event event = ${ident}_Event_FIRST;
725             event < ${ident}_Event_NUM; ++event) {
726            Stats::Vector *t = new Stats::Vector();
727            t->init(m_num_controllers);
728            t->name(params()->ruby_system->name() + ".${c_ident}." +
729                ${ident}_Event_to_string(event));
730            t->flags(Stats::pdf | Stats::total | Stats::oneline |
731                     Stats::nozero);
732
733            eventVec.push_back(t);
734        }
735
736        for (${ident}_State state = ${ident}_State_FIRST;
737             state < ${ident}_State_NUM; ++state) {
738
739            transVec.push_back(std::vector<Stats::Vector *>());
740
741            for (${ident}_Event event = ${ident}_Event_FIRST;
742                 event < ${ident}_Event_NUM; ++event) {
743
744                Stats::Vector *t = new Stats::Vector();
745                t->init(m_num_controllers);
746                t->name(params()->ruby_system->name() + ".${c_ident}." +
747                        ${ident}_State_to_string(state) +
748                        "." + ${ident}_Event_to_string(event));
749
750                t->flags(Stats::pdf | Stats::total | Stats::oneline |
751                         Stats::nozero);
752                transVec[state].push_back(t);
753            }
754        }
755    }
756}
757
758void
759$c_ident::collateStats()
760{
761    for (${ident}_Event event = ${ident}_Event_FIRST;
762         event < ${ident}_Event_NUM; ++event) {
763        for (unsigned int i = 0; i < m_num_controllers; ++i) {
764            RubySystem *rs = params()->ruby_system;
765            std::map<uint32_t, AbstractController *>::iterator it =
766                     rs->m_abstract_controls[MachineType_${ident}].find(i);
767            assert(it != rs->m_abstract_controls[MachineType_${ident}].end());
768            (*eventVec[event])[i] =
769                (($c_ident *)(*it).second)->getEventCount(event);
770        }
771    }
772
773    for (${ident}_State state = ${ident}_State_FIRST;
774         state < ${ident}_State_NUM; ++state) {
775
776        for (${ident}_Event event = ${ident}_Event_FIRST;
777             event < ${ident}_Event_NUM; ++event) {
778
779            for (unsigned int i = 0; i < m_num_controllers; ++i) {
780                RubySystem *rs = params()->ruby_system;
781                std::map<uint32_t, AbstractController *>::iterator it =
782                         rs->m_abstract_controls[MachineType_${ident}].find(i);
783                assert(it != rs->m_abstract_controls[MachineType_${ident}].end());
784                (*transVec[state][event])[i] =
785                    (($c_ident *)(*it).second)->getTransitionCount(state, event);
786            }
787        }
788    }
789}
790
791void
792$c_ident::countTransition(${ident}_State state, ${ident}_Event event)
793{
794    assert(m_possible[state][event]);
795    m_counters[state][event]++;
796    m_event_counters[event]++;
797}
798void
799$c_ident::possibleTransition(${ident}_State state,
800                             ${ident}_Event event)
801{
802    m_possible[state][event] = true;
803}
804
805uint64
806$c_ident::getEventCount(${ident}_Event event)
807{
808    return m_event_counters[event];
809}
810
811bool
812$c_ident::isPossible(${ident}_State state, ${ident}_Event event)
813{
814    return m_possible[state][event];
815}
816
817uint64
818$c_ident::getTransitionCount(${ident}_State state,
819                             ${ident}_Event event)
820{
821    return m_counters[state][event];
822}
823
824int
825$c_ident::getNumControllers()
826{
827    return m_num_controllers;
828}
829
830MessageBuffer*
831$c_ident::getMandatoryQueue() const
832{
833    return $mq_ident;
834}
835
836MessageBuffer*
837$c_ident::getMemoryQueue() const
838{
839    return $memq_ident;
840}
841
842Sequencer*
843$c_ident::getSequencer() const
844{
845    return $seq_ident;
846}
847
848void
849$c_ident::print(ostream& out) const
850{
851    out << "[$c_ident " << m_version << "]";
852}
853
854void $c_ident::resetStats()
855{
856    for (int state = 0; state < ${ident}_State_NUM; state++) {
857        for (int event = 0; event < ${ident}_Event_NUM; event++) {
858            m_counters[state][event] = 0;
859        }
860    }
861
862    for (int event = 0; event < ${ident}_Event_NUM; event++) {
863        m_event_counters[event] = 0;
864    }
865
866    AbstractController::resetStats();
867}
868''')
869
870        if self.EntryType != None:
871            code('''
872
873// Set and Reset for cache_entry variable
874void
875$c_ident::set_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, AbstractCacheEntry* m_new_cache_entry)
876{
877  m_cache_entry_ptr = (${{self.EntryType.c_ident}}*)m_new_cache_entry;
878}
879
880void
881$c_ident::unset_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr)
882{
883  m_cache_entry_ptr = 0;
884}
885''')
886
887        if self.TBEType != None:
888            code('''
889
890// Set and Reset for tbe variable
891void
892$c_ident::set_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.TBEType.c_ident}}* m_new_tbe)
893{
894  m_tbe_ptr = m_new_tbe;
895}
896
897void
898$c_ident::unset_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr)
899{
900  m_tbe_ptr = NULL;
901}
902''')
903
904        code('''
905
906void
907$c_ident::recordCacheTrace(int cntrl, CacheRecorder* tr)
908{
909''')
910        #
911        # Record cache contents for all associated caches.
912        #
913        code.indent()
914        for param in self.config_parameters:
915            if param.type_ast.type.ident == "CacheMemory":
916                assert(param.pointer)
917                code('m_${{param.ident}}_ptr->recordCacheContents(cntrl, tr);')
918
919        code.dedent()
920        code('''
921}
922
923// Actions
924''')
925        if self.TBEType != None and self.EntryType != None:
926            for action in self.actions.itervalues():
927                if "c_code" not in action:
928                 continue
929
930                code('''
931/** \\brief ${{action.desc}} */
932void
933$c_ident::${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.EntryType.c_ident}}*& m_cache_entry_ptr, Addr addr)
934{
935    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
936    try {
937       ${{action["c_code"]}}
938    } catch (const RejectException & e) {
939       fatal("Error in action ${{ident}}:${{action.ident}}: "
940             "executed a peek statement with the wrong message "
941             "type specified. ");
942    }
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, Addr 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, Addr 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}}(Addr 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 writes to messages buffered in the controller
995        code('''
996int
997$c_ident::functionalWriteBuffers(PacketPtr& pkt)
998{
999    int num_functional_writes = 0;
1000''')
1001        for var in self.objects:
1002            vtype = var.type
1003            if vtype.isBuffer:
1004                vid = "m_%s_ptr" % var.ident
1005                code('num_functional_writes += $vid->functionalWrite(pkt);')
1006
1007        for var in self.config_parameters:
1008            vtype = var.type_ast.type
1009            if vtype.isBuffer:
1010                vid = "m_%s_ptr" % var.ident
1011                code('num_functional_writes += $vid->functionalWrite(pkt);')
1012
1013        code('''
1014    return num_functional_writes;
1015}
1016''')
1017
1018        code.write(path, "%s.cc" % c_ident)
1019
1020    def printCWakeup(self, path, includes):
1021        '''Output the wakeup loop for the events'''
1022
1023        code = self.symtab.codeFormatter()
1024        ident = self.ident
1025
1026        outputRequest_types = True
1027        if len(self.request_types) == 0:
1028            outputRequest_types = False
1029
1030        code('''
1031// Auto generated C++ code started by $__file__:$__line__
1032// ${ident}: ${{self.short}}
1033
1034#include <sys/types.h>
1035#include <unistd.h>
1036
1037#include <cassert>
1038#include <typeinfo>
1039
1040#include "base/misc.hh"
1041
1042''')
1043        for f in self.debug_flags:
1044            code('#include "debug/${{f}}.hh"')
1045        code('''
1046#include "mem/protocol/${ident}_Controller.hh"
1047#include "mem/protocol/${ident}_Event.hh"
1048#include "mem/protocol/${ident}_State.hh"
1049
1050''')
1051
1052        if outputRequest_types:
1053            code('''#include "mem/protocol/${ident}_RequestType.hh"''')
1054
1055        code('''
1056#include "mem/protocol/Types.hh"
1057#include "mem/ruby/system/System.hh"
1058
1059''')
1060
1061
1062        for include_path in includes:
1063            code('#include "${{include_path}}"')
1064
1065        port_to_buf_map, in_msg_bufs, msg_bufs = self.getBufferMaps(ident)
1066
1067        code('''
1068
1069using namespace std;
1070
1071void
1072${ident}_Controller::wakeup()
1073{
1074    int counter = 0;
1075    while (true) {
1076        unsigned char rejected[${{len(msg_bufs)}}];
1077        memset(rejected, 0, sizeof(unsigned char)*${{len(msg_bufs)}});
1078        // Some cases will put us into an infinite loop without this limit
1079        assert(counter <= m_transitions_per_cycle);
1080        if (counter == m_transitions_per_cycle) {
1081            // Count how often we are fully utilized
1082            m_fully_busy_cycles++;
1083
1084            // Wakeup in another cycle and try again
1085            scheduleEvent(Cycles(1));
1086            break;
1087        }
1088''')
1089
1090        code.indent()
1091        code.indent()
1092
1093        # InPorts
1094        #
1095        for port in self.in_ports:
1096            code.indent()
1097            code('// ${ident}InPort $port')
1098            if port.pairs.has_key("rank"):
1099                code('m_cur_in_port = ${{port.pairs["rank"]}};')
1100            else:
1101                code('m_cur_in_port = 0;')
1102            if port in port_to_buf_map:
1103                code('try {')
1104                code.indent()
1105            code('${{port["c_code_in_port"]}}')
1106
1107            if port in port_to_buf_map:
1108                code.dedent()
1109                code('''
1110            } catch (const RejectException & e) {
1111                rejected[${{port_to_buf_map[port]}}]++;
1112            }
1113''')
1114            code.dedent()
1115            code('')
1116
1117        code.dedent()
1118        code.dedent()
1119        code('''
1120        // If we got this far, we have nothing left todo or something went
1121        // wrong''')
1122        for buf_name, ports in in_msg_bufs.items():
1123            if len(ports) > 1:
1124                # only produce checks when a buffer is shared by multiple ports
1125                code('''
1126        if (${{buf_name}}->isReady() && rejected[${{port_to_buf_map[ports[0]]}}] == ${{len(ports)}})
1127        {
1128            // no port claimed the message on the top of this buffer
1129            panic("Runtime Error at Ruby Time: %d. "
1130                  "All ports rejected a message. "
1131                  "You are probably sending a message type to this controller "
1132                  "over a virtual network that do not define an in_port for "
1133                  "the incoming message type.\\n",
1134                  Cycles(1));
1135        }
1136''')
1137        code('''
1138        break;
1139    }
1140}
1141''')
1142
1143        code.write(path, "%s_Wakeup.cc" % self.ident)
1144
1145    def printCSwitch(self, path):
1146        '''Output switch statement for transition table'''
1147
1148        code = self.symtab.codeFormatter()
1149        ident = self.ident
1150
1151        code('''
1152// Auto generated C++ code started by $__file__:$__line__
1153// ${ident}: ${{self.short}}
1154
1155#include <cassert>
1156
1157#include "base/misc.hh"
1158#include "base/trace.hh"
1159#include "debug/ProtocolTrace.hh"
1160#include "debug/RubyGenerated.hh"
1161#include "mem/protocol/${ident}_Controller.hh"
1162#include "mem/protocol/${ident}_Event.hh"
1163#include "mem/protocol/${ident}_State.hh"
1164#include "mem/protocol/Types.hh"
1165#include "mem/ruby/system/System.hh"
1166
1167#define HASH_FUN(state, event)  ((int(state)*${ident}_Event_NUM)+int(event))
1168
1169#define GET_TRANSITION_COMMENT() (${ident}_transitionComment.str())
1170#define CLEAR_TRANSITION_COMMENT() (${ident}_transitionComment.str(""))
1171
1172TransitionResult
1173${ident}_Controller::doTransition(${ident}_Event event,
1174''')
1175        if self.EntryType != None:
1176            code('''
1177                                  ${{self.EntryType.c_ident}}* m_cache_entry_ptr,
1178''')
1179        if self.TBEType != None:
1180            code('''
1181                                  ${{self.TBEType.c_ident}}* m_tbe_ptr,
1182''')
1183        code('''
1184                                  Addr addr)
1185{
1186''')
1187        code.indent()
1188
1189        if self.TBEType != None and self.EntryType != None:
1190            code('${ident}_State state = getState(m_tbe_ptr, m_cache_entry_ptr, addr);')
1191        elif self.TBEType != None:
1192            code('${ident}_State state = getState(m_tbe_ptr, addr);')
1193        elif self.EntryType != None:
1194            code('${ident}_State state = getState(m_cache_entry_ptr, addr);')
1195        else:
1196            code('${ident}_State state = getState(addr);')
1197
1198        code('''
1199${ident}_State next_state = state;
1200
1201DPRINTF(RubyGenerated, "%s, Time: %lld, state: %s, event: %s, addr: %s\\n",
1202        *this, curCycle(), ${ident}_State_to_string(state),
1203        ${ident}_Event_to_string(event), addr);
1204
1205TransitionResult result =
1206''')
1207        if self.TBEType != None and self.EntryType != None:
1208            code('doTransitionWorker(event, state, next_state, m_tbe_ptr, m_cache_entry_ptr, addr);')
1209        elif self.TBEType != None:
1210            code('doTransitionWorker(event, state, next_state, m_tbe_ptr, addr);')
1211        elif self.EntryType != None:
1212            code('doTransitionWorker(event, state, next_state, m_cache_entry_ptr, addr);')
1213        else:
1214            code('doTransitionWorker(event, state, next_state, addr);')
1215
1216        port_to_buf_map, in_msg_bufs, msg_bufs = self.getBufferMaps(ident)
1217
1218        code('''
1219
1220if (result == TransitionResult_Valid) {
1221    DPRINTF(RubyGenerated, "next_state: %s\\n",
1222            ${ident}_State_to_string(next_state));
1223    countTransition(state, event);
1224
1225    DPRINTFR(ProtocolTrace, "%15d %3s %10s%20s %6s>%-6s %#x %s\\n",
1226             curTick(), m_version, "${ident}",
1227             ${ident}_Event_to_string(event),
1228             ${ident}_State_to_string(state),
1229             ${ident}_State_to_string(next_state),
1230             addr, GET_TRANSITION_COMMENT());
1231
1232    CLEAR_TRANSITION_COMMENT();
1233''')
1234        if self.TBEType != None and self.EntryType != None:
1235            code('setState(m_tbe_ptr, m_cache_entry_ptr, addr, next_state);')
1236            code('setAccessPermission(m_cache_entry_ptr, addr, next_state);')
1237        elif self.TBEType != None:
1238            code('setState(m_tbe_ptr, addr, next_state);')
1239            code('setAccessPermission(addr, next_state);')
1240        elif self.EntryType != None:
1241            code('setState(m_cache_entry_ptr, addr, next_state);')
1242            code('setAccessPermission(m_cache_entry_ptr, addr, next_state);')
1243        else:
1244            code('setState(addr, next_state);')
1245            code('setAccessPermission(addr, next_state);')
1246
1247        code('''
1248} else if (result == TransitionResult_ResourceStall) {
1249    DPRINTFR(ProtocolTrace, "%15s %3s %10s%20s %6s>%-6s %#x %s\\n",
1250             curTick(), m_version, "${ident}",
1251             ${ident}_Event_to_string(event),
1252             ${ident}_State_to_string(state),
1253             ${ident}_State_to_string(next_state),
1254             addr, "Resource Stall");
1255} else if (result == TransitionResult_ProtocolStall) {
1256    DPRINTF(RubyGenerated, "stalling\\n");
1257    DPRINTFR(ProtocolTrace, "%15s %3s %10s%20s %6s>%-6s %#x %s\\n",
1258             curTick(), m_version, "${ident}",
1259             ${ident}_Event_to_string(event),
1260             ${ident}_State_to_string(state),
1261             ${ident}_State_to_string(next_state),
1262             addr, "Protocol Stall");
1263}
1264
1265return result;
1266''')
1267        code.dedent()
1268        code('''
1269}
1270
1271TransitionResult
1272${ident}_Controller::doTransitionWorker(${ident}_Event event,
1273                                        ${ident}_State state,
1274                                        ${ident}_State& next_state,
1275''')
1276
1277        if self.TBEType != None:
1278            code('''
1279                                        ${{self.TBEType.c_ident}}*& m_tbe_ptr,
1280''')
1281        if self.EntryType != None:
1282                  code('''
1283                                        ${{self.EntryType.c_ident}}*& m_cache_entry_ptr,
1284''')
1285        code('''
1286                                        Addr addr)
1287{
1288    switch(HASH_FUN(state, event)) {
1289''')
1290
1291        # This map will allow suppress generating duplicate code
1292        cases = orderdict()
1293
1294        for trans in self.transitions:
1295            case_string = "%s_State_%s, %s_Event_%s" % \
1296                (self.ident, trans.state.ident, self.ident, trans.event.ident)
1297
1298            case = self.symtab.codeFormatter()
1299            # Only set next_state if it changes
1300            if trans.state != trans.nextState:
1301                if trans.nextState.isWildcard():
1302                    # When * is encountered as an end state of a transition,
1303                    # the next state is determined by calling the
1304                    # machine-specific getNextState function. The next state
1305                    # is determined before any actions of the transition
1306                    # execute, and therefore the next state calculation cannot
1307                    # depend on any of the transitionactions.
1308                    case('next_state = getNextState(addr);')
1309                else:
1310                    ns_ident = trans.nextState.ident
1311                    case('next_state = ${ident}_State_${ns_ident};')
1312
1313            actions = trans.actions
1314            request_types = trans.request_types
1315
1316            # Check for resources
1317            case_sorter = []
1318            res = trans.resources
1319            for key,val in res.iteritems():
1320                val = '''
1321if (!%s.areNSlotsAvailable(%s))
1322    return TransitionResult_ResourceStall;
1323''' % (key.code, val)
1324                case_sorter.append(val)
1325
1326            # Check all of the request_types for resource constraints
1327            for request_type in request_types:
1328                val = '''
1329if (!checkResourceAvailable(%s_RequestType_%s, addr)) {
1330    return TransitionResult_ResourceStall;
1331}
1332''' % (self.ident, request_type.ident)
1333                case_sorter.append(val)
1334
1335            # Emit the code sequences in a sorted order.  This makes the
1336            # output deterministic (without this the output order can vary
1337            # since Map's keys() on a vector of pointers is not deterministic
1338            for c in sorted(case_sorter):
1339                case("$c")
1340
1341            # Record access types for this transition
1342            for request_type in request_types:
1343                case('recordRequestType(${ident}_RequestType_${{request_type.ident}}, addr);')
1344
1345            # Figure out if we stall
1346            stall = False
1347            for action in actions:
1348                if action.ident == "z_stall":
1349                    stall = True
1350                    break
1351
1352            if stall:
1353                case('return TransitionResult_ProtocolStall;')
1354            else:
1355                if self.TBEType != None and self.EntryType != None:
1356                    for action in actions:
1357                        case('${{action.ident}}(m_tbe_ptr, m_cache_entry_ptr, addr);')
1358                elif self.TBEType != None:
1359                    for action in actions:
1360                        case('${{action.ident}}(m_tbe_ptr, addr);')
1361                elif self.EntryType != None:
1362                    for action in actions:
1363                        case('${{action.ident}}(m_cache_entry_ptr, addr);')
1364                else:
1365                    for action in actions:
1366                        case('${{action.ident}}(addr);')
1367                case('return TransitionResult_Valid;')
1368
1369            case = str(case)
1370
1371            # Look to see if this transition code is unique.
1372            if case not in cases:
1373                cases[case] = []
1374
1375            cases[case].append(case_string)
1376
1377        # Walk through all of the unique code blocks and spit out the
1378        # corresponding case statement elements
1379        for case,transitions in cases.iteritems():
1380            # Iterative over all the multiple transitions that share
1381            # the same code
1382            for trans in transitions:
1383                code('  case HASH_FUN($trans):')
1384            code('    $case\n')
1385
1386        code('''
1387      default:
1388        panic("Invalid transition\\n"
1389              "%s time: %d addr: %s event: %s state: %s\\n",
1390              name(), curCycle(), addr, event, state);
1391    }
1392
1393    return TransitionResult_Valid;
1394}
1395''')
1396        code.write(path, "%s_Transitions.cc" % self.ident)
1397
1398
1399    # **************************
1400    # ******* HTML Files *******
1401    # **************************
1402    def frameRef(self, click_href, click_target, over_href, over_num, text):
1403        code = self.symtab.codeFormatter(fix_newlines=False)
1404        code("""<A href=\"$click_href\" target=\"$click_target\" onmouseover=\"
1405    if (parent.frames[$over_num].location != parent.location + '$over_href') {
1406        parent.frames[$over_num].location='$over_href'
1407    }\">
1408    ${{html.formatShorthand(text)}}
1409    </A>""")
1410        return str(code)
1411
1412    def writeHTMLFiles(self, path):
1413        # Create table with no row hilighted
1414        self.printHTMLTransitions(path, None)
1415
1416        # Generate transition tables
1417        for state in self.states.itervalues():
1418            self.printHTMLTransitions(path, state)
1419
1420        # Generate action descriptions
1421        for action in self.actions.itervalues():
1422            name = "%s_action_%s.html" % (self.ident, action.ident)
1423            code = html.createSymbol(action, "Action")
1424            code.write(path, name)
1425
1426        # Generate state descriptions
1427        for state in self.states.itervalues():
1428            name = "%s_State_%s.html" % (self.ident, state.ident)
1429            code = html.createSymbol(state, "State")
1430            code.write(path, name)
1431
1432        # Generate event descriptions
1433        for event in self.events.itervalues():
1434            name = "%s_Event_%s.html" % (self.ident, event.ident)
1435            code = html.createSymbol(event, "Event")
1436            code.write(path, name)
1437
1438    def printHTMLTransitions(self, path, active_state):
1439        code = self.symtab.codeFormatter()
1440
1441        code('''
1442<HTML>
1443<BODY link="blue" vlink="blue">
1444
1445<H1 align="center">${{html.formatShorthand(self.short)}}:
1446''')
1447        code.indent()
1448        for i,machine in enumerate(self.symtab.getAllType(StateMachine)):
1449            mid = machine.ident
1450            if i != 0:
1451                extra = " - "
1452            else:
1453                extra = ""
1454            if machine == self:
1455                code('$extra$mid')
1456            else:
1457                code('$extra<A target="Table" href="${mid}_table.html">$mid</A>')
1458        code.dedent()
1459
1460        code("""
1461</H1>
1462
1463<TABLE border=1>
1464<TR>
1465  <TH> </TH>
1466""")
1467
1468        for event in self.events.itervalues():
1469            href = "%s_Event_%s.html" % (self.ident, event.ident)
1470            ref = self.frameRef(href, "Status", href, "1", event.short)
1471            code('<TH bgcolor=white>$ref</TH>')
1472
1473        code('</TR>')
1474        # -- Body of table
1475        for state in self.states.itervalues():
1476            # -- Each row
1477            if state == active_state:
1478                color = "yellow"
1479            else:
1480                color = "white"
1481
1482            click = "%s_table_%s.html" % (self.ident, state.ident)
1483            over = "%s_State_%s.html" % (self.ident, state.ident)
1484            text = html.formatShorthand(state.short)
1485            ref = self.frameRef(click, "Table", over, "1", state.short)
1486            code('''
1487<TR>
1488  <TH bgcolor=$color>$ref</TH>
1489''')
1490
1491            # -- One column for each event
1492            for event in self.events.itervalues():
1493                trans = self.table.get((state,event), None)
1494                if trans is None:
1495                    # This is the no transition case
1496                    if state == active_state:
1497                        color = "#C0C000"
1498                    else:
1499                        color = "lightgrey"
1500
1501                    code('<TD bgcolor=$color>&nbsp;</TD>')
1502                    continue
1503
1504                next = trans.nextState
1505                stall_action = False
1506
1507                # -- Get the actions
1508                for action in trans.actions:
1509                    if action.ident == "z_stall" or \
1510                       action.ident == "zz_recycleMandatoryQueue":
1511                        stall_action = True
1512
1513                # -- Print out "actions/next-state"
1514                if stall_action:
1515                    if state == active_state:
1516                        color = "#C0C000"
1517                    else:
1518                        color = "lightgrey"
1519
1520                elif active_state and next.ident == active_state.ident:
1521                    color = "aqua"
1522                elif state == active_state:
1523                    color = "yellow"
1524                else:
1525                    color = "white"
1526
1527                code('<TD bgcolor=$color>')
1528                for action in trans.actions:
1529                    href = "%s_action_%s.html" % (self.ident, action.ident)
1530                    ref = self.frameRef(href, "Status", href, "1",
1531                                        action.short)
1532                    code('  $ref')
1533                if next != state:
1534                    if trans.actions:
1535                        code('/')
1536                    click = "%s_table_%s.html" % (self.ident, next.ident)
1537                    over = "%s_State_%s.html" % (self.ident, next.ident)
1538                    ref = self.frameRef(click, "Table", over, "1", next.short)
1539                    code("$ref")
1540                code("</TD>")
1541
1542            # -- Each row
1543            if state == active_state:
1544                color = "yellow"
1545            else:
1546                color = "white"
1547
1548            click = "%s_table_%s.html" % (self.ident, state.ident)
1549            over = "%s_State_%s.html" % (self.ident, state.ident)
1550            ref = self.frameRef(click, "Table", over, "1", state.short)
1551            code('''
1552  <TH bgcolor=$color>$ref</TH>
1553</TR>
1554''')
1555        code('''
1556<!- Column footer->
1557<TR>
1558  <TH> </TH>
1559''')
1560
1561        for event in self.events.itervalues():
1562            href = "%s_Event_%s.html" % (self.ident, event.ident)
1563            ref = self.frameRef(href, "Status", href, "1", event.short)
1564            code('<TH bgcolor=white>$ref</TH>')
1565        code('''
1566</TR>
1567</TABLE>
1568</BODY></HTML>
1569''')
1570
1571
1572        if active_state:
1573            name = "%s_table_%s.html" % (self.ident, active_state.ident)
1574        else:
1575            name = "%s_table.html" % self.ident
1576        code.write(path, name)
1577
1578__all__ = [ "StateMachine" ]
1579