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