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