StateMachine.py revision 9745
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#define APPEND_TRANSITION_COMMENT(str) (${ident}_transitionComment << str)
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 int &
839$c_ident::getVersion() const
840{
841    return m_version;
842}
843
844const string
845$c_ident::toString() const
846{
847    return "$c_ident";
848}
849
850const string
851$c_ident::getName() const
852{
853    return m_name;
854}
855
856void
857$c_ident::blockOnQueue(Address addr, MessageBuffer* port)
858{
859    m_is_blocking = true;
860    m_block_map[addr] = port;
861}
862
863void
864$c_ident::unblock(Address addr)
865{
866    m_block_map.erase(addr);
867    if (m_block_map.size() == 0) {
868       m_is_blocking = false;
869    }
870}
871
872void
873$c_ident::print(ostream& out) const
874{
875    out << "[$c_ident " << m_version << "]";
876}
877
878void
879$c_ident::printStats(ostream& out) const
880{
881''')
882        #
883        # Cache and Memory Controllers have specific profilers associated with
884        # them.  Print out these stats before dumping state transition stats.
885        #
886        for param in self.config_parameters:
887            if param.type_ast.type.ident == "DirectoryMemory":
888                assert(param.pointer)
889                code('    m_${{param.ident}}_ptr->printStats(out);')
890
891        code('''
892}
893
894void $c_ident::clearStats()
895{
896    for (int state = 0; state < ${ident}_State_NUM; state++) {
897        for (int event = 0; event < ${ident}_Event_NUM; event++) {
898            m_counters[state][event] = 0;
899        }
900    }
901
902    for (int event = 0; event < ${ident}_Event_NUM; event++) {
903        m_event_counters[event] = 0;
904    }
905
906    AbstractController::clearStats();
907}
908''')
909
910        if self.EntryType != None:
911            code('''
912
913// Set and Reset for cache_entry variable
914void
915$c_ident::set_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, AbstractCacheEntry* m_new_cache_entry)
916{
917  m_cache_entry_ptr = (${{self.EntryType.c_ident}}*)m_new_cache_entry;
918}
919
920void
921$c_ident::unset_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr)
922{
923  m_cache_entry_ptr = 0;
924}
925''')
926
927        if self.TBEType != None:
928            code('''
929
930// Set and Reset for tbe variable
931void
932$c_ident::set_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.TBEType.c_ident}}* m_new_tbe)
933{
934  m_tbe_ptr = m_new_tbe;
935}
936
937void
938$c_ident::unset_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr)
939{
940  m_tbe_ptr = NULL;
941}
942''')
943
944        code('''
945
946void
947$c_ident::recordCacheTrace(int cntrl, CacheRecorder* tr)
948{
949''')
950        #
951        # Record cache contents for all associated caches.
952        #
953        code.indent()
954        for param in self.config_parameters:
955            if param.type_ast.type.ident == "CacheMemory":
956                assert(param.pointer)
957                code('m_${{param.ident}}_ptr->recordCacheContents(cntrl, tr);')
958
959        code.dedent()
960        code('''
961}
962
963// Actions
964''')
965        if self.TBEType != None and self.EntryType != None:
966            for action in self.actions.itervalues():
967                if "c_code" not in action:
968                 continue
969
970                code('''
971/** \\brief ${{action.desc}} */
972void
973$c_ident::${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr)
974{
975    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
976    ${{action["c_code"]}}
977}
978
979''')
980        elif self.TBEType != None:
981            for action in self.actions.itervalues():
982                if "c_code" not in action:
983                 continue
984
985                code('''
986/** \\brief ${{action.desc}} */
987void
988$c_ident::${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, const Address& addr)
989{
990    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
991    ${{action["c_code"]}}
992}
993
994''')
995        elif self.EntryType != None:
996            for action in self.actions.itervalues():
997                if "c_code" not in action:
998                 continue
999
1000                code('''
1001/** \\brief ${{action.desc}} */
1002void
1003$c_ident::${{action.ident}}(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr)
1004{
1005    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
1006    ${{action["c_code"]}}
1007}
1008
1009''')
1010        else:
1011            for action in self.actions.itervalues():
1012                if "c_code" not in action:
1013                 continue
1014
1015                code('''
1016/** \\brief ${{action.desc}} */
1017void
1018$c_ident::${{action.ident}}(const Address& addr)
1019{
1020    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
1021    ${{action["c_code"]}}
1022}
1023
1024''')
1025        for func in self.functions:
1026            code(func.generateCode())
1027
1028        # Function for functional reads from messages buffered in the controller
1029        code('''
1030bool
1031$c_ident::functionalReadBuffers(PacketPtr& pkt)
1032{
1033''')
1034        for var in self.objects:
1035            vtype = var.type
1036            if vtype.isBuffer:
1037                vid = "m_%s_ptr" % var.c_ident
1038                code('if ($vid->functionalRead(pkt)) { return true; }')
1039        code('''
1040                return false;
1041}
1042''')
1043
1044        # Function for functional writes to messages buffered in the controller
1045        code('''
1046uint32_t
1047$c_ident::functionalWriteBuffers(PacketPtr& pkt)
1048{
1049    uint32_t num_functional_writes = 0;
1050''')
1051        for var in self.objects:
1052            vtype = var.type
1053            if vtype.isBuffer:
1054                vid = "m_%s_ptr" % var.c_ident
1055                code('num_functional_writes += $vid->functionalWrite(pkt);')
1056        code('''
1057    return num_functional_writes;
1058}
1059''')
1060
1061        # Check if this controller has a peer, if yes then write the
1062        # function for connecting to the peer.
1063        if has_peer:
1064            code('''
1065
1066void
1067$c_ident::getQueuesFromPeer(AbstractController *peer)
1068{
1069''')
1070            for var in self.objects:
1071                if "network" in var and "physical_network" in var and \
1072                   var["network"] == "From":
1073                    code('''
1074m_${{var.c_ident}}_ptr = peer->getPeerQueue(${{var["physical_network"]}});
1075assert(m_${{var.c_ident}}_ptr != NULL);
1076m_${{var.c_ident}}_ptr->setReceiver(this);
1077
1078''')
1079            code('}')
1080
1081        code.write(path, "%s.cc" % c_ident)
1082
1083    def printCWakeup(self, path, includes):
1084        '''Output the wakeup loop for the events'''
1085
1086        code = self.symtab.codeFormatter()
1087        ident = self.ident
1088
1089        outputRequest_types = True
1090        if len(self.request_types) == 0:
1091            outputRequest_types = False
1092
1093        code('''
1094// Auto generated C++ code started by $__file__:$__line__
1095// ${ident}: ${{self.short}}
1096
1097#include <sys/types.h>
1098#include <unistd.h>
1099
1100#include <cassert>
1101
1102#include "base/misc.hh"
1103#include "debug/RubySlicc.hh"
1104#include "mem/protocol/${ident}_Controller.hh"
1105#include "mem/protocol/${ident}_Event.hh"
1106#include "mem/protocol/${ident}_State.hh"
1107''')
1108
1109        if outputRequest_types:
1110            code('''#include "mem/protocol/${ident}_RequestType.hh"''')
1111
1112        code('''
1113#include "mem/protocol/Types.hh"
1114#include "mem/ruby/common/Global.hh"
1115#include "mem/ruby/system/System.hh"
1116''')
1117
1118
1119        for include_path in includes:
1120            code('#include "${{include_path}}"')
1121
1122        code('''
1123
1124using namespace std;
1125
1126void
1127${ident}_Controller::wakeup()
1128{
1129    int counter = 0;
1130    while (true) {
1131        // Some cases will put us into an infinite loop without this limit
1132        assert(counter <= m_transitions_per_cycle);
1133        if (counter == m_transitions_per_cycle) {
1134            // Count how often we are fully utilized
1135            m_fully_busy_cycles++;
1136
1137            // Wakeup in another cycle and try again
1138            scheduleEvent(Cycles(1));
1139            break;
1140        }
1141''')
1142
1143        code.indent()
1144        code.indent()
1145
1146        # InPorts
1147        #
1148        for port in self.in_ports:
1149            code.indent()
1150            code('// ${ident}InPort $port')
1151            if port.pairs.has_key("rank"):
1152                code('m_cur_in_port_rank = ${{port.pairs["rank"]}};')
1153            else:
1154                code('m_cur_in_port_rank = 0;')
1155            code('${{port["c_code_in_port"]}}')
1156            code.dedent()
1157
1158            code('')
1159
1160        code.dedent()
1161        code.dedent()
1162        code('''
1163        break;  // If we got this far, we have nothing left todo
1164    }
1165}
1166''')
1167
1168        code.write(path, "%s_Wakeup.cc" % self.ident)
1169
1170    def printCSwitch(self, path):
1171        '''Output switch statement for transition table'''
1172
1173        code = self.symtab.codeFormatter()
1174        ident = self.ident
1175
1176        code('''
1177// Auto generated C++ code started by $__file__:$__line__
1178// ${ident}: ${{self.short}}
1179
1180#include <cassert>
1181
1182#include "base/misc.hh"
1183#include "base/trace.hh"
1184#include "debug/ProtocolTrace.hh"
1185#include "debug/RubyGenerated.hh"
1186#include "mem/protocol/${ident}_Controller.hh"
1187#include "mem/protocol/${ident}_Event.hh"
1188#include "mem/protocol/${ident}_State.hh"
1189#include "mem/protocol/Types.hh"
1190#include "mem/ruby/common/Global.hh"
1191#include "mem/ruby/system/System.hh"
1192
1193#define HASH_FUN(state, event)  ((int(state)*${ident}_Event_NUM)+int(event))
1194
1195#define GET_TRANSITION_COMMENT() (${ident}_transitionComment.str())
1196#define CLEAR_TRANSITION_COMMENT() (${ident}_transitionComment.str(""))
1197
1198TransitionResult
1199${ident}_Controller::doTransition(${ident}_Event event,
1200''')
1201        if self.EntryType != None:
1202            code('''
1203                                  ${{self.EntryType.c_ident}}* m_cache_entry_ptr,
1204''')
1205        if self.TBEType != None:
1206            code('''
1207                                  ${{self.TBEType.c_ident}}* m_tbe_ptr,
1208''')
1209        code('''
1210                                  const Address &addr)
1211{
1212''')
1213        if self.TBEType != None and self.EntryType != None:
1214            code('${ident}_State state = getState(m_tbe_ptr, m_cache_entry_ptr, addr);')
1215        elif self.TBEType != None:
1216            code('${ident}_State state = getState(m_tbe_ptr, addr);')
1217        elif self.EntryType != None:
1218            code('${ident}_State state = getState(m_cache_entry_ptr, addr);')
1219        else:
1220            code('${ident}_State state = getState(addr);')
1221
1222        code('''
1223    ${ident}_State next_state = state;
1224
1225    DPRINTF(RubyGenerated, "%s, Time: %lld, state: %s, event: %s, addr: %s\\n",
1226            *this, curCycle(), ${ident}_State_to_string(state),
1227            ${ident}_Event_to_string(event), addr);
1228
1229    TransitionResult result =
1230''')
1231        if self.TBEType != None and self.EntryType != None:
1232            code('doTransitionWorker(event, state, next_state, m_tbe_ptr, m_cache_entry_ptr, addr);')
1233        elif self.TBEType != None:
1234            code('doTransitionWorker(event, state, next_state, m_tbe_ptr, addr);')
1235        elif self.EntryType != None:
1236            code('doTransitionWorker(event, state, next_state, m_cache_entry_ptr, addr);')
1237        else:
1238            code('doTransitionWorker(event, state, next_state, addr);')
1239
1240        code('''
1241    if (result == TransitionResult_Valid) {
1242        DPRINTF(RubyGenerated, "next_state: %s\\n",
1243                ${ident}_State_to_string(next_state));
1244        countTransition(state, event);
1245        DPRINTFR(ProtocolTrace, "%15d %3s %10s%20s %6s>%-6s %s %s\\n",
1246                 curTick(), m_version, "${ident}",
1247                 ${ident}_Event_to_string(event),
1248                 ${ident}_State_to_string(state),
1249                 ${ident}_State_to_string(next_state),
1250                 addr, GET_TRANSITION_COMMENT());
1251
1252        CLEAR_TRANSITION_COMMENT();
1253''')
1254        if self.TBEType != None and self.EntryType != None:
1255            code('setState(m_tbe_ptr, m_cache_entry_ptr, addr, next_state);')
1256            code('setAccessPermission(m_cache_entry_ptr, addr, next_state);')
1257        elif self.TBEType != None:
1258            code('setState(m_tbe_ptr, addr, next_state);')
1259            code('setAccessPermission(addr, next_state);')
1260        elif self.EntryType != None:
1261            code('setState(m_cache_entry_ptr, addr, next_state);')
1262            code('setAccessPermission(m_cache_entry_ptr, addr, next_state);')
1263        else:
1264            code('setState(addr, next_state);')
1265            code('setAccessPermission(addr, next_state);')
1266
1267        code('''
1268    } else if (result == TransitionResult_ResourceStall) {
1269        DPRINTFR(ProtocolTrace, "%15s %3s %10s%20s %6s>%-6s %s %s\\n",
1270                 curTick(), m_version, "${ident}",
1271                 ${ident}_Event_to_string(event),
1272                 ${ident}_State_to_string(state),
1273                 ${ident}_State_to_string(next_state),
1274                 addr, "Resource Stall");
1275    } else if (result == TransitionResult_ProtocolStall) {
1276        DPRINTF(RubyGenerated, "stalling\\n");
1277        DPRINTFR(ProtocolTrace, "%15s %3s %10s%20s %6s>%-6s %s %s\\n",
1278                 curTick(), m_version, "${ident}",
1279                 ${ident}_Event_to_string(event),
1280                 ${ident}_State_to_string(state),
1281                 ${ident}_State_to_string(next_state),
1282                 addr, "Protocol Stall");
1283    }
1284
1285    return result;
1286}
1287
1288TransitionResult
1289${ident}_Controller::doTransitionWorker(${ident}_Event event,
1290                                        ${ident}_State state,
1291                                        ${ident}_State& next_state,
1292''')
1293
1294        if self.TBEType != None:
1295            code('''
1296                                        ${{self.TBEType.c_ident}}*& m_tbe_ptr,
1297''')
1298        if self.EntryType != None:
1299                  code('''
1300                                        ${{self.EntryType.c_ident}}*& m_cache_entry_ptr,
1301''')
1302        code('''
1303                                        const Address& addr)
1304{
1305    switch(HASH_FUN(state, event)) {
1306''')
1307
1308        # This map will allow suppress generating duplicate code
1309        cases = orderdict()
1310
1311        for trans in self.transitions:
1312            case_string = "%s_State_%s, %s_Event_%s" % \
1313                (self.ident, trans.state.ident, self.ident, trans.event.ident)
1314
1315            case = self.symtab.codeFormatter()
1316            # Only set next_state if it changes
1317            if trans.state != trans.nextState:
1318                ns_ident = trans.nextState.ident
1319                case('next_state = ${ident}_State_${ns_ident};')
1320
1321            actions = trans.actions
1322            request_types = trans.request_types
1323
1324            # Check for resources
1325            case_sorter = []
1326            res = trans.resources
1327            for key,val in res.iteritems():
1328                if key.type.ident != "DNUCAStopTable":
1329                    val = '''
1330if (!%s.areNSlotsAvailable(%s))
1331    return TransitionResult_ResourceStall;
1332''' % (key.code, val)
1333                case_sorter.append(val)
1334
1335            # Check all of the request_types for resource constraints
1336            for request_type in request_types:
1337                val = '''
1338if (!checkResourceAvailable(%s_RequestType_%s, addr)) {
1339    return TransitionResult_ResourceStall;
1340}
1341''' % (self.ident, request_type.ident)
1342                case_sorter.append(val)
1343
1344            # Emit the code sequences in a sorted order.  This makes the
1345            # output deterministic (without this the output order can vary
1346            # since Map's keys() on a vector of pointers is not deterministic
1347            for c in sorted(case_sorter):
1348                case("$c")
1349
1350            # Record access types for this transition
1351            for request_type in request_types:
1352                case('recordRequestType(${ident}_RequestType_${{request_type.ident}}, addr);')
1353
1354            # Figure out if we stall
1355            stall = False
1356            for action in actions:
1357                if action.ident == "z_stall":
1358                    stall = True
1359                    break
1360
1361            if stall:
1362                case('return TransitionResult_ProtocolStall;')
1363            else:
1364                if self.TBEType != None and self.EntryType != None:
1365                    for action in actions:
1366                        case('${{action.ident}}(m_tbe_ptr, m_cache_entry_ptr, addr);')
1367                elif self.TBEType != None:
1368                    for action in actions:
1369                        case('${{action.ident}}(m_tbe_ptr, addr);')
1370                elif self.EntryType != None:
1371                    for action in actions:
1372                        case('${{action.ident}}(m_cache_entry_ptr, addr);')
1373                else:
1374                    for action in actions:
1375                        case('${{action.ident}}(addr);')
1376                case('return TransitionResult_Valid;')
1377
1378            case = str(case)
1379
1380            # Look to see if this transition code is unique.
1381            if case not in cases:
1382                cases[case] = []
1383
1384            cases[case].append(case_string)
1385
1386        # Walk through all of the unique code blocks and spit out the
1387        # corresponding case statement elements
1388        for case,transitions in cases.iteritems():
1389            # Iterative over all the multiple transitions that share
1390            # the same code
1391            for trans in transitions:
1392                code('  case HASH_FUN($trans):')
1393            code('    $case')
1394
1395        code('''
1396      default:
1397        fatal("Invalid transition\\n"
1398              "%s time: %d addr: %s event: %s state: %s\\n",
1399              name(), curCycle(), addr, event, state);
1400    }
1401    return TransitionResult_Valid;
1402}
1403''')
1404        code.write(path, "%s_Transitions.cc" % self.ident)
1405
1406
1407    # **************************
1408    # ******* HTML Files *******
1409    # **************************
1410    def frameRef(self, click_href, click_target, over_href, over_num, text):
1411        code = self.symtab.codeFormatter(fix_newlines=False)
1412        code("""<A href=\"$click_href\" target=\"$click_target\" onmouseover=\"
1413    if (parent.frames[$over_num].location != parent.location + '$over_href') {
1414        parent.frames[$over_num].location='$over_href'
1415    }\">
1416    ${{html.formatShorthand(text)}}
1417    </A>""")
1418        return str(code)
1419
1420    def writeHTMLFiles(self, path):
1421        # Create table with no row hilighted
1422        self.printHTMLTransitions(path, None)
1423
1424        # Generate transition tables
1425        for state in self.states.itervalues():
1426            self.printHTMLTransitions(path, state)
1427
1428        # Generate action descriptions
1429        for action in self.actions.itervalues():
1430            name = "%s_action_%s.html" % (self.ident, action.ident)
1431            code = html.createSymbol(action, "Action")
1432            code.write(path, name)
1433
1434        # Generate state descriptions
1435        for state in self.states.itervalues():
1436            name = "%s_State_%s.html" % (self.ident, state.ident)
1437            code = html.createSymbol(state, "State")
1438            code.write(path, name)
1439
1440        # Generate event descriptions
1441        for event in self.events.itervalues():
1442            name = "%s_Event_%s.html" % (self.ident, event.ident)
1443            code = html.createSymbol(event, "Event")
1444            code.write(path, name)
1445
1446    def printHTMLTransitions(self, path, active_state):
1447        code = self.symtab.codeFormatter()
1448
1449        code('''
1450<HTML>
1451<BODY link="blue" vlink="blue">
1452
1453<H1 align="center">${{html.formatShorthand(self.short)}}:
1454''')
1455        code.indent()
1456        for i,machine in enumerate(self.symtab.getAllType(StateMachine)):
1457            mid = machine.ident
1458            if i != 0:
1459                extra = " - "
1460            else:
1461                extra = ""
1462            if machine == self:
1463                code('$extra$mid')
1464            else:
1465                code('$extra<A target="Table" href="${mid}_table.html">$mid</A>')
1466        code.dedent()
1467
1468        code("""
1469</H1>
1470
1471<TABLE border=1>
1472<TR>
1473  <TH> </TH>
1474""")
1475
1476        for event in self.events.itervalues():
1477            href = "%s_Event_%s.html" % (self.ident, event.ident)
1478            ref = self.frameRef(href, "Status", href, "1", event.short)
1479            code('<TH bgcolor=white>$ref</TH>')
1480
1481        code('</TR>')
1482        # -- Body of table
1483        for state in self.states.itervalues():
1484            # -- Each row
1485            if state == active_state:
1486                color = "yellow"
1487            else:
1488                color = "white"
1489
1490            click = "%s_table_%s.html" % (self.ident, state.ident)
1491            over = "%s_State_%s.html" % (self.ident, state.ident)
1492            text = html.formatShorthand(state.short)
1493            ref = self.frameRef(click, "Table", over, "1", state.short)
1494            code('''
1495<TR>
1496  <TH bgcolor=$color>$ref</TH>
1497''')
1498
1499            # -- One column for each event
1500            for event in self.events.itervalues():
1501                trans = self.table.get((state,event), None)
1502                if trans is None:
1503                    # This is the no transition case
1504                    if state == active_state:
1505                        color = "#C0C000"
1506                    else:
1507                        color = "lightgrey"
1508
1509                    code('<TD bgcolor=$color>&nbsp;</TD>')
1510                    continue
1511
1512                next = trans.nextState
1513                stall_action = False
1514
1515                # -- Get the actions
1516                for action in trans.actions:
1517                    if action.ident == "z_stall" or \
1518                       action.ident == "zz_recycleMandatoryQueue":
1519                        stall_action = True
1520
1521                # -- Print out "actions/next-state"
1522                if stall_action:
1523                    if state == active_state:
1524                        color = "#C0C000"
1525                    else:
1526                        color = "lightgrey"
1527
1528                elif active_state and next.ident == active_state.ident:
1529                    color = "aqua"
1530                elif state == active_state:
1531                    color = "yellow"
1532                else:
1533                    color = "white"
1534
1535                code('<TD bgcolor=$color>')
1536                for action in trans.actions:
1537                    href = "%s_action_%s.html" % (self.ident, action.ident)
1538                    ref = self.frameRef(href, "Status", href, "1",
1539                                        action.short)
1540                    code('  $ref')
1541                if next != state:
1542                    if trans.actions:
1543                        code('/')
1544                    click = "%s_table_%s.html" % (self.ident, next.ident)
1545                    over = "%s_State_%s.html" % (self.ident, next.ident)
1546                    ref = self.frameRef(click, "Table", over, "1", next.short)
1547                    code("$ref")
1548                code("</TD>")
1549
1550            # -- Each row
1551            if state == active_state:
1552                color = "yellow"
1553            else:
1554                color = "white"
1555
1556            click = "%s_table_%s.html" % (self.ident, state.ident)
1557            over = "%s_State_%s.html" % (self.ident, state.ident)
1558            ref = self.frameRef(click, "Table", over, "1", state.short)
1559            code('''
1560  <TH bgcolor=$color>$ref</TH>
1561</TR>
1562''')
1563        code('''
1564<!- Column footer->
1565<TR>
1566  <TH> </TH>
1567''')
1568
1569        for event in self.events.itervalues():
1570            href = "%s_Event_%s.html" % (self.ident, event.ident)
1571            ref = self.frameRef(href, "Status", href, "1", event.short)
1572            code('<TH bgcolor=white>$ref</TH>')
1573        code('''
1574</TR>
1575</TABLE>
1576</BODY></HTML>
1577''')
1578
1579
1580        if active_state:
1581            name = "%s_table_%s.html" % (self.ident, active_state.ident)
1582        else:
1583            name = "%s_table.html" % self.ident
1584        code.write(path, name)
1585
1586__all__ = [ "StateMachine" ]
1587