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