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