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