StateMachine.py revision 7056
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
322710SN/Aimport slicc.generate.html as html
332934SN/A
342934SN/Apython_class_map = {"int": "Int",
352549SN/A                    "std::string": "String",
362995SN/A                    "bool": "Bool",
373395Shsul@eecs.umich.edu                    "CacheMemory": "RubyCache",
383448Shsul@eecs.umich.edu                    "Sequencer": "RubySequencer",
392549SN/A                    "DirectoryMemory": "RubyDirectoryMemory",
403088Sstever@eecs.umich.edu                    "MemoryControl": "RubyMemoryControl",
413088Sstever@eecs.umich.edu                    "DMASequencer": "DMASequencer"
423088Sstever@eecs.umich.edu                    }
433444Sktlim@umich.edu
443444Sktlim@umich.educlass StateMachine(Symbol):
453444Sktlim@umich.edu    def __init__(self, symtab, ident, location, pairs, config_parameters):
463444Sktlim@umich.edu        super(StateMachine, self).__init__(symtab, ident, location, pairs)
472889SN/A        self.table = None
482710SN/A        self.config_parameters = config_parameters
493873Sbinkertn@umich.edu        for param in config_parameters:
503873Sbinkertn@umich.edu            if param.pointer:
513873Sbinkertn@umich.edu                var = Var(symtab, param.name, location, param.type_ast.type,
523873Sbinkertn@umich.edu                          "(*m_%s_ptr)" % param.name, {}, self)
533322Shsul@eecs.umich.edu            else:
542995SN/A                var = Var(symtab, param.name, location, param.type_ast.type,
552995SN/A                          "m_%s" % param.name, {}, self)
562995SN/A            self.symtab.registerSym(param.name, var)
572995SN/A
582995SN/A        self.states = orderdict()
593143Shsul@eecs.umich.edu        self.events = orderdict()
603322Shsul@eecs.umich.edu        self.actions = orderdict()
613322Shsul@eecs.umich.edu        self.transitions = []
623025Ssaidi@eecs.umich.edu        self.in_ports = []
633143Shsul@eecs.umich.edu        self.functions = []
643143Shsul@eecs.umich.edu        self.objects = []
653322Shsul@eecs.umich.edu
663444Sktlim@umich.edu        self.message_buffer_names = []
673322Shsul@eecs.umich.edu
682710SN/A    def __repr__(self):
692710SN/A        return "[StateMachine: %s]" % self.ident
702710SN/A
712710SN/A    def addState(self, state):
722710SN/A        assert self.table is None
732710SN/A        self.states[state.ident] = state
743322Shsul@eecs.umich.edu
753304Sstever@eecs.umich.edu    def addEvent(self, event):
763322Shsul@eecs.umich.edu        assert self.table is None
773322Shsul@eecs.umich.edu        self.events[event.ident] = event
783304Sstever@eecs.umich.edu
793481Shsul@eecs.umich.edu    def addAction(self, action):
803481Shsul@eecs.umich.edu        assert self.table is None
812566SN/A
823322Shsul@eecs.umich.edu        # Check for duplicate action
833322Shsul@eecs.umich.edu        for other in self.actions.itervalues():
842995SN/A            if action.ident == other.ident:
852995SN/A                action.warning("Duplicate action definition: %s" % action.ident)
863304Sstever@eecs.umich.edu                action.error("Duplicate action definition: %s" % action.ident)
873304Sstever@eecs.umich.edu            if action.short == other.short:
883304Sstever@eecs.umich.edu                other.warning("Duplicate action shorthand: %s" % other.ident)
892995SN/A                other.warning("    shorthand = %s" % other.short)
902995SN/A                action.warning("Duplicate action shorthand: %s" % action.ident)
912995SN/A                action.error("    shorthand = %s" % action.short)
922917SN/A
932995SN/A        self.actions[action.ident] = action
943304Sstever@eecs.umich.edu
952995SN/A    def addTransition(self, trans):
963304Sstever@eecs.umich.edu        assert self.table is None
973304Sstever@eecs.umich.edu        self.transitions.append(trans)
983819Shsul@eecs.umich.edu
993819Shsul@eecs.umich.edu    def addInPort(self, var):
1005222Sksewell@umich.edu        self.in_ports.append(var)
1015222Sksewell@umich.edu
1023819Shsul@eecs.umich.edu    def addFunc(self, func):
1033819Shsul@eecs.umich.edu        # register func in the symbol table
1045133Sgblack@eecs.umich.edu        self.symtab.registerSym(str(func), func)
1055299Sgblack@eecs.umich.edu        self.functions.append(func)
1063819Shsul@eecs.umich.edu
1073819Shsul@eecs.umich.edu    def addObject(self, obj):
1083819Shsul@eecs.umich.edu        self.objects.append(obj)
1093873Sbinkertn@umich.edu
1103873Sbinkertn@umich.edu    # Needs to be called before accessing the table
1113873Sbinkertn@umich.edu    def buildTable(self):
1123873Sbinkertn@umich.edu        assert self.table is None
1133873Sbinkertn@umich.edu
1143873Sbinkertn@umich.edu        table = {}
1153312Sstever@eecs.umich.edu
1163668Srdreslin@umich.edu        for trans in self.transitions:
1173668Srdreslin@umich.edu            # Track which actions we touch so we know if we use them
1183668Srdreslin@umich.edu            # all -- really this should be done for all symbols as
1193668Srdreslin@umich.edu            # part of the symbol table, then only trigger it for
1203668Srdreslin@umich.edu            # Actions, States, Events, etc.
1213668Srdreslin@umich.edu
1223668Srdreslin@umich.edu            for action in trans.actions:
1233322Shsul@eecs.umich.edu                action.used = True
1245142Ssaidi@eecs.umich.edu
1255142Ssaidi@eecs.umich.edu            index = (trans.state, trans.event)
1265142Ssaidi@eecs.umich.edu            if index in table:
1275142Ssaidi@eecs.umich.edu                table[index].warning("Duplicate transition: %s" % table[index])
1285142Ssaidi@eecs.umich.edu                trans.error("Duplicate transition: %s" % trans)
1295142Ssaidi@eecs.umich.edu            table[index] = trans
1305142Ssaidi@eecs.umich.edu
1315142Ssaidi@eecs.umich.edu        # Look at all actions to make sure we used them all
1325142Ssaidi@eecs.umich.edu        for action in self.actions.itervalues():
1333312Sstever@eecs.umich.edu            if not action.used:
1343514Sktlim@umich.edu                error_msg = "Unused action: %s" % action.ident
1353395Shsul@eecs.umich.edu                if "desc" in action:
1363448Shsul@eecs.umich.edu                    error_msg += ", "  + action.desc
1373668Srdreslin@umich.edu                action.warning(error_msg)
1383668Srdreslin@umich.edu        self.table = table
1393668Srdreslin@umich.edu
1403668Srdreslin@umich.edu    def writeCodeFiles(self, path):
1413005Sstever@eecs.umich.edu        self.printControllerPython(path)
1424968Sacolyte@umich.edu        self.printControllerHH(path)
1434968Sacolyte@umich.edu        self.printControllerCC(path)
1444968Sacolyte@umich.edu        self.printCSwitch(path)
1455222Sksewell@umich.edu        self.printCWakeup(path)
1465254Sksewell@umich.edu        self.printProfilerCC(path)
1475222Sksewell@umich.edu        self.printProfilerHH(path)
1483005Sstever@eecs.umich.edu
1493819Shsul@eecs.umich.edu        for func in self.functions:
1503819Shsul@eecs.umich.edu            func.writeCodeFiles(path)
1515222Sksewell@umich.edu
1525222Sksewell@umich.edu    def printControllerPython(self, path):
1533819Shsul@eecs.umich.edu        code = self.symtab.codeFormatter()
1543819Shsul@eecs.umich.edu        ident = self.ident
1555133Sgblack@eecs.umich.edu        py_ident = "%s_Controller" % ident
1565133Sgblack@eecs.umich.edu        c_ident = "%s_Controller" % self.ident
1573322Shsul@eecs.umich.edu        code('''
1583322Shsul@eecs.umich.edufrom m5.params import *
1594968Sacolyte@umich.edufrom m5.SimObject import SimObject
1604968Sacolyte@umich.edufrom Controller import RubyController
1614837Ssaidi@eecs.umich.edu
1624837Ssaidi@eecs.umich.educlass $py_ident(RubyController):
1634837Ssaidi@eecs.umich.edu    type = '$py_ident'
1643322Shsul@eecs.umich.edu''')
1653005Sstever@eecs.umich.edu        code.indent()
1664167Sbinkertn@umich.edu        for param in self.config_parameters:
1673005Sstever@eecs.umich.edu            dflt_str = ''
1683005Sstever@eecs.umich.edu            if param.default is not None:
1693005Sstever@eecs.umich.edu                dflt_str = str(param.default) + ', '
1702566SN/A            if python_class_map.has_key(param.type_ast.type.c_ident):
1713481Shsul@eecs.umich.edu                python_type = python_class_map[param.type_ast.type.c_ident]
172                code('${{param.name}} = Param.${{python_type}}(${dflt_str}"")')
173            else:
174                self.error("Unknown c++ to python class conversion for c++ " \
175                           "type: '%s'. Please update the python_class_map " \
176                           "in StateMachine.py", param.type_ast.type.c_ident)
177        code.dedent()
178        code.write(path, '%s.py' % py_ident)
179
180
181    def printControllerHH(self, path):
182        '''Output the method declarations for the class declaration'''
183        code = self.symtab.codeFormatter()
184        ident = self.ident
185        c_ident = "%s_Controller" % self.ident
186
187        self.message_buffer_names = []
188
189        code('''
190/** \\file $c_ident.hh
191 *
192 * Auto generated C++ code started by $__file__:$__line__
193 * Created by slicc definition of Module "${{self.short}}"
194 */
195
196#ifndef __${ident}_CONTROLLER_HH__
197#define __${ident}_CONTROLLER_HH__
198
199#include <iostream>
200#include <sstream>
201#include <string>
202
203#include "params/$c_ident.hh"
204
205#include "mem/ruby/common/Global.hh"
206#include "mem/ruby/common/Consumer.hh"
207#include "mem/ruby/slicc_interface/AbstractController.hh"
208#include "mem/protocol/TransitionResult.hh"
209#include "mem/protocol/Types.hh"
210#include "mem/protocol/${ident}_Profiler.hh"
211''')
212
213        seen_types = set()
214        for var in self.objects:
215            if var.type.ident not in seen_types and not var.type.isPrimitive:
216                code('#include "mem/protocol/${{var.type.c_ident}}.hh"')
217            seen_types.add(var.type.ident)
218
219        # for adding information to the protocol debug trace
220        code('''
221extern std::stringstream ${ident}_transitionComment;
222
223class $c_ident : public AbstractController
224{
225// the coherence checker needs to call isBlockExclusive() and isBlockShared()
226// making the Chip a friend class is an easy way to do this for now
227
228public:
229    typedef ${c_ident}Params Params;
230    $c_ident(const Params *p);
231    static int getNumControllers();
232    void init();
233    MessageBuffer* getMandatoryQueue() const;
234    const int & getVersion() const;
235    const std::string toString() const;
236    const std::string getName() const;
237    const MachineType getMachineType() const;
238    void initNetworkPtr(Network* net_ptr) { m_net_ptr = net_ptr; }
239    void print(std::ostream& out) const;
240    void printConfig(std::ostream& out) const;
241    void wakeup();
242    void printStats(std::ostream& out) const;
243    void clearStats();
244    void blockOnQueue(Address addr, MessageBuffer* port);
245    void unblock(Address addr);
246
247private:
248''')
249
250        code.indent()
251        # added by SS
252        for param in self.config_parameters:
253            if param.pointer:
254                code('${{param.type_ast.type}}* m_${{param.ident}}_ptr;')
255            else:
256                code('${{param.type_ast.type}} m_${{param.ident}};')
257
258        code('''
259int m_number_of_TBEs;
260
261TransitionResult doTransition(${ident}_Event event,
262                              ${ident}_State state,
263                              const Address& addr);
264
265TransitionResult doTransitionWorker(${ident}_Event event,
266                                    ${ident}_State state,
267                                    ${ident}_State& next_state,
268                                    const Address& addr);
269
270std::string m_name;
271int m_transitions_per_cycle;
272int m_buffer_size;
273int m_recycle_latency;
274std::map<std::string, std::string> m_cfg;
275NodeID m_version;
276Network* m_net_ptr;
277MachineID m_machineID;
278bool m_is_blocking;
279std::map<Address, MessageBuffer*> m_block_map;
280${ident}_Profiler s_profiler;
281static int m_num_controllers;
282
283// Internal functions
284''')
285
286        for func in self.functions:
287            proto = func.prototype
288            if proto:
289                code('$proto')
290
291        code('''
292
293// Actions
294''')
295        for action in self.actions.itervalues():
296            code('/** \\brief ${{action.desc}} */')
297            code('void ${{action.ident}}(const Address& addr);')
298
299        # the controller internal variables
300        code('''
301
302// Objects
303''')
304        for var in self.objects:
305            th = var.get("template_hack", "")
306            code('${{var.type.c_ident}}$th* m_${{var.c_ident}}_ptr;')
307
308            if var.type.ident == "MessageBuffer":
309                self.message_buffer_names.append("m_%s_ptr" % var.c_ident)
310
311        code.dedent()
312        code('};')
313        code('#endif // __${ident}_CONTROLLER_H__')
314        code.write(path, '%s.hh' % c_ident)
315
316    def printControllerCC(self, path):
317        '''Output the actions for performing the actions'''
318
319        code = self.symtab.codeFormatter()
320        ident = self.ident
321        c_ident = "%s_Controller" % self.ident
322
323        code('''
324/** \\file $c_ident.cc
325 *
326 * Auto generated C++ code started by $__file__:$__line__
327 * Created by slicc definition of Module "${{self.short}}"
328 */
329
330#include <sstream>
331#include <string>
332
333#include "base/cprintf.hh"
334#include "mem/protocol/${ident}_Controller.hh"
335#include "mem/protocol/${ident}_State.hh"
336#include "mem/protocol/${ident}_Event.hh"
337#include "mem/protocol/Types.hh"
338#include "mem/ruby/common/Global.hh"
339#include "mem/ruby/slicc_interface/RubySlicc_includes.hh"
340#include "mem/ruby/system/System.hh"
341
342using namespace std;
343''')
344
345        # include object classes
346        seen_types = set()
347        for var in self.objects:
348            if var.type.ident not in seen_types and not var.type.isPrimitive:
349                code('#include "mem/protocol/${{var.type.c_ident}}.hh"')
350            seen_types.add(var.type.ident)
351
352        code('''
353$c_ident *
354${c_ident}Params::create()
355{
356    return new $c_ident(this);
357}
358
359int $c_ident::m_num_controllers = 0;
360
361// for adding information to the protocol debug trace
362stringstream ${ident}_transitionComment;
363#define APPEND_TRANSITION_COMMENT(str) (${ident}_transitionComment << str)
364
365/** \\brief constructor */
366$c_ident::$c_ident(const Params *p)
367    : AbstractController(p)
368{
369    m_version = p->version;
370    m_transitions_per_cycle = p->transitions_per_cycle;
371    m_buffer_size = p->buffer_size;
372    m_recycle_latency = p->recycle_latency;
373    m_number_of_TBEs = p->number_of_TBEs;
374    m_is_blocking = false;
375''')
376        code.indent()
377
378        #
379        # After initializing the universal machine parameters, initialize the
380        # this machines config parameters.  Also detemine if these configuration
381        # params include a sequencer.  This information will be used later for
382        # contecting the sequencer back to the L1 cache controller.
383        #
384        contains_sequencer = False
385        for param in self.config_parameters:
386            if param.name == "sequencer" or param.name == "dma_sequencer":
387                contains_sequencer = True
388            if param.pointer:
389                code('m_${{param.name}}_ptr = p->${{param.name}};')
390            else:
391                code('m_${{param.name}} = p->${{param.name}};')
392
393        #
394        # For the l1 cache controller, add the special atomic support which
395        # includes passing the sequencer a pointer to the controller.
396        #
397        if self.ident == "L1Cache":
398            if not contains_sequencer:
399                self.error("The L1Cache controller must include the sequencer " \
400                           "configuration parameter")
401
402            code('''
403m_sequencer_ptr->setController(this);
404''')
405        #
406        # For the DMA controller, pass the sequencer a pointer to the
407        # controller.
408        #
409        if self.ident == "DMA":
410            if not contains_sequencer:
411                self.error("The DMA controller must include the sequencer " \
412                           "configuration parameter")
413
414            code('''
415m_dma_sequencer_ptr->setController(this);
416''')
417
418        code('m_num_controllers++;')
419        for var in self.objects:
420            if var.ident.find("mandatoryQueue") >= 0:
421                code('m_${{var.c_ident}}_ptr = new ${{var.type.c_ident}}();')
422
423        code.dedent()
424        code('''
425}
426
427void
428$c_ident::init()
429{
430    MachineType machine_type;
431    int base;
432
433    m_machineID.type = MachineType_${ident};
434    m_machineID.num = m_version;
435
436    // initialize objects
437    s_profiler.setVersion(m_version);
438
439''')
440
441        code.indent()
442        for var in self.objects:
443            vtype = var.type
444            vid = "m_%s_ptr" % var.c_ident
445            if "network" not in var:
446                # Not a network port object
447                if "primitive" in vtype:
448                    code('$vid = new ${{vtype.c_ident}};')
449                    if "default" in var:
450                        code('(*$vid) = ${{var["default"]}};')
451                else:
452                    # Normal Object
453                    # added by SS
454                    if "factory" in var:
455                        code('$vid = ${{var["factory"]}};')
456                    elif var.ident.find("mandatoryQueue") < 0:
457                        th = var.get("template_hack", "")
458                        expr = "%s  = new %s%s" % (vid, vtype.c_ident, th)
459
460                        args = ""
461                        if "non_obj" not in vtype and not vtype.isEnumeration:
462                            if expr.find("TBETable") >= 0:
463                                args = "m_number_of_TBEs"
464                            else:
465                                args = var.get("constructor_hack", "")
466
467                        code('$expr($args);')
468
469                    code('assert($vid != NULL);')
470
471                    if "default" in var:
472                        code('*$vid = ${{var["default"]}}; // Object default')
473                    elif "default" in vtype:
474                        comment = "Type %s default" % vtype.ident
475                        code('*$vid = ${{vtype["default"]}}; // $comment')
476
477                    # Set ordering
478                    if "ordered" in var and "trigger_queue" not in var:
479                        # A buffer
480                        code('$vid->setOrdering(${{var["ordered"]}});')
481
482                    # Set randomization
483                    if "random" in var:
484                        # A buffer
485                        code('$vid->setRandomization(${{var["random"]}});')
486
487                    # Set Priority
488                    if vtype.isBuffer and \
489                           "rank" in var and "trigger_queue" not in var:
490                        code('$vid->setPriority(${{var["rank"]}});')
491            else:
492                # Network port object
493                network = var["network"]
494                ordered =  var["ordered"]
495                vnet = var["virtual_network"]
496
497                assert var.machine is not None
498                code('''
499machine_type = string_to_MachineType("${{var.machine.ident}}");
500base = MachineType_base_number(machine_type);
501$vid = m_net_ptr->get${network}NetQueue(m_version + base, $ordered, $vnet);
502''')
503
504                code('assert($vid != NULL);')
505
506                # Set ordering
507                if "ordered" in var:
508                    # A buffer
509                    code('$vid->setOrdering(${{var["ordered"]}});')
510
511                # Set randomization
512                if "random" in var:
513                    # A buffer
514                    code('$vid->setRandomization(${{var["random"]}})')
515
516                # Set Priority
517                if "rank" in var:
518                    code('$vid->setPriority(${{var["rank"]}})')
519
520                # Set buffer size
521                if vtype.isBuffer:
522                    code('''
523if (m_buffer_size > 0) {
524    $vid->setSize(m_buffer_size);
525}
526''')
527
528                # set description (may be overriden later by port def)
529                code('''
530$vid->setDescription("[Version " + to_string(m_version) + ", ${ident}, name=${{var.c_ident}}]");
531
532''')
533
534        # Set the queue consumers
535        code.insert_newline()
536        for port in self.in_ports:
537            code('${{port.code}}.setConsumer(this);')
538
539        # Set the queue descriptions
540        code.insert_newline()
541        for port in self.in_ports:
542            code('${{port.code}}.setDescription("[Version " + to_string(m_version) + ", $ident, $port]");')
543
544        # Initialize the transition profiling
545        code.insert_newline()
546        for trans in self.transitions:
547            # Figure out if we stall
548            stall = False
549            for action in trans.actions:
550                if action.ident == "z_stall":
551                    stall = True
552
553            # Only possible if it is not a 'z' case
554            if not stall:
555                state = "%s_State_%s" % (self.ident, trans.state.ident)
556                event = "%s_Event_%s" % (self.ident, trans.event.ident)
557                code('s_profiler.possibleTransition($state, $event);')
558
559        # added by SS to initialize recycle_latency of message buffers
560        for buf in self.message_buffer_names:
561            code("$buf->setRecycleLatency(m_recycle_latency);")
562
563        code.dedent()
564        code('}')
565
566        has_mandatory_q = False
567        for port in self.in_ports:
568            if port.code.find("mandatoryQueue_ptr") >= 0:
569                has_mandatory_q = True
570
571        if has_mandatory_q:
572            mq_ident = "m_%s_mandatoryQueue_ptr" % self.ident
573        else:
574            mq_ident = "NULL"
575
576        code('''
577int
578$c_ident::getNumControllers()
579{
580    return m_num_controllers;
581}
582
583MessageBuffer*
584$c_ident::getMandatoryQueue() const
585{
586    return $mq_ident;
587}
588
589const int &
590$c_ident::getVersion() const
591{
592    return m_version;
593}
594
595const string
596$c_ident::toString() const
597{
598    return "$c_ident";
599}
600
601const string
602$c_ident::getName() const
603{
604    return m_name;
605}
606
607const MachineType
608$c_ident::getMachineType() const
609{
610    return MachineType_${ident};
611}
612
613void
614$c_ident::blockOnQueue(Address addr, MessageBuffer* port)
615{
616    m_is_blocking = true;
617    m_block_map[addr] = port;
618}
619
620void
621$c_ident::unblock(Address addr)
622{
623    m_block_map.erase(addr);
624    if (m_block_map.size() == 0) {
625       m_is_blocking = false;
626    }
627}
628
629void
630$c_ident::print(ostream& out) const
631{
632    out << "[$c_ident " << m_version << "]";
633}
634
635void
636$c_ident::printConfig(ostream& out) const
637{
638    out << "$c_ident config: " << m_name << endl;
639    out << "  version: " << m_version << endl;
640    map<string, string>::const_iterator it;
641    for (it = m_cfg.begin(); it != m_cfg.end(); it++)
642        out << "  " << it->first << ": " << it->second << endl;
643}
644
645void
646$c_ident::printStats(ostream& out) const
647{
648''')
649        #
650        # Cache and Memory Controllers have specific profilers associated with
651        # them.  Print out these stats before dumping state transition stats.
652        #
653        for param in self.config_parameters:
654            if param.type_ast.type.ident == "CacheMemory" or \
655               param.type_ast.type.ident == "DirectoryMemory" or \
656                   param.type_ast.type.ident == "MemoryControl":
657                assert(param.pointer)
658                code('    m_${{param.ident}}_ptr->printStats(out);')
659
660        code('''
661    s_profiler.dumpStats(out);
662}
663
664void $c_ident::clearStats() {
665''')
666        #
667        # Cache and Memory Controllers have specific profilers associated with
668        # them.  These stats must be cleared too.
669        #
670        for param in self.config_parameters:
671            if param.type_ast.type.ident == "CacheMemory" or \
672                   param.type_ast.type.ident == "MemoryControl":
673                assert(param.pointer)
674                code('    m_${{param.ident}}_ptr->clearStats();')
675
676        code('''
677    s_profiler.clearStats();
678}
679
680// Actions
681''')
682
683        for action in self.actions.itervalues():
684            if "c_code" not in action:
685                continue
686
687            code('''
688/** \\brief ${{action.desc}} */
689void
690$c_ident::${{action.ident}}(const Address& addr)
691{
692    DEBUG_MSG(GENERATED_COMP, HighPrio, "executing");
693    ${{action["c_code"]}}
694}
695
696''')
697        code.write(path, "%s.cc" % c_ident)
698
699    def printCWakeup(self, path):
700        '''Output the wakeup loop for the events'''
701
702        code = self.symtab.codeFormatter()
703        ident = self.ident
704
705        code('''
706// Auto generated C++ code started by $__file__:$__line__
707// ${ident}: ${{self.short}}
708
709#include "base/misc.hh"
710#include "mem/ruby/common/Global.hh"
711#include "mem/ruby/slicc_interface/RubySlicc_includes.hh"
712#include "mem/protocol/${ident}_Controller.hh"
713#include "mem/protocol/${ident}_State.hh"
714#include "mem/protocol/${ident}_Event.hh"
715#include "mem/protocol/Types.hh"
716#include "mem/ruby/system/System.hh"
717
718using namespace std;
719
720void
721${ident}_Controller::wakeup()
722{
723    // DEBUG_EXPR(GENERATED_COMP, MedPrio, *this);
724    // DEBUG_EXPR(GENERATED_COMP, MedPrio, g_eventQueue_ptr->getTime());
725
726    int counter = 0;
727    while (true) {
728        // Some cases will put us into an infinite loop without this limit
729        assert(counter <= m_transitions_per_cycle);
730        if (counter == m_transitions_per_cycle) {
731            // Count how often we are fully utilized
732            g_system_ptr->getProfiler()->controllerBusy(m_machineID);
733
734            // Wakeup in another cycle and try again
735            g_eventQueue_ptr->scheduleEvent(this, 1);
736            break;
737        }
738''')
739
740        code.indent()
741        code.indent()
742
743        # InPorts
744        #
745        for port in self.in_ports:
746            code.indent()
747            code('// ${ident}InPort $port')
748            code('${{port["c_code_in_port"]}}')
749            code.dedent()
750
751            code('')
752
753        code.dedent()
754        code.dedent()
755        code('''
756        break;  // If we got this far, we have nothing left todo
757    }
758    // g_eventQueue_ptr->scheduleEvent(this, 1);
759    // DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
760}
761''')
762
763        code.write(path, "%s_Wakeup.cc" % self.ident)
764
765    def printCSwitch(self, path):
766        '''Output switch statement for transition table'''
767
768        code = self.symtab.codeFormatter()
769        ident = self.ident
770
771        code('''
772// Auto generated C++ code started by $__file__:$__line__
773// ${ident}: ${{self.short}}
774
775#include "mem/ruby/common/Global.hh"
776#include "mem/protocol/${ident}_Controller.hh"
777#include "mem/protocol/${ident}_State.hh"
778#include "mem/protocol/${ident}_Event.hh"
779#include "mem/protocol/Types.hh"
780#include "mem/ruby/system/System.hh"
781
782#define HASH_FUN(state, event)  ((int(state)*${ident}_Event_NUM)+int(event))
783
784#define GET_TRANSITION_COMMENT() (${ident}_transitionComment.str())
785#define CLEAR_TRANSITION_COMMENT() (${ident}_transitionComment.str(""))
786
787TransitionResult
788${ident}_Controller::doTransition(${ident}_Event event,
789                                  ${ident}_State state,
790                                  const Address &addr)
791{
792    ${ident}_State next_state = state;
793
794    DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
795    DEBUG_MSG(GENERATED_COMP, MedPrio, *this);
796    DEBUG_EXPR(GENERATED_COMP, MedPrio, g_eventQueue_ptr->getTime());
797    DEBUG_EXPR(GENERATED_COMP, MedPrio,state);
798    DEBUG_EXPR(GENERATED_COMP, MedPrio,event);
799    DEBUG_EXPR(GENERATED_COMP, MedPrio,addr);
800
801    TransitionResult result =
802        doTransitionWorker(event, state, next_state, addr);
803
804    if (result == TransitionResult_Valid) {
805        DEBUG_EXPR(GENERATED_COMP, MedPrio, next_state);
806        DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
807        s_profiler.countTransition(state, event);
808        if (Debug::getProtocolTrace()) {
809            g_system_ptr->getProfiler()->profileTransition("${ident}",
810                    m_version, addr,
811                    ${ident}_State_to_string(state),
812                    ${ident}_Event_to_string(event),
813                    ${ident}_State_to_string(next_state),
814                    GET_TRANSITION_COMMENT());
815        }
816    CLEAR_TRANSITION_COMMENT();
817    ${ident}_setState(addr, next_state);
818
819    } else if (result == TransitionResult_ResourceStall) {
820        if (Debug::getProtocolTrace()) {
821            g_system_ptr->getProfiler()->profileTransition("${ident}",
822                   m_version, addr,
823                   ${ident}_State_to_string(state),
824                   ${ident}_Event_to_string(event),
825                   ${ident}_State_to_string(next_state),
826                   "Resource Stall");
827        }
828    } else if (result == TransitionResult_ProtocolStall) {
829        DEBUG_MSG(GENERATED_COMP, HighPrio, "stalling");
830        DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
831        if (Debug::getProtocolTrace()) {
832            g_system_ptr->getProfiler()->profileTransition("${ident}",
833                   m_version, addr,
834                   ${ident}_State_to_string(state),
835                   ${ident}_Event_to_string(event),
836                   ${ident}_State_to_string(next_state),
837                   "Protocol Stall");
838        }
839    }
840
841    return result;
842}
843
844TransitionResult
845${ident}_Controller::doTransitionWorker(${ident}_Event event,
846                                        ${ident}_State state,
847                                        ${ident}_State& next_state,
848                                        const Address& addr)
849{
850    switch(HASH_FUN(state, event)) {
851''')
852
853        # This map will allow suppress generating duplicate code
854        cases = orderdict()
855
856        for trans in self.transitions:
857            case_string = "%s_State_%s, %s_Event_%s" % \
858                (self.ident, trans.state.ident, self.ident, trans.event.ident)
859
860            case = self.symtab.codeFormatter()
861            # Only set next_state if it changes
862            if trans.state != trans.nextState:
863                ns_ident = trans.nextState.ident
864                case('next_state = ${ident}_State_${ns_ident};')
865
866            actions = trans.actions
867
868            # Check for resources
869            case_sorter = []
870            res = trans.resources
871            for key,val in res.iteritems():
872                if key.type.ident != "DNUCAStopTable":
873                    val = '''
874if (!%s.areNSlotsAvailable(%s))
875    return TransitionResult_ResourceStall;
876''' % (key.code, val)
877                case_sorter.append(val)
878
879
880            # Emit the code sequences in a sorted order.  This makes the
881            # output deterministic (without this the output order can vary
882            # since Map's keys() on a vector of pointers is not deterministic
883            for c in sorted(case_sorter):
884                case("$c")
885
886            # Figure out if we stall
887            stall = False
888            for action in actions:
889                if action.ident == "z_stall":
890                    stall = True
891                    break
892
893            if stall:
894                case('return TransitionResult_ProtocolStall;')
895            else:
896                for action in actions:
897                    case('${{action.ident}}(addr);')
898                case('return TransitionResult_Valid;')
899
900            case = str(case)
901
902            # Look to see if this transition code is unique.
903            if case not in cases:
904                cases[case] = []
905
906            cases[case].append(case_string)
907
908        # Walk through all of the unique code blocks and spit out the
909        # corresponding case statement elements
910        for case,transitions in cases.iteritems():
911            # Iterative over all the multiple transitions that share
912            # the same code
913            for trans in transitions:
914                code('  case HASH_FUN($trans):')
915            code('    $case')
916
917        code('''
918      default:
919        WARN_EXPR(m_version);
920        WARN_EXPR(g_eventQueue_ptr->getTime());
921        WARN_EXPR(addr);
922        WARN_EXPR(event);
923        WARN_EXPR(state);
924        ERROR_MSG(\"Invalid transition\");
925    }
926    return TransitionResult_Valid;
927}
928''')
929        code.write(path, "%s_Transitions.cc" % self.ident)
930
931    def printProfilerHH(self, path):
932        code = self.symtab.codeFormatter()
933        ident = self.ident
934
935        code('''
936// Auto generated C++ code started by $__file__:$__line__
937// ${ident}: ${{self.short}}
938
939#ifndef __${ident}_PROFILER_HH_
940#define __${ident}_PROFILER_HH_
941
942#include <iostream>
943
944#include "mem/ruby/common/Global.hh"
945#include "mem/protocol/${ident}_State.hh"
946#include "mem/protocol/${ident}_Event.hh"
947
948class ${ident}_Profiler
949{
950  public:
951    ${ident}_Profiler();
952    void setVersion(int version);
953    void countTransition(${ident}_State state, ${ident}_Event event);
954    void possibleTransition(${ident}_State state, ${ident}_Event event);
955    void dumpStats(std::ostream& out) const;
956    void clearStats();
957
958  private:
959    int m_counters[${ident}_State_NUM][${ident}_Event_NUM];
960    int m_event_counters[${ident}_Event_NUM];
961    bool m_possible[${ident}_State_NUM][${ident}_Event_NUM];
962    int m_version;
963};
964
965#endif // __${ident}_PROFILER_HH__
966''')
967        code.write(path, "%s_Profiler.hh" % self.ident)
968
969    def printProfilerCC(self, path):
970        code = self.symtab.codeFormatter()
971        ident = self.ident
972
973        code('''
974// Auto generated C++ code started by $__file__:$__line__
975// ${ident}: ${{self.short}}
976
977#include "mem/protocol/${ident}_Profiler.hh"
978
979${ident}_Profiler::${ident}_Profiler()
980{
981    for (int state = 0; state < ${ident}_State_NUM; state++) {
982        for (int event = 0; event < ${ident}_Event_NUM; event++) {
983            m_possible[state][event] = false;
984            m_counters[state][event] = 0;
985        }
986    }
987    for (int event = 0; event < ${ident}_Event_NUM; event++) {
988        m_event_counters[event] = 0;
989    }
990}
991
992void
993${ident}_Profiler::setVersion(int version)
994{
995    m_version = version;
996}
997
998void
999${ident}_Profiler::clearStats()
1000{
1001    for (int state = 0; state < ${ident}_State_NUM; state++) {
1002        for (int event = 0; event < ${ident}_Event_NUM; event++) {
1003            m_counters[state][event] = 0;
1004        }
1005    }
1006
1007    for (int event = 0; event < ${ident}_Event_NUM; event++) {
1008        m_event_counters[event] = 0;
1009    }
1010}
1011void
1012${ident}_Profiler::countTransition(${ident}_State state, ${ident}_Event event)
1013{
1014    assert(m_possible[state][event]);
1015    m_counters[state][event]++;
1016    m_event_counters[event]++;
1017}
1018void
1019${ident}_Profiler::possibleTransition(${ident}_State state,
1020                                      ${ident}_Event event)
1021{
1022    m_possible[state][event] = true;
1023}
1024
1025void
1026${ident}_Profiler::dumpStats(std::ostream& out) const
1027{
1028    using namespace std;
1029
1030    out << " --- ${ident} " << m_version << " ---" << endl;
1031    out << " - Event Counts -" << endl;
1032    for (int event = 0; event < ${ident}_Event_NUM; event++) {
1033        int count = m_event_counters[event];
1034        out << (${ident}_Event) event << "  " << count << endl;
1035    }
1036    out << endl;
1037    out << " - Transitions -" << endl;
1038    for (int state = 0; state < ${ident}_State_NUM; state++) {
1039        for (int event = 0; event < ${ident}_Event_NUM; event++) {
1040            if (m_possible[state][event]) {
1041                int count = m_counters[state][event];
1042                out << (${ident}_State) state << "  "
1043                    << (${ident}_Event) event << "  " << count;
1044                if (count == 0) {
1045                    out << " <-- ";
1046                }
1047                out << endl;
1048            }
1049        }
1050        out << endl;
1051    }
1052}
1053''')
1054        code.write(path, "%s_Profiler.cc" % self.ident)
1055
1056    # **************************
1057    # ******* HTML Files *******
1058    # **************************
1059    def frameRef(self, click_href, click_target, over_href, over_num, text):
1060        code = self.symtab.codeFormatter(fix_newlines=False)
1061        code("""<A href=\"$click_href\" target=\"$click_target\" onmouseover=\"
1062    if (parent.frames[$over_num].location != parent.location + '$over_href') {
1063        parent.frames[$over_num].location='$over_href'
1064    }\">
1065    ${{html.formatShorthand(text)}}
1066    </A>""")
1067        return str(code)
1068
1069    def writeHTMLFiles(self, path):
1070        # Create table with no row hilighted
1071        self.printHTMLTransitions(path, None)
1072
1073        # Generate transition tables
1074        for state in self.states.itervalues():
1075            self.printHTMLTransitions(path, state)
1076
1077        # Generate action descriptions
1078        for action in self.actions.itervalues():
1079            name = "%s_action_%s.html" % (self.ident, action.ident)
1080            code = html.createSymbol(action, "Action")
1081            code.write(path, name)
1082
1083        # Generate state descriptions
1084        for state in self.states.itervalues():
1085            name = "%s_State_%s.html" % (self.ident, state.ident)
1086            code = html.createSymbol(state, "State")
1087            code.write(path, name)
1088
1089        # Generate event descriptions
1090        for event in self.events.itervalues():
1091            name = "%s_Event_%s.html" % (self.ident, event.ident)
1092            code = html.createSymbol(event, "Event")
1093            code.write(path, name)
1094
1095    def printHTMLTransitions(self, path, active_state):
1096        code = self.symtab.codeFormatter()
1097
1098        code('''
1099<HTML>
1100<BODY link="blue" vlink="blue">
1101
1102<H1 align="center">${{html.formatShorthand(self.short)}}:
1103''')
1104        code.indent()
1105        for i,machine in enumerate(self.symtab.getAllType(StateMachine)):
1106            mid = machine.ident
1107            if i != 0:
1108                extra = " - "
1109            else:
1110                extra = ""
1111            if machine == self:
1112                code('$extra$mid')
1113            else:
1114                code('$extra<A target="Table" href="${mid}_table.html">$mid</A>')
1115        code.dedent()
1116
1117        code("""
1118</H1>
1119
1120<TABLE border=1>
1121<TR>
1122  <TH> </TH>
1123""")
1124
1125        for event in self.events.itervalues():
1126            href = "%s_Event_%s.html" % (self.ident, event.ident)
1127            ref = self.frameRef(href, "Status", href, "1", event.short)
1128            code('<TH bgcolor=white>$ref</TH>')
1129
1130        code('</TR>')
1131        # -- Body of table
1132        for state in self.states.itervalues():
1133            # -- Each row
1134            if state == active_state:
1135                color = "yellow"
1136            else:
1137                color = "white"
1138
1139            click = "%s_table_%s.html" % (self.ident, state.ident)
1140            over = "%s_State_%s.html" % (self.ident, state.ident)
1141            text = html.formatShorthand(state.short)
1142            ref = self.frameRef(click, "Table", over, "1", state.short)
1143            code('''
1144<TR>
1145  <TH bgcolor=$color>$ref</TH>
1146''')
1147
1148            # -- One column for each event
1149            for event in self.events.itervalues():
1150                trans = self.table.get((state,event), None)
1151                if trans is None:
1152                    # This is the no transition case
1153                    if state == active_state:
1154                        color = "#C0C000"
1155                    else:
1156                        color = "lightgrey"
1157
1158                    code('<TD bgcolor=$color>&nbsp;</TD>')
1159                    continue
1160
1161                next = trans.nextState
1162                stall_action = False
1163
1164                # -- Get the actions
1165                for action in trans.actions:
1166                    if action.ident == "z_stall" or \
1167                       action.ident == "zz_recycleMandatoryQueue":
1168                        stall_action = True
1169
1170                # -- Print out "actions/next-state"
1171                if stall_action:
1172                    if state == active_state:
1173                        color = "#C0C000"
1174                    else:
1175                        color = "lightgrey"
1176
1177                elif active_state and next.ident == active_state.ident:
1178                    color = "aqua"
1179                elif state == active_state:
1180                    color = "yellow"
1181                else:
1182                    color = "white"
1183
1184                code('<TD bgcolor=$color>')
1185                for action in trans.actions:
1186                    href = "%s_action_%s.html" % (self.ident, action.ident)
1187                    ref = self.frameRef(href, "Status", href, "1",
1188                                        action.short)
1189                    code('  $ref')
1190                if next != state:
1191                    if trans.actions:
1192                        code('/')
1193                    click = "%s_table_%s.html" % (self.ident, next.ident)
1194                    over = "%s_State_%s.html" % (self.ident, next.ident)
1195                    ref = self.frameRef(click, "Table", over, "1", next.short)
1196                    code("$ref")
1197                code("</TD>")
1198
1199            # -- Each row
1200            if state == active_state:
1201                color = "yellow"
1202            else:
1203                color = "white"
1204
1205            click = "%s_table_%s.html" % (self.ident, state.ident)
1206            over = "%s_State_%s.html" % (self.ident, state.ident)
1207            ref = self.frameRef(click, "Table", over, "1", state.short)
1208            code('''
1209  <TH bgcolor=$color>$ref</TH>
1210</TR>
1211''')
1212        code('''
1213<!- Column footer->
1214<TR>
1215  <TH> </TH>
1216''')
1217
1218        for event in self.events.itervalues():
1219            href = "%s_Event_%s.html" % (self.ident, event.ident)
1220            ref = self.frameRef(href, "Status", href, "1", event.short)
1221            code('<TH bgcolor=white>$ref</TH>')
1222        code('''
1223</TR>
1224</TABLE>
1225</BODY></HTML>
1226''')
1227
1228
1229        if active_state:
1230            name = "%s_table_%s.html" % (self.ident, active_state.ident)
1231        else:
1232            name = "%s_table.html" % self.ident
1233        code.write(path, name)
1234
1235__all__ = [ "StateMachine" ]
1236