StateMachine.py revision 9996
13970Sgblack@eecs.umich.edu# Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
23005Sstever@eecs.umich.edu# Copyright (c) 2009 The Hewlett-Packard Development Company
33005Sstever@eecs.umich.edu# All rights reserved.
43005Sstever@eecs.umich.edu#
53005Sstever@eecs.umich.edu# Redistribution and use in source and binary forms, with or without
63005Sstever@eecs.umich.edu# modification, are permitted provided that the following conditions are
73005Sstever@eecs.umich.edu# met: redistributions of source code must retain the above copyright
83005Sstever@eecs.umich.edu# notice, this list of conditions and the following disclaimer;
93005Sstever@eecs.umich.edu# redistributions in binary form must reproduce the above copyright
103005Sstever@eecs.umich.edu# notice, this list of conditions and the following disclaimer in the
113005Sstever@eecs.umich.edu# documentation and/or other materials provided with the distribution;
123005Sstever@eecs.umich.edu# neither the name of the copyright holders nor the names of its
133005Sstever@eecs.umich.edu# contributors may be used to endorse or promote products derived from
143005Sstever@eecs.umich.edu# this software without specific prior written permission.
153005Sstever@eecs.umich.edu#
163005Sstever@eecs.umich.edu# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
173005Sstever@eecs.umich.edu# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
183005Sstever@eecs.umich.edu# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
193005Sstever@eecs.umich.edu# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
203005Sstever@eecs.umich.edu# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
213005Sstever@eecs.umich.edu# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
223005Sstever@eecs.umich.edu# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
233005Sstever@eecs.umich.edu# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
243005Sstever@eecs.umich.edu# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
253005Sstever@eecs.umich.edu# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
263005Sstever@eecs.umich.edu# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
273005Sstever@eecs.umich.edu
283005Sstever@eecs.umich.edufrom m5.util import orderdict
292889SN/A
302889SN/Afrom slicc.symbols.Symbol import Symbol
312710SN/Afrom slicc.symbols.Var import Var
325457Ssaidi@eecs.umich.eduimport slicc.generate.html as html
335457Ssaidi@eecs.umich.eduimport re
345457Ssaidi@eecs.umich.edu
355457Ssaidi@eecs.umich.edupython_class_map = {
362710SN/A                    "int": "Int",
372934SN/A                    "uint32_t" : "UInt32",
382934SN/A                    "std::string": "String",
392549SN/A                    "bool": "Bool",
402995SN/A                    "CacheMemory": "RubyCache",
413395Shsul@eecs.umich.edu                    "WireBuffer": "RubyWireBuffer",
423448Shsul@eecs.umich.edu                    "Sequencer": "RubySequencer",
432549SN/A                    "DirectoryMemory": "RubyDirectoryMemory",
443444Sktlim@umich.edu                    "MemoryControl": "MemoryControl",
453444Sktlim@umich.edu                    "DMASequencer": "DMASequencer",
463444Sktlim@umich.edu                    "Prefetcher":"Prefetcher",
473444Sktlim@umich.edu                    "Cycles":"Cycles",
482889SN/A                   }
492710SN/A
503873Sbinkertn@umich.educlass StateMachine(Symbol):
513873Sbinkertn@umich.edu    def __init__(self, symtab, ident, location, pairs, config_parameters):
523873Sbinkertn@umich.edu        super(StateMachine, self).__init__(symtab, ident, location, pairs)
533873Sbinkertn@umich.edu        self.table = None
543322Shsul@eecs.umich.edu        self.config_parameters = config_parameters
552995SN/A        self.prefetchers = []
562995SN/A
572995SN/A        for param in config_parameters:
582995SN/A            if param.pointer:
592995SN/A                var = Var(symtab, param.name, location, param.type_ast.type,
603143Shsul@eecs.umich.edu                          "(*m_%s_ptr)" % param.name, {}, self)
613322Shsul@eecs.umich.edu            else:
623322Shsul@eecs.umich.edu                var = Var(symtab, param.name, location, param.type_ast.type,
633025Ssaidi@eecs.umich.edu                          "m_%s" % param.name, {}, self)
643143Shsul@eecs.umich.edu            self.symtab.registerSym(param.name, var)
653143Shsul@eecs.umich.edu            if str(param.type_ast.type) == "Prefetcher":
663322Shsul@eecs.umich.edu                self.prefetchers.append(var)
673444Sktlim@umich.edu
683322Shsul@eecs.umich.edu        self.states = orderdict()
692710SN/A        self.events = orderdict()
702710SN/A        self.actions = orderdict()
712710SN/A        self.request_types = orderdict()
722710SN/A        self.transitions = []
732710SN/A        self.in_ports = []
742710SN/A        self.functions = []
753322Shsul@eecs.umich.edu        self.objects = []
763304Sstever@eecs.umich.edu        self.TBEType   = None
773322Shsul@eecs.umich.edu        self.EntryType = None
783322Shsul@eecs.umich.edu
793304Sstever@eecs.umich.edu    def __repr__(self):
803481Shsul@eecs.umich.edu        return "[StateMachine: %s]" % self.ident
813481Shsul@eecs.umich.edu
822566SN/A    def addState(self, state):
833322Shsul@eecs.umich.edu        assert self.table is None
843322Shsul@eecs.umich.edu        self.states[state.ident] = state
852995SN/A
862995SN/A    def addEvent(self, event):
873304Sstever@eecs.umich.edu        assert self.table is None
883304Sstever@eecs.umich.edu        self.events[event.ident] = event
893304Sstever@eecs.umich.edu
902995SN/A    def addAction(self, action):
912995SN/A        assert self.table is None
922995SN/A
932917SN/A        # Check for duplicate action
942995SN/A        for other in self.actions.itervalues():
953304Sstever@eecs.umich.edu            if action.ident == other.ident:
962995SN/A                action.warning("Duplicate action definition: %s" % action.ident)
973304Sstever@eecs.umich.edu                action.error("Duplicate action definition: %s" % action.ident)
983304Sstever@eecs.umich.edu            if action.short == other.short:
993819Shsul@eecs.umich.edu                other.warning("Duplicate action shorthand: %s" % other.ident)
1003819Shsul@eecs.umich.edu                other.warning("    shorthand = %s" % other.short)
1015222Sksewell@umich.edu                action.warning("Duplicate action shorthand: %s" % action.ident)
1025222Sksewell@umich.edu                action.error("    shorthand = %s" % action.short)
1033819Shsul@eecs.umich.edu
1043819Shsul@eecs.umich.edu        self.actions[action.ident] = action
1055133Sgblack@eecs.umich.edu
1065299Sgblack@eecs.umich.edu    def addRequestType(self, request_type):
1073819Shsul@eecs.umich.edu        assert self.table is None
1083819Shsul@eecs.umich.edu        self.request_types[request_type.ident] = request_type
1093819Shsul@eecs.umich.edu
1103873Sbinkertn@umich.edu    def addTransition(self, trans):
1113873Sbinkertn@umich.edu        assert self.table is None
1123873Sbinkertn@umich.edu        self.transitions.append(trans)
1133873Sbinkertn@umich.edu
1143873Sbinkertn@umich.edu    def addInPort(self, var):
1153873Sbinkertn@umich.edu        self.in_ports.append(var)
1163312Sstever@eecs.umich.edu
1173668Srdreslin@umich.edu    def addFunc(self, func):
1183668Srdreslin@umich.edu        # register func in the symbol table
1193668Srdreslin@umich.edu        self.symtab.registerSym(str(func), func)
1203668Srdreslin@umich.edu        self.functions.append(func)
1213668Srdreslin@umich.edu
1223668Srdreslin@umich.edu    def addObject(self, obj):
1233668Srdreslin@umich.edu        self.objects.append(obj)
1243322Shsul@eecs.umich.edu
1255142Ssaidi@eecs.umich.edu    def addType(self, type):
1265142Ssaidi@eecs.umich.edu        type_ident = '%s' % type.c_ident
1275142Ssaidi@eecs.umich.edu
1285142Ssaidi@eecs.umich.edu        if type_ident == "%s_TBE" %self.ident:
1295142Ssaidi@eecs.umich.edu            if self.TBEType != None:
1305142Ssaidi@eecs.umich.edu                self.error("Multiple Transaction Buffer types in a " \
1315142Ssaidi@eecs.umich.edu                           "single machine.");
1325142Ssaidi@eecs.umich.edu            self.TBEType = type
1335142Ssaidi@eecs.umich.edu
1343312Sstever@eecs.umich.edu        elif "interface" in type and "AbstractCacheEntry" == type["interface"]:
1353514Sktlim@umich.edu            if self.EntryType != None:
1363395Shsul@eecs.umich.edu                self.error("Multiple AbstractCacheEntry types in a " \
1373448Shsul@eecs.umich.edu                           "single machine.");
1383668Srdreslin@umich.edu            self.EntryType = type
1393668Srdreslin@umich.edu
1403668Srdreslin@umich.edu    # Needs to be called before accessing the table
1413668Srdreslin@umich.edu    def buildTable(self):
1423005Sstever@eecs.umich.edu        assert self.table is None
1434968Sacolyte@umich.edu
1444968Sacolyte@umich.edu        table = {}
1454968Sacolyte@umich.edu
1465222Sksewell@umich.edu        for trans in self.transitions:
1475254Sksewell@umich.edu            # Track which actions we touch so we know if we use them
1485222Sksewell@umich.edu            # all -- really this should be done for all symbols as
1493005Sstever@eecs.umich.edu            # part of the symbol table, then only trigger it for
1503819Shsul@eecs.umich.edu            # Actions, States, Events, etc.
1513819Shsul@eecs.umich.edu
1525222Sksewell@umich.edu            for action in trans.actions:
1535222Sksewell@umich.edu                action.used = True
1543819Shsul@eecs.umich.edu
1553819Shsul@eecs.umich.edu            index = (trans.state, trans.event)
1565133Sgblack@eecs.umich.edu            if index in table:
1575133Sgblack@eecs.umich.edu                table[index].warning("Duplicate transition: %s" % table[index])
1583322Shsul@eecs.umich.edu                trans.error("Duplicate transition: %s" % trans)
1593322Shsul@eecs.umich.edu            table[index] = trans
1604968Sacolyte@umich.edu
1614968Sacolyte@umich.edu        # Look at all actions to make sure we used them all
1624837Ssaidi@eecs.umich.edu        for action in self.actions.itervalues():
1634837Ssaidi@eecs.umich.edu            if not action.used:
1644837Ssaidi@eecs.umich.edu                error_msg = "Unused action: %s" % action.ident
1653322Shsul@eecs.umich.edu                if "desc" in action:
1663005Sstever@eecs.umich.edu                    error_msg += ", "  + action.desc
1674167Sbinkertn@umich.edu                action.warning(error_msg)
1683005Sstever@eecs.umich.edu        self.table = table
1693005Sstever@eecs.umich.edu
1703005Sstever@eecs.umich.edu    def writeCodeFiles(self, path, includes):
1712566SN/A        self.printControllerPython(path)
1723481Shsul@eecs.umich.edu        self.printControllerHH(path)
173        self.printControllerCC(path, includes)
174        self.printCSwitch(path)
175        self.printCWakeup(path, includes)
176
177    def printControllerPython(self, path):
178        code = self.symtab.codeFormatter()
179        ident = self.ident
180        py_ident = "%s_Controller" % ident
181        c_ident = "%s_Controller" % self.ident
182        code('''
183from m5.params import *
184from m5.SimObject import SimObject
185from Controller import RubyController
186
187class $py_ident(RubyController):
188    type = '$py_ident'
189    cxx_header = 'mem/protocol/${c_ident}.hh'
190''')
191        code.indent()
192        for param in self.config_parameters:
193            dflt_str = ''
194            if param.default is not None:
195                dflt_str = str(param.default) + ', '
196            if python_class_map.has_key(param.type_ast.type.c_ident):
197                python_type = python_class_map[param.type_ast.type.c_ident]
198                code('${{param.name}} = Param.${{python_type}}(${dflt_str}"")')
199            else:
200                self.error("Unknown c++ to python class conversion for c++ " \
201                           "type: '%s'. Please update the python_class_map " \
202                           "in StateMachine.py", param.type_ast.type.c_ident)
203        code.dedent()
204        code.write(path, '%s.py' % py_ident)
205
206
207    def printControllerHH(self, path):
208        '''Output the method declarations for the class declaration'''
209        code = self.symtab.codeFormatter()
210        ident = self.ident
211        c_ident = "%s_Controller" % self.ident
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/TransitionResult.hh"
228#include "mem/protocol/Types.hh"
229#include "mem/ruby/common/Consumer.hh"
230#include "mem/ruby/common/Global.hh"
231#include "mem/ruby/slicc_interface/AbstractController.hh"
232#include "params/$c_ident.hh"
233''')
234
235        seen_types = set()
236        has_peer = False
237        for var in self.objects:
238            if var.type.ident not in seen_types and not var.type.isPrimitive:
239                code('#include "mem/protocol/${{var.type.c_ident}}.hh"')
240            if "network" in var and "physical_network" in var:
241                has_peer = True
242            seen_types.add(var.type.ident)
243
244        # for adding information to the protocol debug trace
245        code('''
246extern std::stringstream ${ident}_transitionComment;
247
248class $c_ident : public AbstractController
249{
250  public:
251    typedef ${c_ident}Params Params;
252    $c_ident(const Params *p);
253    static int getNumControllers();
254    void init();
255    MessageBuffer* getMandatoryQueue() const;
256    const std::string toString() const;
257
258    void print(std::ostream& out) const;
259    void wakeup();
260    void clearStats();
261    void regStats();
262    void collateStats();
263
264    void recordCacheTrace(int cntrl, CacheRecorder* tr);
265    Sequencer* getSequencer() const;
266
267    bool functionalReadBuffers(PacketPtr&);
268    uint32_t functionalWriteBuffers(PacketPtr&);
269
270    void countTransition(${ident}_State state, ${ident}_Event event);
271    void possibleTransition(${ident}_State state, ${ident}_Event event);
272    uint64 getEventCount(${ident}_Event event);
273    bool isPossible(${ident}_State state, ${ident}_Event event);
274    uint64 getTransitionCount(${ident}_State state, ${ident}_Event event);
275
276private:
277''')
278
279        code.indent()
280        # added by SS
281        for param in self.config_parameters:
282            if param.pointer:
283                code('${{param.type_ast.type}}* m_${{param.ident}}_ptr;')
284            else:
285                code('${{param.type_ast.type}} m_${{param.ident}};')
286
287        code('''
288TransitionResult doTransition(${ident}_Event event,
289''')
290
291        if self.EntryType != None:
292            code('''
293                              ${{self.EntryType.c_ident}}* m_cache_entry_ptr,
294''')
295        if self.TBEType != None:
296            code('''
297                              ${{self.TBEType.c_ident}}* m_tbe_ptr,
298''')
299
300        code('''
301                              const Address& addr);
302
303TransitionResult doTransitionWorker(${ident}_Event event,
304                                    ${ident}_State state,
305                                    ${ident}_State& next_state,
306''')
307
308        if self.TBEType != None:
309            code('''
310                                    ${{self.TBEType.c_ident}}*& m_tbe_ptr,
311''')
312        if self.EntryType != None:
313            code('''
314                                    ${{self.EntryType.c_ident}}*& m_cache_entry_ptr,
315''')
316
317        code('''
318                                    const Address& addr);
319
320int m_counters[${ident}_State_NUM][${ident}_Event_NUM];
321int m_event_counters[${ident}_Event_NUM];
322bool m_possible[${ident}_State_NUM][${ident}_Event_NUM];
323
324static std::vector<Stats::Vector *> eventVec;
325static std::vector<std::vector<Stats::Vector *> > transVec;
326static int m_num_controllers;
327
328// Internal functions
329''')
330
331        for func in self.functions:
332            proto = func.prototype
333            if proto:
334                code('$proto')
335
336        if has_peer:
337            code('void getQueuesFromPeer(AbstractController *);')
338        if self.EntryType != None:
339            code('''
340
341// Set and Reset for cache_entry variable
342void set_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, AbstractCacheEntry* m_new_cache_entry);
343void unset_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr);
344''')
345
346        if self.TBEType != None:
347            code('''
348
349// Set and Reset for tbe variable
350void set_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${ident}_TBE* m_new_tbe);
351void unset_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr);
352''')
353
354        code('''
355
356// Actions
357''')
358        if self.TBEType != None and self.EntryType != None:
359            for action in self.actions.itervalues():
360                code('/** \\brief ${{action.desc}} */')
361                code('void ${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr);')
362        elif self.TBEType != None:
363            for action in self.actions.itervalues():
364                code('/** \\brief ${{action.desc}} */')
365                code('void ${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, const Address& addr);')
366        elif self.EntryType != None:
367            for action in self.actions.itervalues():
368                code('/** \\brief ${{action.desc}} */')
369                code('void ${{action.ident}}(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr);')
370        else:
371            for action in self.actions.itervalues():
372                code('/** \\brief ${{action.desc}} */')
373                code('void ${{action.ident}}(const Address& addr);')
374
375        # the controller internal variables
376        code('''
377
378// Objects
379''')
380        for var in self.objects:
381            th = var.get("template", "")
382            code('${{var.type.c_ident}}$th* m_${{var.c_ident}}_ptr;')
383
384        code.dedent()
385        code('};')
386        code('#endif // __${ident}_CONTROLLER_H__')
387        code.write(path, '%s.hh' % c_ident)
388
389    def printControllerCC(self, path, includes):
390        '''Output the actions for performing the actions'''
391
392        code = self.symtab.codeFormatter()
393        ident = self.ident
394        c_ident = "%s_Controller" % self.ident
395        has_peer = False
396
397        code('''
398/** \\file $c_ident.cc
399 *
400 * Auto generated C++ code started by $__file__:$__line__
401 * Created by slicc definition of Module "${{self.short}}"
402 */
403
404#include <sys/types.h>
405#include <unistd.h>
406
407#include <cassert>
408#include <sstream>
409#include <string>
410
411#include "base/compiler.hh"
412#include "base/cprintf.hh"
413#include "debug/RubyGenerated.hh"
414#include "debug/RubySlicc.hh"
415#include "mem/protocol/${ident}_Controller.hh"
416#include "mem/protocol/${ident}_Event.hh"
417#include "mem/protocol/${ident}_State.hh"
418#include "mem/protocol/Types.hh"
419#include "mem/ruby/common/Global.hh"
420#include "mem/ruby/system/System.hh"
421''')
422        for include_path in includes:
423            code('#include "${{include_path}}"')
424
425        code('''
426
427using namespace std;
428''')
429
430        # include object classes
431        seen_types = set()
432        for var in self.objects:
433            if var.type.ident not in seen_types and not var.type.isPrimitive:
434                code('#include "mem/protocol/${{var.type.c_ident}}.hh"')
435            seen_types.add(var.type.ident)
436
437        code('''
438$c_ident *
439${c_ident}Params::create()
440{
441    return new $c_ident(this);
442}
443
444int $c_ident::m_num_controllers = 0;
445std::vector<Stats::Vector *>  $c_ident::eventVec;
446std::vector<std::vector<Stats::Vector *> >  $c_ident::transVec;
447
448// for adding information to the protocol debug trace
449stringstream ${ident}_transitionComment;
450
451#ifndef NDEBUG
452#define APPEND_TRANSITION_COMMENT(str) (${ident}_transitionComment << str)
453#else
454#define APPEND_TRANSITION_COMMENT(str) do {} while (0)
455#endif
456
457/** \\brief constructor */
458$c_ident::$c_ident(const Params *p)
459    : AbstractController(p)
460{
461    m_name = "${ident}";
462''')
463        num_in_ports = len(self.in_ports)
464        code('    m_in_ports = $num_in_ports;')
465        code.indent()
466
467        #
468        # After initializing the universal machine parameters, initialize the
469        # this machines config parameters.  Also detemine if these configuration
470        # params include a sequencer.  This information will be used later for
471        # contecting the sequencer back to the L1 cache controller.
472        #
473        contains_dma_sequencer = False
474        sequencers = []
475        for param in self.config_parameters:
476            if param.name == "dma_sequencer":
477                contains_dma_sequencer = True
478            elif re.compile("sequencer").search(param.name):
479                sequencers.append(param.name)
480            if param.pointer:
481                code('m_${{param.name}}_ptr = p->${{param.name}};')
482            else:
483                code('m_${{param.name}} = p->${{param.name}};')
484
485        #
486        # For the l1 cache controller, add the special atomic support which
487        # includes passing the sequencer a pointer to the controller.
488        #
489        for seq in sequencers:
490            code('''
491m_${{seq}}_ptr->setController(this);
492    ''')
493
494        #
495        # For the DMA controller, pass the sequencer a pointer to the
496        # controller.
497        #
498        if self.ident == "DMA":
499            if not contains_dma_sequencer:
500                self.error("The DMA controller must include the sequencer " \
501                           "configuration parameter")
502
503            code('''
504m_dma_sequencer_ptr->setController(this);
505''')
506
507        code('m_num_controllers++;')
508        for var in self.objects:
509            if var.ident.find("mandatoryQueue") >= 0:
510                code('''
511m_${{var.c_ident}}_ptr = new ${{var.type.c_ident}}();
512m_${{var.c_ident}}_ptr->setReceiver(this);
513''')
514            else:
515                if "network" in var and "physical_network" in var and \
516                   var["network"] == "To":
517                    has_peer = True
518                    code('''
519m_${{var.c_ident}}_ptr = new ${{var.type.c_ident}}();
520peerQueueMap[${{var["physical_network"]}}] = m_${{var.c_ident}}_ptr;
521m_${{var.c_ident}}_ptr->setSender(this);
522''')
523
524        code('''
525if (p->peer != NULL)
526    connectWithPeer(p->peer);
527
528for (int state = 0; state < ${ident}_State_NUM; state++) {
529    for (int event = 0; event < ${ident}_Event_NUM; event++) {
530        m_possible[state][event] = false;
531        m_counters[state][event] = 0;
532    }
533}
534for (int event = 0; event < ${ident}_Event_NUM; event++) {
535    m_event_counters[event] = 0;
536}
537''')
538        code.dedent()
539        code('''
540}
541
542void
543$c_ident::init()
544{
545    MachineType machine_type = string_to_MachineType("${{var.machine.ident}}");
546    int base = MachineType_base_number(machine_type);
547
548    m_machineID.type = MachineType_${ident};
549    m_machineID.num = m_version;
550
551    // initialize objects
552
553''')
554
555        code.indent()
556        for var in self.objects:
557            vtype = var.type
558            vid = "m_%s_ptr" % var.c_ident
559            if "network" not in var:
560                # Not a network port object
561                if "primitive" in vtype:
562                    code('$vid = new ${{vtype.c_ident}};')
563                    if "default" in var:
564                        code('(*$vid) = ${{var["default"]}};')
565                else:
566                    # Normal Object
567                    if var.ident.find("mandatoryQueue") < 0:
568                        th = var.get("template", "")
569                        expr = "%s  = new %s%s" % (vid, vtype.c_ident, th)
570                        args = ""
571                        if "non_obj" not in vtype and not vtype.isEnumeration:
572                            args = var.get("constructor", "")
573                        code('$expr($args);')
574
575                    code('assert($vid != NULL);')
576
577                    if "default" in var:
578                        code('*$vid = ${{var["default"]}}; // Object default')
579                    elif "default" in vtype:
580                        comment = "Type %s default" % vtype.ident
581                        code('*$vid = ${{vtype["default"]}}; // $comment')
582
583                    # Set ordering
584                    if "ordered" in var:
585                        # A buffer
586                        code('$vid->setOrdering(${{var["ordered"]}});')
587
588                    # Set randomization
589                    if "random" in var:
590                        # A buffer
591                        code('$vid->setRandomization(${{var["random"]}});')
592
593                    # Set Priority
594                    if vtype.isBuffer and "rank" in var:
595                        code('$vid->setPriority(${{var["rank"]}});')
596
597                    # Set sender and receiver for trigger queue
598                    if var.ident.find("triggerQueue") >= 0:
599                        code('$vid->setSender(this);')
600                        code('$vid->setReceiver(this);')
601                    elif vtype.c_ident == "TimerTable":
602                        code('$vid->setClockObj(this);')
603                    elif var.ident.find("optionalQueue") >= 0:
604                        code('$vid->setSender(this);')
605                        code('$vid->setReceiver(this);')
606
607            else:
608                # Network port object
609                network = var["network"]
610                ordered =  var["ordered"]
611
612                if "virtual_network" in var:
613                    vnet = var["virtual_network"]
614                    vnet_type = var["vnet_type"]
615
616                    assert var.machine is not None
617                    code('''
618$vid = m_net_ptr->get${network}NetQueue(m_version + base, $ordered, $vnet, "$vnet_type");
619assert($vid != NULL);
620''')
621
622                    # Set the end
623                    if network == "To":
624                        code('$vid->setSender(this);')
625                    else:
626                        code('$vid->setReceiver(this);')
627
628                # Set ordering
629                if "ordered" in var:
630                    # A buffer
631                    code('$vid->setOrdering(${{var["ordered"]}});')
632
633                # Set randomization
634                if "random" in var:
635                    # A buffer
636                    code('$vid->setRandomization(${{var["random"]}});')
637
638                # Set Priority
639                if "rank" in var:
640                    code('$vid->setPriority(${{var["rank"]}})')
641
642                # Set buffer size
643                if vtype.isBuffer:
644                    code('''
645if (m_buffer_size > 0) {
646    $vid->resize(m_buffer_size);
647}
648''')
649
650                # set description (may be overriden later by port def)
651                code('''
652$vid->setDescription("[Version " + to_string(m_version) + ", ${ident}, name=${{var.c_ident}}]");
653
654''')
655
656            if vtype.isBuffer:
657                if "recycle_latency" in var:
658                    code('$vid->setRecycleLatency( ' \
659                         'Cycles(${{var["recycle_latency"]}}));')
660                else:
661                    code('$vid->setRecycleLatency(m_recycle_latency);')
662
663        # Set the prefetchers
664        code()
665        for prefetcher in self.prefetchers:
666            code('${{prefetcher.code}}.setController(this);')
667
668        code()
669        for port in self.in_ports:
670            # Set the queue consumers
671            code('${{port.code}}.setConsumer(this);')
672            # Set the queue descriptions
673            code('${{port.code}}.setDescription("[Version " + to_string(m_version) + ", $ident, $port]");')
674
675        # Initialize the transition profiling
676        code()
677        for trans in self.transitions:
678            # Figure out if we stall
679            stall = False
680            for action in trans.actions:
681                if action.ident == "z_stall":
682                    stall = True
683
684            # Only possible if it is not a 'z' case
685            if not stall:
686                state = "%s_State_%s" % (self.ident, trans.state.ident)
687                event = "%s_Event_%s" % (self.ident, trans.event.ident)
688                code('possibleTransition($state, $event);')
689
690        code.dedent()
691        code('''
692    AbstractController::init();
693    clearStats();
694}
695''')
696
697        has_mandatory_q = False
698        for port in self.in_ports:
699            if port.code.find("mandatoryQueue_ptr") >= 0:
700                has_mandatory_q = True
701
702        if has_mandatory_q:
703            mq_ident = "m_%s_mandatoryQueue_ptr" % self.ident
704        else:
705            mq_ident = "NULL"
706
707        seq_ident = "NULL"
708        for param in self.config_parameters:
709            if param.name == "sequencer":
710                assert(param.pointer)
711                seq_ident = "m_%s_ptr" % param.name
712
713        code('''
714
715void
716$c_ident::regStats()
717{
718    if (m_version == 0) {
719        for (${ident}_Event event = ${ident}_Event_FIRST;
720             event < ${ident}_Event_NUM; ++event) {
721            Stats::Vector *t = new Stats::Vector();
722            t->init(m_num_controllers);
723            t->name(name() + "." + ${ident}_Event_to_string(event));
724            t->flags(Stats::pdf | Stats::total | Stats::oneline |
725                     Stats::nozero);
726
727            eventVec.push_back(t);
728        }
729
730        for (${ident}_State state = ${ident}_State_FIRST;
731             state < ${ident}_State_NUM; ++state) {
732
733            transVec.push_back(std::vector<Stats::Vector *>());
734
735            for (${ident}_Event event = ${ident}_Event_FIRST;
736                 event < ${ident}_Event_NUM; ++event) {
737
738                Stats::Vector *t = new Stats::Vector();
739                t->init(m_num_controllers);
740                t->name(name() + "." + ${ident}_State_to_string(state) +
741                        "." + ${ident}_Event_to_string(event));
742
743                t->flags(Stats::pdf | Stats::total | Stats::oneline |
744                         Stats::nozero);
745                transVec[state].push_back(t);
746            }
747        }
748    }
749}
750
751void
752$c_ident::collateStats()
753{
754    for (${ident}_Event event = ${ident}_Event_FIRST;
755         event < ${ident}_Event_NUM; ++event) {
756        for (unsigned int i = 0; i < m_num_controllers; ++i) {
757            std::map<uint32_t, AbstractController *>::iterator it =
758                                g_abs_controls[MachineType_${ident}].find(i);
759            assert(it != g_abs_controls[MachineType_${ident}].end());
760            (*eventVec[event])[i] =
761                (($c_ident *)(*it).second)->getEventCount(event);
762        }
763    }
764
765    for (${ident}_State state = ${ident}_State_FIRST;
766         state < ${ident}_State_NUM; ++state) {
767
768        for (${ident}_Event event = ${ident}_Event_FIRST;
769             event < ${ident}_Event_NUM; ++event) {
770
771            for (unsigned int i = 0; i < m_num_controllers; ++i) {
772                std::map<uint32_t, AbstractController *>::iterator it =
773                                g_abs_controls[MachineType_${ident}].find(i);
774                assert(it != g_abs_controls[MachineType_${ident}].end());
775                (*transVec[state][event])[i] =
776                    (($c_ident *)(*it).second)->getTransitionCount(state, event);
777            }
778        }
779    }
780}
781
782void
783$c_ident::countTransition(${ident}_State state, ${ident}_Event event)
784{
785    assert(m_possible[state][event]);
786    m_counters[state][event]++;
787    m_event_counters[event]++;
788}
789void
790$c_ident::possibleTransition(${ident}_State state,
791                             ${ident}_Event event)
792{
793    m_possible[state][event] = true;
794}
795
796uint64
797$c_ident::getEventCount(${ident}_Event event)
798{
799    return m_event_counters[event];
800}
801
802bool
803$c_ident::isPossible(${ident}_State state, ${ident}_Event event)
804{
805    return m_possible[state][event];
806}
807
808uint64
809$c_ident::getTransitionCount(${ident}_State state,
810                             ${ident}_Event event)
811{
812    return m_counters[state][event];
813}
814
815int
816$c_ident::getNumControllers()
817{
818    return m_num_controllers;
819}
820
821MessageBuffer*
822$c_ident::getMandatoryQueue() const
823{
824    return $mq_ident;
825}
826
827Sequencer*
828$c_ident::getSequencer() const
829{
830    return $seq_ident;
831}
832
833const string
834$c_ident::toString() const
835{
836    return "$c_ident";
837}
838
839void
840$c_ident::print(ostream& out) const
841{
842    out << "[$c_ident " << m_version << "]";
843}
844
845void $c_ident::clearStats()
846{
847    for (int state = 0; state < ${ident}_State_NUM; state++) {
848        for (int event = 0; event < ${ident}_Event_NUM; event++) {
849            m_counters[state][event] = 0;
850        }
851    }
852
853    for (int event = 0; event < ${ident}_Event_NUM; event++) {
854        m_event_counters[event] = 0;
855    }
856
857    AbstractController::clearStats();
858}
859''')
860
861        if self.EntryType != None:
862            code('''
863
864// Set and Reset for cache_entry variable
865void
866$c_ident::set_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, AbstractCacheEntry* m_new_cache_entry)
867{
868  m_cache_entry_ptr = (${{self.EntryType.c_ident}}*)m_new_cache_entry;
869}
870
871void
872$c_ident::unset_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr)
873{
874  m_cache_entry_ptr = 0;
875}
876''')
877
878        if self.TBEType != None:
879            code('''
880
881// Set and Reset for tbe variable
882void
883$c_ident::set_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.TBEType.c_ident}}* m_new_tbe)
884{
885  m_tbe_ptr = m_new_tbe;
886}
887
888void
889$c_ident::unset_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr)
890{
891  m_tbe_ptr = NULL;
892}
893''')
894
895        code('''
896
897void
898$c_ident::recordCacheTrace(int cntrl, CacheRecorder* tr)
899{
900''')
901        #
902        # Record cache contents for all associated caches.
903        #
904        code.indent()
905        for param in self.config_parameters:
906            if param.type_ast.type.ident == "CacheMemory":
907                assert(param.pointer)
908                code('m_${{param.ident}}_ptr->recordCacheContents(cntrl, tr);')
909
910        code.dedent()
911        code('''
912}
913
914// Actions
915''')
916        if self.TBEType != None and self.EntryType != None:
917            for action in self.actions.itervalues():
918                if "c_code" not in action:
919                 continue
920
921                code('''
922/** \\brief ${{action.desc}} */
923void
924$c_ident::${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr)
925{
926    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
927    ${{action["c_code"]}}
928}
929
930''')
931        elif self.TBEType != None:
932            for action in self.actions.itervalues():
933                if "c_code" not in action:
934                 continue
935
936                code('''
937/** \\brief ${{action.desc}} */
938void
939$c_ident::${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, const Address& addr)
940{
941    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
942    ${{action["c_code"]}}
943}
944
945''')
946        elif self.EntryType != 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.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr)
955{
956    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
957    ${{action["c_code"]}}
958}
959
960''')
961        else:
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}}(const Address& addr)
970{
971    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
972    ${{action["c_code"]}}
973}
974
975''')
976        for func in self.functions:
977            code(func.generateCode())
978
979        # Function for functional reads from messages buffered in the controller
980        code('''
981bool
982$c_ident::functionalReadBuffers(PacketPtr& pkt)
983{
984''')
985        for var in self.objects:
986            vtype = var.type
987            if vtype.isBuffer:
988                vid = "m_%s_ptr" % var.c_ident
989                code('if ($vid->functionalRead(pkt)) { return true; }')
990        code('''
991                return false;
992}
993''')
994
995        # Function for functional writes to messages buffered in the controller
996        code('''
997uint32_t
998$c_ident::functionalWriteBuffers(PacketPtr& pkt)
999{
1000    uint32_t num_functional_writes = 0;
1001''')
1002        for var in self.objects:
1003            vtype = var.type
1004            if vtype.isBuffer:
1005                vid = "m_%s_ptr" % var.c_ident
1006                code('num_functional_writes += $vid->functionalWrite(pkt);')
1007        code('''
1008    return num_functional_writes;
1009}
1010''')
1011
1012        # Check if this controller has a peer, if yes then write the
1013        # function for connecting to the peer.
1014        if has_peer:
1015            code('''
1016
1017void
1018$c_ident::getQueuesFromPeer(AbstractController *peer)
1019{
1020''')
1021            for var in self.objects:
1022                if "network" in var and "physical_network" in var and \
1023                   var["network"] == "From":
1024                    code('''
1025m_${{var.c_ident}}_ptr = peer->getPeerQueue(${{var["physical_network"]}});
1026assert(m_${{var.c_ident}}_ptr != NULL);
1027m_${{var.c_ident}}_ptr->setReceiver(this);
1028
1029''')
1030            code('}')
1031
1032        code.write(path, "%s.cc" % c_ident)
1033
1034    def printCWakeup(self, path, includes):
1035        '''Output the wakeup loop for the events'''
1036
1037        code = self.symtab.codeFormatter()
1038        ident = self.ident
1039
1040        outputRequest_types = True
1041        if len(self.request_types) == 0:
1042            outputRequest_types = False
1043
1044        code('''
1045// Auto generated C++ code started by $__file__:$__line__
1046// ${ident}: ${{self.short}}
1047
1048#include <sys/types.h>
1049#include <unistd.h>
1050
1051#include <cassert>
1052
1053#include "base/misc.hh"
1054#include "debug/RubySlicc.hh"
1055#include "mem/protocol/${ident}_Controller.hh"
1056#include "mem/protocol/${ident}_Event.hh"
1057#include "mem/protocol/${ident}_State.hh"
1058''')
1059
1060        if outputRequest_types:
1061            code('''#include "mem/protocol/${ident}_RequestType.hh"''')
1062
1063        code('''
1064#include "mem/protocol/Types.hh"
1065#include "mem/ruby/common/Global.hh"
1066#include "mem/ruby/system/System.hh"
1067''')
1068
1069
1070        for include_path in includes:
1071            code('#include "${{include_path}}"')
1072
1073        code('''
1074
1075using namespace std;
1076
1077void
1078${ident}_Controller::wakeup()
1079{
1080    int counter = 0;
1081    while (true) {
1082        // Some cases will put us into an infinite loop without this limit
1083        assert(counter <= m_transitions_per_cycle);
1084        if (counter == m_transitions_per_cycle) {
1085            // Count how often we are fully utilized
1086            m_fully_busy_cycles++;
1087
1088            // Wakeup in another cycle and try again
1089            scheduleEvent(Cycles(1));
1090            break;
1091        }
1092''')
1093
1094        code.indent()
1095        code.indent()
1096
1097        # InPorts
1098        #
1099        for port in self.in_ports:
1100            code.indent()
1101            code('// ${ident}InPort $port')
1102            if port.pairs.has_key("rank"):
1103                code('m_cur_in_port = ${{port.pairs["rank"]}};')
1104            else:
1105                code('m_cur_in_port = 0;')
1106            code('${{port["c_code_in_port"]}}')
1107            code.dedent()
1108
1109            code('')
1110
1111        code.dedent()
1112        code.dedent()
1113        code('''
1114        break;  // If we got this far, we have nothing left todo
1115    }
1116}
1117''')
1118
1119        code.write(path, "%s_Wakeup.cc" % self.ident)
1120
1121    def printCSwitch(self, path):
1122        '''Output switch statement for transition table'''
1123
1124        code = self.symtab.codeFormatter()
1125        ident = self.ident
1126
1127        code('''
1128// Auto generated C++ code started by $__file__:$__line__
1129// ${ident}: ${{self.short}}
1130
1131#include <cassert>
1132
1133#include "base/misc.hh"
1134#include "base/trace.hh"
1135#include "debug/ProtocolTrace.hh"
1136#include "debug/RubyGenerated.hh"
1137#include "mem/protocol/${ident}_Controller.hh"
1138#include "mem/protocol/${ident}_Event.hh"
1139#include "mem/protocol/${ident}_State.hh"
1140#include "mem/protocol/Types.hh"
1141#include "mem/ruby/common/Global.hh"
1142#include "mem/ruby/system/System.hh"
1143
1144#define HASH_FUN(state, event)  ((int(state)*${ident}_Event_NUM)+int(event))
1145
1146#define GET_TRANSITION_COMMENT() (${ident}_transitionComment.str())
1147#define CLEAR_TRANSITION_COMMENT() (${ident}_transitionComment.str(""))
1148
1149TransitionResult
1150${ident}_Controller::doTransition(${ident}_Event event,
1151''')
1152        if self.EntryType != None:
1153            code('''
1154                                  ${{self.EntryType.c_ident}}* m_cache_entry_ptr,
1155''')
1156        if self.TBEType != None:
1157            code('''
1158                                  ${{self.TBEType.c_ident}}* m_tbe_ptr,
1159''')
1160        code('''
1161                                  const Address &addr)
1162{
1163''')
1164        if self.TBEType != None and self.EntryType != None:
1165            code('${ident}_State state = getState(m_tbe_ptr, m_cache_entry_ptr, addr);')
1166        elif self.TBEType != None:
1167            code('${ident}_State state = getState(m_tbe_ptr, addr);')
1168        elif self.EntryType != None:
1169            code('${ident}_State state = getState(m_cache_entry_ptr, addr);')
1170        else:
1171            code('${ident}_State state = getState(addr);')
1172
1173        code('''
1174    ${ident}_State next_state = state;
1175
1176    DPRINTF(RubyGenerated, "%s, Time: %lld, state: %s, event: %s, addr: %s\\n",
1177            *this, curCycle(), ${ident}_State_to_string(state),
1178            ${ident}_Event_to_string(event), addr);
1179
1180    TransitionResult result =
1181''')
1182        if self.TBEType != None and self.EntryType != None:
1183            code('doTransitionWorker(event, state, next_state, m_tbe_ptr, m_cache_entry_ptr, addr);')
1184        elif self.TBEType != None:
1185            code('doTransitionWorker(event, state, next_state, m_tbe_ptr, addr);')
1186        elif self.EntryType != None:
1187            code('doTransitionWorker(event, state, next_state, m_cache_entry_ptr, addr);')
1188        else:
1189            code('doTransitionWorker(event, state, next_state, addr);')
1190
1191        code('''
1192    if (result == TransitionResult_Valid) {
1193        DPRINTF(RubyGenerated, "next_state: %s\\n",
1194                ${ident}_State_to_string(next_state));
1195        countTransition(state, event);
1196        DPRINTFR(ProtocolTrace, "%15d %3s %10s%20s %6s>%-6s %s %s\\n",
1197                 curTick(), m_version, "${ident}",
1198                 ${ident}_Event_to_string(event),
1199                 ${ident}_State_to_string(state),
1200                 ${ident}_State_to_string(next_state),
1201                 addr, GET_TRANSITION_COMMENT());
1202
1203        CLEAR_TRANSITION_COMMENT();
1204''')
1205        if self.TBEType != None and self.EntryType != None:
1206            code('setState(m_tbe_ptr, m_cache_entry_ptr, addr, next_state);')
1207            code('setAccessPermission(m_cache_entry_ptr, addr, next_state);')
1208        elif self.TBEType != None:
1209            code('setState(m_tbe_ptr, addr, next_state);')
1210            code('setAccessPermission(addr, next_state);')
1211        elif self.EntryType != None:
1212            code('setState(m_cache_entry_ptr, addr, next_state);')
1213            code('setAccessPermission(m_cache_entry_ptr, addr, next_state);')
1214        else:
1215            code('setState(addr, next_state);')
1216            code('setAccessPermission(addr, next_state);')
1217
1218        code('''
1219    } else if (result == TransitionResult_ResourceStall) {
1220        DPRINTFR(ProtocolTrace, "%15s %3s %10s%20s %6s>%-6s %s %s\\n",
1221                 curTick(), m_version, "${ident}",
1222                 ${ident}_Event_to_string(event),
1223                 ${ident}_State_to_string(state),
1224                 ${ident}_State_to_string(next_state),
1225                 addr, "Resource Stall");
1226    } else if (result == TransitionResult_ProtocolStall) {
1227        DPRINTF(RubyGenerated, "stalling\\n");
1228        DPRINTFR(ProtocolTrace, "%15s %3s %10s%20s %6s>%-6s %s %s\\n",
1229                 curTick(), m_version, "${ident}",
1230                 ${ident}_Event_to_string(event),
1231                 ${ident}_State_to_string(state),
1232                 ${ident}_State_to_string(next_state),
1233                 addr, "Protocol Stall");
1234    }
1235
1236    return result;
1237}
1238
1239TransitionResult
1240${ident}_Controller::doTransitionWorker(${ident}_Event event,
1241                                        ${ident}_State state,
1242                                        ${ident}_State& next_state,
1243''')
1244
1245        if self.TBEType != None:
1246            code('''
1247                                        ${{self.TBEType.c_ident}}*& m_tbe_ptr,
1248''')
1249        if self.EntryType != None:
1250                  code('''
1251                                        ${{self.EntryType.c_ident}}*& m_cache_entry_ptr,
1252''')
1253        code('''
1254                                        const Address& addr)
1255{
1256    switch(HASH_FUN(state, event)) {
1257''')
1258
1259        # This map will allow suppress generating duplicate code
1260        cases = orderdict()
1261
1262        for trans in self.transitions:
1263            case_string = "%s_State_%s, %s_Event_%s" % \
1264                (self.ident, trans.state.ident, self.ident, trans.event.ident)
1265
1266            case = self.symtab.codeFormatter()
1267            # Only set next_state if it changes
1268            if trans.state != trans.nextState:
1269                ns_ident = trans.nextState.ident
1270                case('next_state = ${ident}_State_${ns_ident};')
1271
1272            actions = trans.actions
1273            request_types = trans.request_types
1274
1275            # Check for resources
1276            case_sorter = []
1277            res = trans.resources
1278            for key,val in res.iteritems():
1279                if key.type.ident != "DNUCAStopTable":
1280                    val = '''
1281if (!%s.areNSlotsAvailable(%s))
1282    return TransitionResult_ResourceStall;
1283''' % (key.code, val)
1284                case_sorter.append(val)
1285
1286            # Check all of the request_types for resource constraints
1287            for request_type in request_types:
1288                val = '''
1289if (!checkResourceAvailable(%s_RequestType_%s, addr)) {
1290    return TransitionResult_ResourceStall;
1291}
1292''' % (self.ident, request_type.ident)
1293                case_sorter.append(val)
1294
1295            # Emit the code sequences in a sorted order.  This makes the
1296            # output deterministic (without this the output order can vary
1297            # since Map's keys() on a vector of pointers is not deterministic
1298            for c in sorted(case_sorter):
1299                case("$c")
1300
1301            # Record access types for this transition
1302            for request_type in request_types:
1303                case('recordRequestType(${ident}_RequestType_${{request_type.ident}}, addr);')
1304
1305            # Figure out if we stall
1306            stall = False
1307            for action in actions:
1308                if action.ident == "z_stall":
1309                    stall = True
1310                    break
1311
1312            if stall:
1313                case('return TransitionResult_ProtocolStall;')
1314            else:
1315                if self.TBEType != None and self.EntryType != None:
1316                    for action in actions:
1317                        case('${{action.ident}}(m_tbe_ptr, m_cache_entry_ptr, addr);')
1318                elif self.TBEType != None:
1319                    for action in actions:
1320                        case('${{action.ident}}(m_tbe_ptr, addr);')
1321                elif self.EntryType != None:
1322                    for action in actions:
1323                        case('${{action.ident}}(m_cache_entry_ptr, addr);')
1324                else:
1325                    for action in actions:
1326                        case('${{action.ident}}(addr);')
1327                case('return TransitionResult_Valid;')
1328
1329            case = str(case)
1330
1331            # Look to see if this transition code is unique.
1332            if case not in cases:
1333                cases[case] = []
1334
1335            cases[case].append(case_string)
1336
1337        # Walk through all of the unique code blocks and spit out the
1338        # corresponding case statement elements
1339        for case,transitions in cases.iteritems():
1340            # Iterative over all the multiple transitions that share
1341            # the same code
1342            for trans in transitions:
1343                code('  case HASH_FUN($trans):')
1344            code('    $case')
1345
1346        code('''
1347      default:
1348        fatal("Invalid transition\\n"
1349              "%s time: %d addr: %s event: %s state: %s\\n",
1350              name(), curCycle(), addr, event, state);
1351    }
1352    return TransitionResult_Valid;
1353}
1354''')
1355        code.write(path, "%s_Transitions.cc" % self.ident)
1356
1357
1358    # **************************
1359    # ******* HTML Files *******
1360    # **************************
1361    def frameRef(self, click_href, click_target, over_href, over_num, text):
1362        code = self.symtab.codeFormatter(fix_newlines=False)
1363        code("""<A href=\"$click_href\" target=\"$click_target\" onmouseover=\"
1364    if (parent.frames[$over_num].location != parent.location + '$over_href') {
1365        parent.frames[$over_num].location='$over_href'
1366    }\">
1367    ${{html.formatShorthand(text)}}
1368    </A>""")
1369        return str(code)
1370
1371    def writeHTMLFiles(self, path):
1372        # Create table with no row hilighted
1373        self.printHTMLTransitions(path, None)
1374
1375        # Generate transition tables
1376        for state in self.states.itervalues():
1377            self.printHTMLTransitions(path, state)
1378
1379        # Generate action descriptions
1380        for action in self.actions.itervalues():
1381            name = "%s_action_%s.html" % (self.ident, action.ident)
1382            code = html.createSymbol(action, "Action")
1383            code.write(path, name)
1384
1385        # Generate state descriptions
1386        for state in self.states.itervalues():
1387            name = "%s_State_%s.html" % (self.ident, state.ident)
1388            code = html.createSymbol(state, "State")
1389            code.write(path, name)
1390
1391        # Generate event descriptions
1392        for event in self.events.itervalues():
1393            name = "%s_Event_%s.html" % (self.ident, event.ident)
1394            code = html.createSymbol(event, "Event")
1395            code.write(path, name)
1396
1397    def printHTMLTransitions(self, path, active_state):
1398        code = self.symtab.codeFormatter()
1399
1400        code('''
1401<HTML>
1402<BODY link="blue" vlink="blue">
1403
1404<H1 align="center">${{html.formatShorthand(self.short)}}:
1405''')
1406        code.indent()
1407        for i,machine in enumerate(self.symtab.getAllType(StateMachine)):
1408            mid = machine.ident
1409            if i != 0:
1410                extra = " - "
1411            else:
1412                extra = ""
1413            if machine == self:
1414                code('$extra$mid')
1415            else:
1416                code('$extra<A target="Table" href="${mid}_table.html">$mid</A>')
1417        code.dedent()
1418
1419        code("""
1420</H1>
1421
1422<TABLE border=1>
1423<TR>
1424  <TH> </TH>
1425""")
1426
1427        for event in self.events.itervalues():
1428            href = "%s_Event_%s.html" % (self.ident, event.ident)
1429            ref = self.frameRef(href, "Status", href, "1", event.short)
1430            code('<TH bgcolor=white>$ref</TH>')
1431
1432        code('</TR>')
1433        # -- Body of table
1434        for state in self.states.itervalues():
1435            # -- Each row
1436            if state == active_state:
1437                color = "yellow"
1438            else:
1439                color = "white"
1440
1441            click = "%s_table_%s.html" % (self.ident, state.ident)
1442            over = "%s_State_%s.html" % (self.ident, state.ident)
1443            text = html.formatShorthand(state.short)
1444            ref = self.frameRef(click, "Table", over, "1", state.short)
1445            code('''
1446<TR>
1447  <TH bgcolor=$color>$ref</TH>
1448''')
1449
1450            # -- One column for each event
1451            for event in self.events.itervalues():
1452                trans = self.table.get((state,event), None)
1453                if trans is None:
1454                    # This is the no transition case
1455                    if state == active_state:
1456                        color = "#C0C000"
1457                    else:
1458                        color = "lightgrey"
1459
1460                    code('<TD bgcolor=$color>&nbsp;</TD>')
1461                    continue
1462
1463                next = trans.nextState
1464                stall_action = False
1465
1466                # -- Get the actions
1467                for action in trans.actions:
1468                    if action.ident == "z_stall" or \
1469                       action.ident == "zz_recycleMandatoryQueue":
1470                        stall_action = True
1471
1472                # -- Print out "actions/next-state"
1473                if stall_action:
1474                    if state == active_state:
1475                        color = "#C0C000"
1476                    else:
1477                        color = "lightgrey"
1478
1479                elif active_state and next.ident == active_state.ident:
1480                    color = "aqua"
1481                elif state == active_state:
1482                    color = "yellow"
1483                else:
1484                    color = "white"
1485
1486                code('<TD bgcolor=$color>')
1487                for action in trans.actions:
1488                    href = "%s_action_%s.html" % (self.ident, action.ident)
1489                    ref = self.frameRef(href, "Status", href, "1",
1490                                        action.short)
1491                    code('  $ref')
1492                if next != state:
1493                    if trans.actions:
1494                        code('/')
1495                    click = "%s_table_%s.html" % (self.ident, next.ident)
1496                    over = "%s_State_%s.html" % (self.ident, next.ident)
1497                    ref = self.frameRef(click, "Table", over, "1", next.short)
1498                    code("$ref")
1499                code("</TD>")
1500
1501            # -- Each row
1502            if state == active_state:
1503                color = "yellow"
1504            else:
1505                color = "white"
1506
1507            click = "%s_table_%s.html" % (self.ident, state.ident)
1508            over = "%s_State_%s.html" % (self.ident, state.ident)
1509            ref = self.frameRef(click, "Table", over, "1", state.short)
1510            code('''
1511  <TH bgcolor=$color>$ref</TH>
1512</TR>
1513''')
1514        code('''
1515<!- Column footer->
1516<TR>
1517  <TH> </TH>
1518''')
1519
1520        for event in self.events.itervalues():
1521            href = "%s_Event_%s.html" % (self.ident, event.ident)
1522            ref = self.frameRef(href, "Status", href, "1", event.short)
1523            code('<TH bgcolor=white>$ref</TH>')
1524        code('''
1525</TR>
1526</TABLE>
1527</BODY></HTML>
1528''')
1529
1530
1531        if active_state:
1532            name = "%s_table_%s.html" % (self.ident, active_state.ident)
1533        else:
1534            name = "%s_table.html" % self.ident
1535        code.write(path, name)
1536
1537__all__ = [ "StateMachine" ]
1538