StateMachine.py revision 9819:e4b12145f4eb
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 std::string toString() const;
257
258    void print(std::ostream& out) const;
259    void wakeup();
260    void printStats(std::ostream& out) const;
261    void clearStats();
262    void regStats();
263    void collateStats();
264
265    void recordCacheTrace(int cntrl, CacheRecorder* tr);
266    Sequencer* getSequencer() const;
267
268    bool functionalReadBuffers(PacketPtr&);
269    uint32_t functionalWriteBuffers(PacketPtr&);
270
271    void countTransition(${ident}_State state, ${ident}_Event event);
272    void possibleTransition(${ident}_State state, ${ident}_Event event);
273    uint64 getEventCount(${ident}_Event event);
274    bool isPossible(${ident}_State state, ${ident}_Event event);
275    uint64 getTransitionCount(${ident}_State state, ${ident}_Event event);
276
277private:
278''')
279
280        code.indent()
281        # added by SS
282        for param in self.config_parameters:
283            if param.pointer:
284                code('${{param.type_ast.type}}* m_${{param.ident}}_ptr;')
285            else:
286                code('${{param.type_ast.type}} m_${{param.ident}};')
287
288        code('''
289TransitionResult doTransition(${ident}_Event event,
290''')
291
292        if self.EntryType != None:
293            code('''
294                              ${{self.EntryType.c_ident}}* m_cache_entry_ptr,
295''')
296        if self.TBEType != None:
297            code('''
298                              ${{self.TBEType.c_ident}}* m_tbe_ptr,
299''')
300
301        code('''
302                              const Address& addr);
303
304TransitionResult doTransitionWorker(${ident}_Event event,
305                                    ${ident}_State state,
306                                    ${ident}_State& next_state,
307''')
308
309        if self.TBEType != None:
310            code('''
311                                    ${{self.TBEType.c_ident}}*& m_tbe_ptr,
312''')
313        if self.EntryType != None:
314            code('''
315                                    ${{self.EntryType.c_ident}}*& m_cache_entry_ptr,
316''')
317
318        code('''
319                                    const Address& addr);
320
321int m_counters[${ident}_State_NUM][${ident}_Event_NUM];
322int m_event_counters[${ident}_Event_NUM];
323bool m_possible[${ident}_State_NUM][${ident}_Event_NUM];
324
325static std::vector<Stats::Vector *> eventVec;
326static std::vector<std::vector<Stats::Vector *> > transVec;
327static int m_num_controllers;
328
329// Internal functions
330''')
331
332        for func in self.functions:
333            proto = func.prototype
334            if proto:
335                code('$proto')
336
337        if has_peer:
338            code('void getQueuesFromPeer(AbstractController *);')
339        if self.EntryType != None:
340            code('''
341
342// Set and Reset for cache_entry variable
343void set_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, AbstractCacheEntry* m_new_cache_entry);
344void unset_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr);
345''')
346
347        if self.TBEType != None:
348            code('''
349
350// Set and Reset for tbe variable
351void set_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${ident}_TBE* m_new_tbe);
352void unset_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr);
353''')
354
355        code('''
356
357// Actions
358''')
359        if self.TBEType != None and self.EntryType != None:
360            for action in self.actions.itervalues():
361                code('/** \\brief ${{action.desc}} */')
362                code('void ${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr);')
363        elif self.TBEType != None:
364            for action in self.actions.itervalues():
365                code('/** \\brief ${{action.desc}} */')
366                code('void ${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, const Address& addr);')
367        elif self.EntryType != None:
368            for action in self.actions.itervalues():
369                code('/** \\brief ${{action.desc}} */')
370                code('void ${{action.ident}}(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr);')
371        else:
372            for action in self.actions.itervalues():
373                code('/** \\brief ${{action.desc}} */')
374                code('void ${{action.ident}}(const Address& addr);')
375
376        # the controller internal variables
377        code('''
378
379// Objects
380''')
381        for var in self.objects:
382            th = var.get("template", "")
383            code('${{var.type.c_ident}}$th* m_${{var.c_ident}}_ptr;')
384
385        code.dedent()
386        code('};')
387        code('#endif // __${ident}_CONTROLLER_H__')
388        code.write(path, '%s.hh' % c_ident)
389
390    def printControllerCC(self, path, includes):
391        '''Output the actions for performing the actions'''
392
393        code = self.symtab.codeFormatter()
394        ident = self.ident
395        c_ident = "%s_Controller" % self.ident
396        has_peer = False
397
398        code('''
399/** \\file $c_ident.cc
400 *
401 * Auto generated C++ code started by $__file__:$__line__
402 * Created by slicc definition of Module "${{self.short}}"
403 */
404
405#include <sys/types.h>
406#include <unistd.h>
407
408#include <cassert>
409#include <sstream>
410#include <string>
411
412#include "base/compiler.hh"
413#include "base/cprintf.hh"
414#include "debug/RubyGenerated.hh"
415#include "debug/RubySlicc.hh"
416#include "mem/protocol/${ident}_Controller.hh"
417#include "mem/protocol/${ident}_Event.hh"
418#include "mem/protocol/${ident}_State.hh"
419#include "mem/protocol/Types.hh"
420#include "mem/ruby/common/Global.hh"
421#include "mem/ruby/system/System.hh"
422''')
423        for include_path in includes:
424            code('#include "${{include_path}}"')
425
426        code('''
427
428using namespace std;
429''')
430
431        # include object classes
432        seen_types = set()
433        for var in self.objects:
434            if var.type.ident not in seen_types and not var.type.isPrimitive:
435                code('#include "mem/protocol/${{var.type.c_ident}}.hh"')
436            seen_types.add(var.type.ident)
437
438        code('''
439$c_ident *
440${c_ident}Params::create()
441{
442    return new $c_ident(this);
443}
444
445int $c_ident::m_num_controllers = 0;
446std::vector<Stats::Vector *>  $c_ident::eventVec;
447std::vector<std::vector<Stats::Vector *> >  $c_ident::transVec;
448
449// for adding information to the protocol debug trace
450stringstream ${ident}_transitionComment;
451
452#ifndef NDEBUG
453#define APPEND_TRANSITION_COMMENT(str) (${ident}_transitionComment << str)
454#else
455#define APPEND_TRANSITION_COMMENT(str) do {} while (0)
456#endif
457
458/** \\brief constructor */
459$c_ident::$c_ident(const Params *p)
460    : AbstractController(p)
461{
462    m_name = "${ident}";
463''')
464        #
465        # max_port_rank is used to size vectors and thus should be one plus the
466        # largest port rank
467        #
468        max_port_rank = self.in_ports[0].pairs["max_port_rank"] + 1
469        code('    m_max_in_port_rank = $max_port_rank;')
470        code.indent()
471
472        #
473        # After initializing the universal machine parameters, initialize the
474        # this machines config parameters.  Also detemine if these configuration
475        # params include a sequencer.  This information will be used later for
476        # contecting the sequencer back to the L1 cache controller.
477        #
478        contains_dma_sequencer = False
479        sequencers = []
480        for param in self.config_parameters:
481            if param.name == "dma_sequencer":
482                contains_dma_sequencer = True
483            elif re.compile("sequencer").search(param.name):
484                sequencers.append(param.name)
485            if param.pointer:
486                code('m_${{param.name}}_ptr = p->${{param.name}};')
487            else:
488                code('m_${{param.name}} = p->${{param.name}};')
489
490        #
491        # For the l1 cache controller, add the special atomic support which
492        # includes passing the sequencer a pointer to the controller.
493        #
494        for seq in sequencers:
495            code('''
496m_${{seq}}_ptr->setController(this);
497    ''')
498
499        #
500        # For the DMA controller, pass the sequencer a pointer to the
501        # controller.
502        #
503        if self.ident == "DMA":
504            if not contains_dma_sequencer:
505                self.error("The DMA controller must include the sequencer " \
506                           "configuration parameter")
507
508            code('''
509m_dma_sequencer_ptr->setController(this);
510''')
511
512        code('m_num_controllers++;')
513        for var in self.objects:
514            if var.ident.find("mandatoryQueue") >= 0:
515                code('''
516m_${{var.c_ident}}_ptr = new ${{var.type.c_ident}}();
517m_${{var.c_ident}}_ptr->setReceiver(this);
518''')
519            else:
520                if "network" in var and "physical_network" in var and \
521                   var["network"] == "To":
522                    has_peer = True
523                    code('''
524m_${{var.c_ident}}_ptr = new ${{var.type.c_ident}}();
525peerQueueMap[${{var["physical_network"]}}] = m_${{var.c_ident}}_ptr;
526m_${{var.c_ident}}_ptr->setSender(this);
527''')
528
529        code('''
530if (p->peer != NULL)
531    connectWithPeer(p->peer);
532
533for (int state = 0; state < ${ident}_State_NUM; state++) {
534    for (int event = 0; event < ${ident}_Event_NUM; event++) {
535        m_possible[state][event] = false;
536        m_counters[state][event] = 0;
537    }
538}
539for (int event = 0; event < ${ident}_Event_NUM; event++) {
540    m_event_counters[event] = 0;
541}
542''')
543        code.dedent()
544        code('''
545}
546
547void
548$c_ident::init()
549{
550    MachineType machine_type = string_to_MachineType("${{var.machine.ident}}");
551    int base = MachineType_base_number(machine_type);
552
553    m_machineID.type = MachineType_${ident};
554    m_machineID.num = m_version;
555
556    // initialize objects
557
558''')
559
560        code.indent()
561        for var in self.objects:
562            vtype = var.type
563            vid = "m_%s_ptr" % var.c_ident
564            if "network" not in var:
565                # Not a network port object
566                if "primitive" in vtype:
567                    code('$vid = new ${{vtype.c_ident}};')
568                    if "default" in var:
569                        code('(*$vid) = ${{var["default"]}};')
570                else:
571                    # Normal Object
572                    if var.ident.find("mandatoryQueue") < 0:
573                        th = var.get("template", "")
574                        expr = "%s  = new %s%s" % (vid, vtype.c_ident, th)
575                        args = ""
576                        if "non_obj" not in vtype and not vtype.isEnumeration:
577                            args = var.get("constructor", "")
578                        code('$expr($args);')
579
580                    code('assert($vid != NULL);')
581
582                    if "default" in var:
583                        code('*$vid = ${{var["default"]}}; // Object default')
584                    elif "default" in vtype:
585                        comment = "Type %s default" % vtype.ident
586                        code('*$vid = ${{vtype["default"]}}; // $comment')
587
588                    # Set ordering
589                    if "ordered" in var:
590                        # A buffer
591                        code('$vid->setOrdering(${{var["ordered"]}});')
592
593                    # Set randomization
594                    if "random" in var:
595                        # A buffer
596                        code('$vid->setRandomization(${{var["random"]}});')
597
598                    # Set Priority
599                    if vtype.isBuffer and "rank" in var:
600                        code('$vid->setPriority(${{var["rank"]}});')
601
602                    # Set sender and receiver for trigger queue
603                    if var.ident.find("triggerQueue") >= 0:
604                        code('$vid->setSender(this);')
605                        code('$vid->setReceiver(this);')
606                    elif vtype.c_ident == "TimerTable":
607                        code('$vid->setClockObj(this);')
608                    elif var.ident.find("optionalQueue") >= 0:
609                        code('$vid->setSender(this);')
610                        code('$vid->setReceiver(this);')
611
612            else:
613                # Network port object
614                network = var["network"]
615                ordered =  var["ordered"]
616
617                if "virtual_network" in var:
618                    vnet = var["virtual_network"]
619                    vnet_type = var["vnet_type"]
620
621                    assert var.machine is not None
622                    code('''
623$vid = m_net_ptr->get${network}NetQueue(m_version + base, $ordered, $vnet, "$vnet_type");
624assert($vid != NULL);
625''')
626
627                    # Set the end
628                    if network == "To":
629                        code('$vid->setSender(this);')
630                    else:
631                        code('$vid->setReceiver(this);')
632
633                # Set ordering
634                if "ordered" in var:
635                    # A buffer
636                    code('$vid->setOrdering(${{var["ordered"]}});')
637
638                # Set randomization
639                if "random" in var:
640                    # A buffer
641                    code('$vid->setRandomization(${{var["random"]}});')
642
643                # Set Priority
644                if "rank" in var:
645                    code('$vid->setPriority(${{var["rank"]}})')
646
647                # Set buffer size
648                if vtype.isBuffer:
649                    code('''
650if (m_buffer_size > 0) {
651    $vid->resize(m_buffer_size);
652}
653''')
654
655                # set description (may be overriden later by port def)
656                code('''
657$vid->setDescription("[Version " + to_string(m_version) + ", ${ident}, name=${{var.c_ident}}]");
658
659''')
660
661            if vtype.isBuffer:
662                if "recycle_latency" in var:
663                    code('$vid->setRecycleLatency( ' \
664                         'Cycles(${{var["recycle_latency"]}}));')
665                else:
666                    code('$vid->setRecycleLatency(m_recycle_latency);')
667
668        # Set the prefetchers
669        code()
670        for prefetcher in self.prefetchers:
671            code('${{prefetcher.code}}.setController(this);')
672
673        code()
674        for port in self.in_ports:
675            # Set the queue consumers
676            code('${{port.code}}.setConsumer(this);')
677            # Set the queue descriptions
678            code('${{port.code}}.setDescription("[Version " + to_string(m_version) + ", $ident, $port]");')
679
680        # Initialize the transition profiling
681        code()
682        for trans in self.transitions:
683            # Figure out if we stall
684            stall = False
685            for action in trans.actions:
686                if action.ident == "z_stall":
687                    stall = True
688
689            # Only possible if it is not a 'z' case
690            if not stall:
691                state = "%s_State_%s" % (self.ident, trans.state.ident)
692                event = "%s_Event_%s" % (self.ident, trans.event.ident)
693                code('possibleTransition($state, $event);')
694
695        code.dedent()
696        code('''
697    AbstractController::init();
698    clearStats();
699}
700''')
701
702        has_mandatory_q = False
703        for port in self.in_ports:
704            if port.code.find("mandatoryQueue_ptr") >= 0:
705                has_mandatory_q = True
706
707        if has_mandatory_q:
708            mq_ident = "m_%s_mandatoryQueue_ptr" % self.ident
709        else:
710            mq_ident = "NULL"
711
712        seq_ident = "NULL"
713        for param in self.config_parameters:
714            if param.name == "sequencer":
715                assert(param.pointer)
716                seq_ident = "m_%s_ptr" % param.name
717
718        code('''
719
720void
721$c_ident::regStats()
722{
723    if (m_version == 0) {
724        for (${ident}_Event event = ${ident}_Event_FIRST;
725             event < ${ident}_Event_NUM; ++event) {
726            Stats::Vector *t = new Stats::Vector();
727            t->init(m_num_controllers);
728            t->name(name() + "." + ${ident}_Event_to_string(event));
729            t->flags(Stats::pdf | Stats::total | Stats::oneline |
730                     Stats::nozero);
731
732            eventVec.push_back(t);
733        }
734
735        for (${ident}_State state = ${ident}_State_FIRST;
736             state < ${ident}_State_NUM; ++state) {
737
738            transVec.push_back(std::vector<Stats::Vector *>());
739
740            for (${ident}_Event event = ${ident}_Event_FIRST;
741                 event < ${ident}_Event_NUM; ++event) {
742
743                Stats::Vector *t = new Stats::Vector();
744                t->init(m_num_controllers);
745                t->name(name() + "." + ${ident}_State_to_string(state) +
746                        "." + ${ident}_Event_to_string(event));
747
748                t->flags(Stats::pdf | Stats::total | Stats::oneline |
749                         Stats::nozero);
750                transVec[state].push_back(t);
751            }
752        }
753    }
754}
755
756void
757$c_ident::collateStats()
758{
759    for (${ident}_Event event = ${ident}_Event_FIRST;
760         event < ${ident}_Event_NUM; ++event) {
761        for (unsigned int i = 0; i < m_num_controllers; ++i) {
762            std::map<uint32_t, AbstractController *>::iterator it =
763                                g_abs_controls[MachineType_${ident}].find(i);
764            assert(it != g_abs_controls[MachineType_${ident}].end());
765            (*eventVec[event])[i] =
766                (($c_ident *)(*it).second)->getEventCount(event);
767        }
768    }
769
770    for (${ident}_State state = ${ident}_State_FIRST;
771         state < ${ident}_State_NUM; ++state) {
772
773        for (${ident}_Event event = ${ident}_Event_FIRST;
774             event < ${ident}_Event_NUM; ++event) {
775
776            for (unsigned int i = 0; i < m_num_controllers; ++i) {
777                std::map<uint32_t, AbstractController *>::iterator it =
778                                g_abs_controls[MachineType_${ident}].find(i);
779                assert(it != g_abs_controls[MachineType_${ident}].end());
780                (*transVec[state][event])[i] =
781                    (($c_ident *)(*it).second)->getTransitionCount(state, event);
782            }
783        }
784    }
785}
786
787void
788$c_ident::countTransition(${ident}_State state, ${ident}_Event event)
789{
790    assert(m_possible[state][event]);
791    m_counters[state][event]++;
792    m_event_counters[event]++;
793}
794void
795$c_ident::possibleTransition(${ident}_State state,
796                             ${ident}_Event event)
797{
798    m_possible[state][event] = true;
799}
800
801uint64
802$c_ident::getEventCount(${ident}_Event event)
803{
804    return m_event_counters[event];
805}
806
807bool
808$c_ident::isPossible(${ident}_State state, ${ident}_Event event)
809{
810    return m_possible[state][event];
811}
812
813uint64
814$c_ident::getTransitionCount(${ident}_State state,
815                             ${ident}_Event event)
816{
817    return m_counters[state][event];
818}
819
820int
821$c_ident::getNumControllers()
822{
823    return m_num_controllers;
824}
825
826MessageBuffer*
827$c_ident::getMandatoryQueue() const
828{
829    return $mq_ident;
830}
831
832Sequencer*
833$c_ident::getSequencer() const
834{
835    return $seq_ident;
836}
837
838const string
839$c_ident::toString() const
840{
841    return "$c_ident";
842}
843
844void
845$c_ident::print(ostream& out) const
846{
847    out << "[$c_ident " << m_version << "]";
848}
849
850void
851$c_ident::printStats(ostream& out) const
852{
853''')
854        #
855        # Cache and Memory Controllers have specific profilers associated with
856        # them.  Print out these stats before dumping state transition stats.
857        #
858        for param in self.config_parameters:
859            if param.type_ast.type.ident == "DirectoryMemory":
860                assert(param.pointer)
861                code('    m_${{param.ident}}_ptr->printStats(out);')
862
863        code('''
864}
865
866void $c_ident::clearStats()
867{
868    for (int state = 0; state < ${ident}_State_NUM; state++) {
869        for (int event = 0; event < ${ident}_Event_NUM; event++) {
870            m_counters[state][event] = 0;
871        }
872    }
873
874    for (int event = 0; event < ${ident}_Event_NUM; event++) {
875        m_event_counters[event] = 0;
876    }
877
878    AbstractController::clearStats();
879}
880''')
881
882        if self.EntryType != None:
883            code('''
884
885// Set and Reset for cache_entry variable
886void
887$c_ident::set_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, AbstractCacheEntry* m_new_cache_entry)
888{
889  m_cache_entry_ptr = (${{self.EntryType.c_ident}}*)m_new_cache_entry;
890}
891
892void
893$c_ident::unset_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr)
894{
895  m_cache_entry_ptr = 0;
896}
897''')
898
899        if self.TBEType != None:
900            code('''
901
902// Set and Reset for tbe variable
903void
904$c_ident::set_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.TBEType.c_ident}}* m_new_tbe)
905{
906  m_tbe_ptr = m_new_tbe;
907}
908
909void
910$c_ident::unset_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr)
911{
912  m_tbe_ptr = NULL;
913}
914''')
915
916        code('''
917
918void
919$c_ident::recordCacheTrace(int cntrl, CacheRecorder* tr)
920{
921''')
922        #
923        # Record cache contents for all associated caches.
924        #
925        code.indent()
926        for param in self.config_parameters:
927            if param.type_ast.type.ident == "CacheMemory":
928                assert(param.pointer)
929                code('m_${{param.ident}}_ptr->recordCacheContents(cntrl, tr);')
930
931        code.dedent()
932        code('''
933}
934
935// Actions
936''')
937        if self.TBEType != None and self.EntryType != None:
938            for action in self.actions.itervalues():
939                if "c_code" not in action:
940                 continue
941
942                code('''
943/** \\brief ${{action.desc}} */
944void
945$c_ident::${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr)
946{
947    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
948    ${{action["c_code"]}}
949}
950
951''')
952        elif self.TBEType != None:
953            for action in self.actions.itervalues():
954                if "c_code" not in action:
955                 continue
956
957                code('''
958/** \\brief ${{action.desc}} */
959void
960$c_ident::${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, const Address& addr)
961{
962    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
963    ${{action["c_code"]}}
964}
965
966''')
967        elif self.EntryType != None:
968            for action in self.actions.itervalues():
969                if "c_code" not in action:
970                 continue
971
972                code('''
973/** \\brief ${{action.desc}} */
974void
975$c_ident::${{action.ident}}(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr)
976{
977    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
978    ${{action["c_code"]}}
979}
980
981''')
982        else:
983            for action in self.actions.itervalues():
984                if "c_code" not in action:
985                 continue
986
987                code('''
988/** \\brief ${{action.desc}} */
989void
990$c_ident::${{action.ident}}(const Address& addr)
991{
992    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
993    ${{action["c_code"]}}
994}
995
996''')
997        for func in self.functions:
998            code(func.generateCode())
999
1000        # Function for functional reads from messages buffered in the controller
1001        code('''
1002bool
1003$c_ident::functionalReadBuffers(PacketPtr& pkt)
1004{
1005''')
1006        for var in self.objects:
1007            vtype = var.type
1008            if vtype.isBuffer:
1009                vid = "m_%s_ptr" % var.c_ident
1010                code('if ($vid->functionalRead(pkt)) { return true; }')
1011        code('''
1012                return false;
1013}
1014''')
1015
1016        # Function for functional writes to messages buffered in the controller
1017        code('''
1018uint32_t
1019$c_ident::functionalWriteBuffers(PacketPtr& pkt)
1020{
1021    uint32_t num_functional_writes = 0;
1022''')
1023        for var in self.objects:
1024            vtype = var.type
1025            if vtype.isBuffer:
1026                vid = "m_%s_ptr" % var.c_ident
1027                code('num_functional_writes += $vid->functionalWrite(pkt);')
1028        code('''
1029    return num_functional_writes;
1030}
1031''')
1032
1033        # Check if this controller has a peer, if yes then write the
1034        # function for connecting to the peer.
1035        if has_peer:
1036            code('''
1037
1038void
1039$c_ident::getQueuesFromPeer(AbstractController *peer)
1040{
1041''')
1042            for var in self.objects:
1043                if "network" in var and "physical_network" in var and \
1044                   var["network"] == "From":
1045                    code('''
1046m_${{var.c_ident}}_ptr = peer->getPeerQueue(${{var["physical_network"]}});
1047assert(m_${{var.c_ident}}_ptr != NULL);
1048m_${{var.c_ident}}_ptr->setReceiver(this);
1049
1050''')
1051            code('}')
1052
1053        code.write(path, "%s.cc" % c_ident)
1054
1055    def printCWakeup(self, path, includes):
1056        '''Output the wakeup loop for the events'''
1057
1058        code = self.symtab.codeFormatter()
1059        ident = self.ident
1060
1061        outputRequest_types = True
1062        if len(self.request_types) == 0:
1063            outputRequest_types = False
1064
1065        code('''
1066// Auto generated C++ code started by $__file__:$__line__
1067// ${ident}: ${{self.short}}
1068
1069#include <sys/types.h>
1070#include <unistd.h>
1071
1072#include <cassert>
1073
1074#include "base/misc.hh"
1075#include "debug/RubySlicc.hh"
1076#include "mem/protocol/${ident}_Controller.hh"
1077#include "mem/protocol/${ident}_Event.hh"
1078#include "mem/protocol/${ident}_State.hh"
1079''')
1080
1081        if outputRequest_types:
1082            code('''#include "mem/protocol/${ident}_RequestType.hh"''')
1083
1084        code('''
1085#include "mem/protocol/Types.hh"
1086#include "mem/ruby/common/Global.hh"
1087#include "mem/ruby/system/System.hh"
1088''')
1089
1090
1091        for include_path in includes:
1092            code('#include "${{include_path}}"')
1093
1094        code('''
1095
1096using namespace std;
1097
1098void
1099${ident}_Controller::wakeup()
1100{
1101    int counter = 0;
1102    while (true) {
1103        // Some cases will put us into an infinite loop without this limit
1104        assert(counter <= m_transitions_per_cycle);
1105        if (counter == m_transitions_per_cycle) {
1106            // Count how often we are fully utilized
1107            m_fully_busy_cycles++;
1108
1109            // Wakeup in another cycle and try again
1110            scheduleEvent(Cycles(1));
1111            break;
1112        }
1113''')
1114
1115        code.indent()
1116        code.indent()
1117
1118        # InPorts
1119        #
1120        for port in self.in_ports:
1121            code.indent()
1122            code('// ${ident}InPort $port')
1123            if port.pairs.has_key("rank"):
1124                code('m_cur_in_port_rank = ${{port.pairs["rank"]}};')
1125            else:
1126                code('m_cur_in_port_rank = 0;')
1127            code('${{port["c_code_in_port"]}}')
1128            code.dedent()
1129
1130            code('')
1131
1132        code.dedent()
1133        code.dedent()
1134        code('''
1135        break;  // If we got this far, we have nothing left todo
1136    }
1137}
1138''')
1139
1140        code.write(path, "%s_Wakeup.cc" % self.ident)
1141
1142    def printCSwitch(self, path):
1143        '''Output switch statement for transition table'''
1144
1145        code = self.symtab.codeFormatter()
1146        ident = self.ident
1147
1148        code('''
1149// Auto generated C++ code started by $__file__:$__line__
1150// ${ident}: ${{self.short}}
1151
1152#include <cassert>
1153
1154#include "base/misc.hh"
1155#include "base/trace.hh"
1156#include "debug/ProtocolTrace.hh"
1157#include "debug/RubyGenerated.hh"
1158#include "mem/protocol/${ident}_Controller.hh"
1159#include "mem/protocol/${ident}_Event.hh"
1160#include "mem/protocol/${ident}_State.hh"
1161#include "mem/protocol/Types.hh"
1162#include "mem/ruby/common/Global.hh"
1163#include "mem/ruby/system/System.hh"
1164
1165#define HASH_FUN(state, event)  ((int(state)*${ident}_Event_NUM)+int(event))
1166
1167#define GET_TRANSITION_COMMENT() (${ident}_transitionComment.str())
1168#define CLEAR_TRANSITION_COMMENT() (${ident}_transitionComment.str(""))
1169
1170TransitionResult
1171${ident}_Controller::doTransition(${ident}_Event event,
1172''')
1173        if self.EntryType != None:
1174            code('''
1175                                  ${{self.EntryType.c_ident}}* m_cache_entry_ptr,
1176''')
1177        if self.TBEType != None:
1178            code('''
1179                                  ${{self.TBEType.c_ident}}* m_tbe_ptr,
1180''')
1181        code('''
1182                                  const Address &addr)
1183{
1184''')
1185        if self.TBEType != None and self.EntryType != None:
1186            code('${ident}_State state = getState(m_tbe_ptr, m_cache_entry_ptr, addr);')
1187        elif self.TBEType != None:
1188            code('${ident}_State state = getState(m_tbe_ptr, addr);')
1189        elif self.EntryType != None:
1190            code('${ident}_State state = getState(m_cache_entry_ptr, addr);')
1191        else:
1192            code('${ident}_State state = getState(addr);')
1193
1194        code('''
1195    ${ident}_State next_state = state;
1196
1197    DPRINTF(RubyGenerated, "%s, Time: %lld, state: %s, event: %s, addr: %s\\n",
1198            *this, curCycle(), ${ident}_State_to_string(state),
1199            ${ident}_Event_to_string(event), addr);
1200
1201    TransitionResult result =
1202''')
1203        if self.TBEType != None and self.EntryType != None:
1204            code('doTransitionWorker(event, state, next_state, m_tbe_ptr, m_cache_entry_ptr, addr);')
1205        elif self.TBEType != None:
1206            code('doTransitionWorker(event, state, next_state, m_tbe_ptr, addr);')
1207        elif self.EntryType != None:
1208            code('doTransitionWorker(event, state, next_state, m_cache_entry_ptr, addr);')
1209        else:
1210            code('doTransitionWorker(event, state, next_state, addr);')
1211
1212        code('''
1213    if (result == TransitionResult_Valid) {
1214        DPRINTF(RubyGenerated, "next_state: %s\\n",
1215                ${ident}_State_to_string(next_state));
1216        countTransition(state, event);
1217        DPRINTFR(ProtocolTrace, "%15d %3s %10s%20s %6s>%-6s %s %s\\n",
1218                 curTick(), m_version, "${ident}",
1219                 ${ident}_Event_to_string(event),
1220                 ${ident}_State_to_string(state),
1221                 ${ident}_State_to_string(next_state),
1222                 addr, GET_TRANSITION_COMMENT());
1223
1224        CLEAR_TRANSITION_COMMENT();
1225''')
1226        if self.TBEType != None and self.EntryType != None:
1227            code('setState(m_tbe_ptr, m_cache_entry_ptr, addr, next_state);')
1228            code('setAccessPermission(m_cache_entry_ptr, addr, next_state);')
1229        elif self.TBEType != None:
1230            code('setState(m_tbe_ptr, addr, next_state);')
1231            code('setAccessPermission(addr, next_state);')
1232        elif self.EntryType != None:
1233            code('setState(m_cache_entry_ptr, addr, next_state);')
1234            code('setAccessPermission(m_cache_entry_ptr, addr, next_state);')
1235        else:
1236            code('setState(addr, next_state);')
1237            code('setAccessPermission(addr, next_state);')
1238
1239        code('''
1240    } else if (result == TransitionResult_ResourceStall) {
1241        DPRINTFR(ProtocolTrace, "%15s %3s %10s%20s %6s>%-6s %s %s\\n",
1242                 curTick(), m_version, "${ident}",
1243                 ${ident}_Event_to_string(event),
1244                 ${ident}_State_to_string(state),
1245                 ${ident}_State_to_string(next_state),
1246                 addr, "Resource Stall");
1247    } else if (result == TransitionResult_ProtocolStall) {
1248        DPRINTF(RubyGenerated, "stalling\\n");
1249        DPRINTFR(ProtocolTrace, "%15s %3s %10s%20s %6s>%-6s %s %s\\n",
1250                 curTick(), m_version, "${ident}",
1251                 ${ident}_Event_to_string(event),
1252                 ${ident}_State_to_string(state),
1253                 ${ident}_State_to_string(next_state),
1254                 addr, "Protocol Stall");
1255    }
1256
1257    return result;
1258}
1259
1260TransitionResult
1261${ident}_Controller::doTransitionWorker(${ident}_Event event,
1262                                        ${ident}_State state,
1263                                        ${ident}_State& next_state,
1264''')
1265
1266        if self.TBEType != None:
1267            code('''
1268                                        ${{self.TBEType.c_ident}}*& m_tbe_ptr,
1269''')
1270        if self.EntryType != None:
1271                  code('''
1272                                        ${{self.EntryType.c_ident}}*& m_cache_entry_ptr,
1273''')
1274        code('''
1275                                        const Address& addr)
1276{
1277    switch(HASH_FUN(state, event)) {
1278''')
1279
1280        # This map will allow suppress generating duplicate code
1281        cases = orderdict()
1282
1283        for trans in self.transitions:
1284            case_string = "%s_State_%s, %s_Event_%s" % \
1285                (self.ident, trans.state.ident, self.ident, trans.event.ident)
1286
1287            case = self.symtab.codeFormatter()
1288            # Only set next_state if it changes
1289            if trans.state != trans.nextState:
1290                ns_ident = trans.nextState.ident
1291                case('next_state = ${ident}_State_${ns_ident};')
1292
1293            actions = trans.actions
1294            request_types = trans.request_types
1295
1296            # Check for resources
1297            case_sorter = []
1298            res = trans.resources
1299            for key,val in res.iteritems():
1300                if key.type.ident != "DNUCAStopTable":
1301                    val = '''
1302if (!%s.areNSlotsAvailable(%s))
1303    return TransitionResult_ResourceStall;
1304''' % (key.code, val)
1305                case_sorter.append(val)
1306
1307            # Check all of the request_types for resource constraints
1308            for request_type in request_types:
1309                val = '''
1310if (!checkResourceAvailable(%s_RequestType_%s, addr)) {
1311    return TransitionResult_ResourceStall;
1312}
1313''' % (self.ident, request_type.ident)
1314                case_sorter.append(val)
1315
1316            # Emit the code sequences in a sorted order.  This makes the
1317            # output deterministic (without this the output order can vary
1318            # since Map's keys() on a vector of pointers is not deterministic
1319            for c in sorted(case_sorter):
1320                case("$c")
1321
1322            # Record access types for this transition
1323            for request_type in request_types:
1324                case('recordRequestType(${ident}_RequestType_${{request_type.ident}}, addr);')
1325
1326            # Figure out if we stall
1327            stall = False
1328            for action in actions:
1329                if action.ident == "z_stall":
1330                    stall = True
1331                    break
1332
1333            if stall:
1334                case('return TransitionResult_ProtocolStall;')
1335            else:
1336                if self.TBEType != None and self.EntryType != None:
1337                    for action in actions:
1338                        case('${{action.ident}}(m_tbe_ptr, m_cache_entry_ptr, addr);')
1339                elif self.TBEType != None:
1340                    for action in actions:
1341                        case('${{action.ident}}(m_tbe_ptr, addr);')
1342                elif self.EntryType != None:
1343                    for action in actions:
1344                        case('${{action.ident}}(m_cache_entry_ptr, addr);')
1345                else:
1346                    for action in actions:
1347                        case('${{action.ident}}(addr);')
1348                case('return TransitionResult_Valid;')
1349
1350            case = str(case)
1351
1352            # Look to see if this transition code is unique.
1353            if case not in cases:
1354                cases[case] = []
1355
1356            cases[case].append(case_string)
1357
1358        # Walk through all of the unique code blocks and spit out the
1359        # corresponding case statement elements
1360        for case,transitions in cases.iteritems():
1361            # Iterative over all the multiple transitions that share
1362            # the same code
1363            for trans in transitions:
1364                code('  case HASH_FUN($trans):')
1365            code('    $case')
1366
1367        code('''
1368      default:
1369        fatal("Invalid transition\\n"
1370              "%s time: %d addr: %s event: %s state: %s\\n",
1371              name(), curCycle(), addr, event, state);
1372    }
1373    return TransitionResult_Valid;
1374}
1375''')
1376        code.write(path, "%s_Transitions.cc" % self.ident)
1377
1378
1379    # **************************
1380    # ******* HTML Files *******
1381    # **************************
1382    def frameRef(self, click_href, click_target, over_href, over_num, text):
1383        code = self.symtab.codeFormatter(fix_newlines=False)
1384        code("""<A href=\"$click_href\" target=\"$click_target\" onmouseover=\"
1385    if (parent.frames[$over_num].location != parent.location + '$over_href') {
1386        parent.frames[$over_num].location='$over_href'
1387    }\">
1388    ${{html.formatShorthand(text)}}
1389    </A>""")
1390        return str(code)
1391
1392    def writeHTMLFiles(self, path):
1393        # Create table with no row hilighted
1394        self.printHTMLTransitions(path, None)
1395
1396        # Generate transition tables
1397        for state in self.states.itervalues():
1398            self.printHTMLTransitions(path, state)
1399
1400        # Generate action descriptions
1401        for action in self.actions.itervalues():
1402            name = "%s_action_%s.html" % (self.ident, action.ident)
1403            code = html.createSymbol(action, "Action")
1404            code.write(path, name)
1405
1406        # Generate state descriptions
1407        for state in self.states.itervalues():
1408            name = "%s_State_%s.html" % (self.ident, state.ident)
1409            code = html.createSymbol(state, "State")
1410            code.write(path, name)
1411
1412        # Generate event descriptions
1413        for event in self.events.itervalues():
1414            name = "%s_Event_%s.html" % (self.ident, event.ident)
1415            code = html.createSymbol(event, "Event")
1416            code.write(path, name)
1417
1418    def printHTMLTransitions(self, path, active_state):
1419        code = self.symtab.codeFormatter()
1420
1421        code('''
1422<HTML>
1423<BODY link="blue" vlink="blue">
1424
1425<H1 align="center">${{html.formatShorthand(self.short)}}:
1426''')
1427        code.indent()
1428        for i,machine in enumerate(self.symtab.getAllType(StateMachine)):
1429            mid = machine.ident
1430            if i != 0:
1431                extra = " - "
1432            else:
1433                extra = ""
1434            if machine == self:
1435                code('$extra$mid')
1436            else:
1437                code('$extra<A target="Table" href="${mid}_table.html">$mid</A>')
1438        code.dedent()
1439
1440        code("""
1441</H1>
1442
1443<TABLE border=1>
1444<TR>
1445  <TH> </TH>
1446""")
1447
1448        for event in self.events.itervalues():
1449            href = "%s_Event_%s.html" % (self.ident, event.ident)
1450            ref = self.frameRef(href, "Status", href, "1", event.short)
1451            code('<TH bgcolor=white>$ref</TH>')
1452
1453        code('</TR>')
1454        # -- Body of table
1455        for state in self.states.itervalues():
1456            # -- Each row
1457            if state == active_state:
1458                color = "yellow"
1459            else:
1460                color = "white"
1461
1462            click = "%s_table_%s.html" % (self.ident, state.ident)
1463            over = "%s_State_%s.html" % (self.ident, state.ident)
1464            text = html.formatShorthand(state.short)
1465            ref = self.frameRef(click, "Table", over, "1", state.short)
1466            code('''
1467<TR>
1468  <TH bgcolor=$color>$ref</TH>
1469''')
1470
1471            # -- One column for each event
1472            for event in self.events.itervalues():
1473                trans = self.table.get((state,event), None)
1474                if trans is None:
1475                    # This is the no transition case
1476                    if state == active_state:
1477                        color = "#C0C000"
1478                    else:
1479                        color = "lightgrey"
1480
1481                    code('<TD bgcolor=$color>&nbsp;</TD>')
1482                    continue
1483
1484                next = trans.nextState
1485                stall_action = False
1486
1487                # -- Get the actions
1488                for action in trans.actions:
1489                    if action.ident == "z_stall" or \
1490                       action.ident == "zz_recycleMandatoryQueue":
1491                        stall_action = True
1492
1493                # -- Print out "actions/next-state"
1494                if stall_action:
1495                    if state == active_state:
1496                        color = "#C0C000"
1497                    else:
1498                        color = "lightgrey"
1499
1500                elif active_state and next.ident == active_state.ident:
1501                    color = "aqua"
1502                elif state == active_state:
1503                    color = "yellow"
1504                else:
1505                    color = "white"
1506
1507                code('<TD bgcolor=$color>')
1508                for action in trans.actions:
1509                    href = "%s_action_%s.html" % (self.ident, action.ident)
1510                    ref = self.frameRef(href, "Status", href, "1",
1511                                        action.short)
1512                    code('  $ref')
1513                if next != state:
1514                    if trans.actions:
1515                        code('/')
1516                    click = "%s_table_%s.html" % (self.ident, next.ident)
1517                    over = "%s_State_%s.html" % (self.ident, next.ident)
1518                    ref = self.frameRef(click, "Table", over, "1", next.short)
1519                    code("$ref")
1520                code("</TD>")
1521
1522            # -- Each row
1523            if state == active_state:
1524                color = "yellow"
1525            else:
1526                color = "white"
1527
1528            click = "%s_table_%s.html" % (self.ident, state.ident)
1529            over = "%s_State_%s.html" % (self.ident, state.ident)
1530            ref = self.frameRef(click, "Table", over, "1", state.short)
1531            code('''
1532  <TH bgcolor=$color>$ref</TH>
1533</TR>
1534''')
1535        code('''
1536<!- Column footer->
1537<TR>
1538  <TH> </TH>
1539''')
1540
1541        for event in self.events.itervalues():
1542            href = "%s_Event_%s.html" % (self.ident, event.ident)
1543            ref = self.frameRef(href, "Status", href, "1", event.short)
1544            code('<TH bgcolor=white>$ref</TH>')
1545        code('''
1546</TR>
1547</TABLE>
1548</BODY></HTML>
1549''')
1550
1551
1552        if active_state:
1553            name = "%s_table_%s.html" % (self.ident, active_state.ident)
1554        else:
1555            name = "%s_table.html" % self.ident
1556        code.write(path, name)
1557
1558__all__ = [ "StateMachine" ]
1559