StateMachine.py revision 10972:53d63eeee46f
1# Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
2# Copyright (c) 2009 The Hewlett-Packard Development Company
3# Copyright (c) 2013 Advanced Micro Devices, Inc.
4# All rights reserved.
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions are
8# met: redistributions of source code must retain the above copyright
9# notice, this list of conditions and the following disclaimer;
10# redistributions in binary form must reproduce the above copyright
11# notice, this list of conditions and the following disclaimer in the
12# documentation and/or other materials provided with the distribution;
13# neither the name of the copyright holders nor the names of its
14# contributors may be used to endorse or promote products derived from
15# this software without specific prior written permission.
16#
17# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29from m5.util import orderdict
30
31from slicc.symbols.Symbol import Symbol
32from slicc.symbols.Var import Var
33import slicc.generate.html as html
34import re
35
36python_class_map = {
37                    "int": "Int",
38                    "uint32_t" : "UInt32",
39                    "std::string": "String",
40                    "bool": "Bool",
41                    "CacheMemory": "RubyCache",
42                    "WireBuffer": "RubyWireBuffer",
43                    "Sequencer": "RubySequencer",
44                    "DirectoryMemory": "RubyDirectoryMemory",
45                    "MemoryControl": "MemoryControl",
46                    "DMASequencer": "DMASequencer",
47                    "Prefetcher":"Prefetcher",
48                    "Cycles":"Cycles",
49                   }
50
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