StateMachine.py revision 8641
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/compiler.hh"
415#include "base/cprintf.hh"
416#include "debug/RubyGenerated.hh"
417#include "debug/RubySlicc.hh"
418#include "mem/protocol/${ident}_Controller.hh"
419#include "mem/protocol/${ident}_Event.hh"
420#include "mem/protocol/${ident}_State.hh"
421#include "mem/protocol/Types.hh"
422#include "mem/ruby/common/Global.hh"
423#include "mem/ruby/slicc_interface/RubySlicc_includes.hh"
424#include "mem/ruby/system/System.hh"
425
426using namespace std;
427''')
428
429        # include object classes
430        seen_types = set()
431        for var in self.objects:
432            if var.type.ident not in seen_types and not var.type.isPrimitive:
433                code('#include "mem/protocol/${{var.type.c_ident}}.hh"')
434            seen_types.add(var.type.ident)
435
436        code('''
437$c_ident *
438${c_ident}Params::create()
439{
440    return new $c_ident(this);
441}
442
443int $c_ident::m_num_controllers = 0;
444${ident}_ProfileDumper $c_ident::s_profileDumper;
445
446// for adding information to the protocol debug trace
447stringstream ${ident}_transitionComment;
448#define APPEND_TRANSITION_COMMENT(str) (${ident}_transitionComment << str)
449
450/** \\brief constructor */
451$c_ident::$c_ident(const Params *p)
452    : AbstractController(p)
453{
454    m_version = p->version;
455    m_transitions_per_cycle = p->transitions_per_cycle;
456    m_buffer_size = p->buffer_size;
457    m_recycle_latency = p->recycle_latency;
458    m_number_of_TBEs = p->number_of_TBEs;
459    m_is_blocking = false;
460    m_name = "${ident}";
461''')
462        #
463        # max_port_rank is used to size vectors and thus should be one plus the
464        # largest port rank
465        #
466        max_port_rank = self.in_ports[0].pairs["max_port_rank"] + 1
467        code('    m_max_in_port_rank = $max_port_rank;')
468        code.indent()
469
470        #
471        # After initializing the universal machine parameters, initialize the
472        # this machines config parameters.  Also detemine if these configuration
473        # params include a sequencer.  This information will be used later for
474        # contecting the sequencer back to the L1 cache controller.
475        #
476        contains_dma_sequencer = False
477        sequencers = []
478        for param in self.config_parameters:
479            if param.name == "dma_sequencer":
480                contains_dma_sequencer = True
481            elif re.compile("sequencer").search(param.name):
482                sequencers.append(param.name)
483            if param.pointer:
484                code('m_${{param.name}}_ptr = p->${{param.name}};')
485            else:
486                code('m_${{param.name}} = p->${{param.name}};')
487
488        #
489        # For the l1 cache controller, add the special atomic support which
490        # includes passing the sequencer a pointer to the controller.
491        #
492        if self.ident == "L1Cache":
493            if not sequencers:
494                self.error("The L1Cache controller must include the sequencer " \
495                           "configuration parameter")
496
497            for seq in sequencers:
498                code('''
499m_${{seq}}_ptr->setController(this);
500    ''')
501        #
502        # For the DMA controller, pass the sequencer a pointer to the
503        # controller.
504        #
505        if self.ident == "DMA":
506            if not contains_dma_sequencer:
507                self.error("The DMA controller must include the sequencer " \
508                           "configuration parameter")
509
510            code('''
511m_dma_sequencer_ptr->setController(this);
512''')
513
514        code('m_num_controllers++;')
515        for var in self.objects:
516            if var.ident.find("mandatoryQueue") >= 0:
517                code('m_${{var.c_ident}}_ptr = new ${{var.type.c_ident}}();')
518
519        code.dedent()
520        code('''
521}
522
523void
524$c_ident::init()
525{
526    MachineType machine_type;
527    int base;
528
529    m_machineID.type = MachineType_${ident};
530    m_machineID.num = m_version;
531
532    // initialize objects
533    m_profiler.setVersion(m_version);
534    s_profileDumper.registerProfiler(&m_profiler);
535
536''')
537
538        code.indent()
539        for var in self.objects:
540            vtype = var.type
541            vid = "m_%s_ptr" % var.c_ident
542            if "network" not in var:
543                # Not a network port object
544                if "primitive" in vtype:
545                    code('$vid = new ${{vtype.c_ident}};')
546                    if "default" in var:
547                        code('(*$vid) = ${{var["default"]}};')
548                else:
549                    # Normal Object
550                    # added by SS
551                    if "factory" in var:
552                        code('$vid = ${{var["factory"]}};')
553                    elif var.ident.find("mandatoryQueue") < 0:
554                        th = var.get("template_hack", "")
555                        expr = "%s  = new %s%s" % (vid, vtype.c_ident, th)
556
557                        args = ""
558                        if "non_obj" not in vtype and not vtype.isEnumeration:
559                            if expr.find("TBETable") >= 0:
560                                args = "m_number_of_TBEs"
561                            else:
562                                args = var.get("constructor_hack", "")
563
564                        code('$expr($args);')
565
566                    code('assert($vid != NULL);')
567
568                    if "default" in var:
569                        code('*$vid = ${{var["default"]}}; // Object default')
570                    elif "default" in vtype:
571                        comment = "Type %s default" % vtype.ident
572                        code('*$vid = ${{vtype["default"]}}; // $comment')
573
574                    # Set ordering
575                    if "ordered" in var and "trigger_queue" not in var:
576                        # A buffer
577                        code('$vid->setOrdering(${{var["ordered"]}});')
578
579                    # Set randomization
580                    if "random" in var:
581                        # A buffer
582                        code('$vid->setRandomization(${{var["random"]}});')
583
584                    # Set Priority
585                    if vtype.isBuffer and \
586                           "rank" in var and "trigger_queue" not in var:
587                        code('$vid->setPriority(${{var["rank"]}});')
588
589            else:
590                # Network port object
591                network = var["network"]
592                ordered =  var["ordered"]
593                vnet = var["virtual_network"]
594                vnet_type = var["vnet_type"]
595
596                assert var.machine is not None
597                code('''
598machine_type = string_to_MachineType("${{var.machine.ident}}");
599base = MachineType_base_number(machine_type);
600$vid = m_net_ptr->get${network}NetQueue(m_version + base, $ordered, $vnet, "$vnet_type");
601''')
602
603                code('assert($vid != NULL);')
604
605                # Set ordering
606                if "ordered" in var:
607                    # A buffer
608                    code('$vid->setOrdering(${{var["ordered"]}});')
609
610                # Set randomization
611                if "random" in var:
612                    # A buffer
613                    code('$vid->setRandomization(${{var["random"]}});')
614
615                # Set Priority
616                if "rank" in var:
617                    code('$vid->setPriority(${{var["rank"]}})')
618
619                # Set buffer size
620                if vtype.isBuffer:
621                    code('''
622if (m_buffer_size > 0) {
623    $vid->resize(m_buffer_size);
624}
625''')
626
627                # set description (may be overriden later by port def)
628                code('''
629$vid->setDescription("[Version " + to_string(m_version) + ", ${ident}, name=${{var.c_ident}}]");
630
631''')
632
633            if vtype.isBuffer:
634                if "recycle_latency" in var:
635                    code('$vid->setRecycleLatency(${{var["recycle_latency"]}});')
636                else:
637                    code('$vid->setRecycleLatency(m_recycle_latency);')
638
639
640        # Set the queue consumers
641        code()
642        for port in self.in_ports:
643            code('${{port.code}}.setConsumer(this);')
644
645        # Set the queue descriptions
646        code()
647        for port in self.in_ports:
648            code('${{port.code}}.setDescription("[Version " + to_string(m_version) + ", $ident, $port]");')
649
650        # Initialize the transition profiling
651        code()
652        for trans in self.transitions:
653            # Figure out if we stall
654            stall = False
655            for action in trans.actions:
656                if action.ident == "z_stall":
657                    stall = True
658
659            # Only possible if it is not a 'z' case
660            if not stall:
661                state = "%s_State_%s" % (self.ident, trans.state.ident)
662                event = "%s_Event_%s" % (self.ident, trans.event.ident)
663                code('m_profiler.possibleTransition($state, $event);')
664
665        code.dedent()
666        code('}')
667
668        has_mandatory_q = False
669        for port in self.in_ports:
670            if port.code.find("mandatoryQueue_ptr") >= 0:
671                has_mandatory_q = True
672
673        if has_mandatory_q:
674            mq_ident = "m_%s_mandatoryQueue_ptr" % self.ident
675        else:
676            mq_ident = "NULL"
677
678        code('''
679int
680$c_ident::getNumControllers()
681{
682    return m_num_controllers;
683}
684
685MessageBuffer*
686$c_ident::getMandatoryQueue() const
687{
688    return $mq_ident;
689}
690
691const int &
692$c_ident::getVersion() const
693{
694    return m_version;
695}
696
697const string
698$c_ident::toString() const
699{
700    return "$c_ident";
701}
702
703const string
704$c_ident::getName() const
705{
706    return m_name;
707}
708
709const MachineType
710$c_ident::getMachineType() const
711{
712    return MachineType_${ident};
713}
714
715void
716$c_ident::stallBuffer(MessageBuffer* buf, Address addr)
717{
718    if (m_waiting_buffers.count(addr) == 0) {
719        MsgVecType* msgVec = new MsgVecType;
720        msgVec->resize(m_max_in_port_rank, NULL);
721        m_waiting_buffers[addr] = msgVec;
722    }
723    (*(m_waiting_buffers[addr]))[m_cur_in_port_rank] = buf;
724}
725
726void
727$c_ident::wakeUpBuffers(Address addr)
728{
729    if (m_waiting_buffers.count(addr) > 0) {
730        //
731        // Wake up all possible lower rank (i.e. lower priority) buffers that could
732        // be waiting on this message.
733        //
734        for (int in_port_rank = m_cur_in_port_rank - 1;
735             in_port_rank >= 0;
736             in_port_rank--) {
737            if ((*(m_waiting_buffers[addr]))[in_port_rank] != NULL) {
738                (*(m_waiting_buffers[addr]))[in_port_rank]->reanalyzeMessages(addr);
739            }
740        }
741        delete m_waiting_buffers[addr];
742        m_waiting_buffers.erase(addr);
743    }
744}
745
746void
747$c_ident::wakeUpAllBuffers()
748{
749    //
750    // Wake up all possible buffers that could be waiting on any message.
751    //
752
753    std::vector<MsgVecType*> wokeUpMsgVecs;
754
755    if(m_waiting_buffers.size() > 0) {
756        for (WaitingBufType::iterator buf_iter = m_waiting_buffers.begin();
757             buf_iter != m_waiting_buffers.end();
758             ++buf_iter) {
759             for (MsgVecType::iterator vec_iter = buf_iter->second->begin();
760                  vec_iter != buf_iter->second->end();
761                  ++vec_iter) {
762                  if (*vec_iter != NULL) {
763                      (*vec_iter)->reanalyzeAllMessages();
764                  }
765             }
766             wokeUpMsgVecs.push_back(buf_iter->second);
767        }
768
769        for (std::vector<MsgVecType*>::iterator wb_iter = wokeUpMsgVecs.begin();
770             wb_iter != wokeUpMsgVecs.end();
771             ++wb_iter) {
772             delete (*wb_iter);
773        }
774
775        m_waiting_buffers.clear();
776    }
777}
778
779void
780$c_ident::blockOnQueue(Address addr, MessageBuffer* port)
781{
782    m_is_blocking = true;
783    m_block_map[addr] = port;
784}
785
786void
787$c_ident::unblock(Address addr)
788{
789    m_block_map.erase(addr);
790    if (m_block_map.size() == 0) {
791       m_is_blocking = false;
792    }
793}
794
795void
796$c_ident::print(ostream& out) const
797{
798    out << "[$c_ident " << m_version << "]";
799}
800
801void
802$c_ident::printConfig(ostream& out) const
803{
804    out << "$c_ident config: " << m_name << endl;
805    out << "  version: " << m_version << endl;
806    map<string, string>::const_iterator it;
807    for (it = m_cfg.begin(); it != m_cfg.end(); it++)
808        out << "  " << it->first << ": " << it->second << endl;
809}
810
811void
812$c_ident::printStats(ostream& out) const
813{
814''')
815        #
816        # Cache and Memory Controllers have specific profilers associated with
817        # them.  Print out these stats before dumping state transition stats.
818        #
819        for param in self.config_parameters:
820            if param.type_ast.type.ident == "CacheMemory" or \
821               param.type_ast.type.ident == "DirectoryMemory" or \
822                   param.type_ast.type.ident == "MemoryControl":
823                assert(param.pointer)
824                code('    m_${{param.ident}}_ptr->printStats(out);')
825
826        code('''
827    if (m_version == 0) {
828        s_profileDumper.dumpStats(out);
829    }
830}
831
832void $c_ident::clearStats() {
833''')
834        #
835        # Cache and Memory Controllers have specific profilers associated with
836        # them.  These stats must be cleared too.
837        #
838        for param in self.config_parameters:
839            if param.type_ast.type.ident == "CacheMemory" or \
840                   param.type_ast.type.ident == "MemoryControl":
841                assert(param.pointer)
842                code('    m_${{param.ident}}_ptr->clearStats();')
843
844        code('''
845    m_profiler.clearStats();
846}
847''')
848
849        if self.EntryType != None:
850            code('''
851
852// Set and Reset for cache_entry variable
853void
854$c_ident::set_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, AbstractCacheEntry* m_new_cache_entry)
855{
856  m_cache_entry_ptr = (${{self.EntryType.c_ident}}*)m_new_cache_entry;
857}
858
859void
860$c_ident::unset_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr)
861{
862  m_cache_entry_ptr = 0;
863}
864''')
865
866        if self.TBEType != None:
867            code('''
868
869// Set and Reset for tbe variable
870void
871$c_ident::set_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.TBEType.c_ident}}* m_new_tbe)
872{
873  m_tbe_ptr = m_new_tbe;
874}
875
876void
877$c_ident::unset_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr)
878{
879  m_tbe_ptr = NULL;
880}
881''')
882
883        code('''
884
885// Actions
886''')
887        if self.TBEType != None and self.EntryType != None:
888            for action in self.actions.itervalues():
889                if "c_code" not in action:
890                 continue
891
892                code('''
893/** \\brief ${{action.desc}} */
894void
895$c_ident::${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr)
896{
897    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
898    ${{action["c_code"]}}
899}
900
901''')
902        elif self.TBEType != None:
903            for action in self.actions.itervalues():
904                if "c_code" not in action:
905                 continue
906
907                code('''
908/** \\brief ${{action.desc}} */
909void
910$c_ident::${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, const Address& addr)
911{
912    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
913    ${{action["c_code"]}}
914}
915
916''')
917        elif self.EntryType != None:
918            for action in self.actions.itervalues():
919                if "c_code" not in action:
920                 continue
921
922                code('''
923/** \\brief ${{action.desc}} */
924void
925$c_ident::${{action.ident}}(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr)
926{
927    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
928    ${{action["c_code"]}}
929}
930
931''')
932        else:
933            for action in self.actions.itervalues():
934                if "c_code" not in action:
935                 continue
936
937                code('''
938/** \\brief ${{action.desc}} */
939void
940$c_ident::${{action.ident}}(const Address& addr)
941{
942    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
943    ${{action["c_code"]}}
944}
945
946''')
947        for func in self.functions:
948            code(func.generateCode())
949
950        code.write(path, "%s.cc" % c_ident)
951
952    def printCWakeup(self, path):
953        '''Output the wakeup loop for the events'''
954
955        code = self.symtab.codeFormatter()
956        ident = self.ident
957
958        code('''
959// Auto generated C++ code started by $__file__:$__line__
960// ${ident}: ${{self.short}}
961
962#include <cassert>
963
964#include "base/misc.hh"
965#include "debug/RubySlicc.hh"
966#include "mem/protocol/${ident}_Controller.hh"
967#include "mem/protocol/${ident}_Event.hh"
968#include "mem/protocol/${ident}_State.hh"
969#include "mem/protocol/Types.hh"
970#include "mem/ruby/common/Global.hh"
971#include "mem/ruby/slicc_interface/RubySlicc_includes.hh"
972#include "mem/ruby/system/System.hh"
973
974using namespace std;
975
976void
977${ident}_Controller::wakeup()
978{
979    int counter = 0;
980    while (true) {
981        // Some cases will put us into an infinite loop without this limit
982        assert(counter <= m_transitions_per_cycle);
983        if (counter == m_transitions_per_cycle) {
984            // Count how often we are fully utilized
985            g_system_ptr->getProfiler()->controllerBusy(m_machineID);
986
987            // Wakeup in another cycle and try again
988            g_eventQueue_ptr->scheduleEvent(this, 1);
989            break;
990        }
991''')
992
993        code.indent()
994        code.indent()
995
996        # InPorts
997        #
998        for port in self.in_ports:
999            code.indent()
1000            code('// ${ident}InPort $port')
1001            if port.pairs.has_key("rank"):
1002                code('m_cur_in_port_rank = ${{port.pairs["rank"]}};')
1003            else:
1004                code('m_cur_in_port_rank = 0;')
1005            code('${{port["c_code_in_port"]}}')
1006            code.dedent()
1007
1008            code('')
1009
1010        code.dedent()
1011        code.dedent()
1012        code('''
1013        break;  // If we got this far, we have nothing left todo
1014    }
1015    // g_eventQueue_ptr->scheduleEvent(this, 1);
1016}
1017''')
1018
1019        code.write(path, "%s_Wakeup.cc" % self.ident)
1020
1021    def printCSwitch(self, path):
1022        '''Output switch statement for transition table'''
1023
1024        code = self.symtab.codeFormatter()
1025        ident = self.ident
1026
1027        code('''
1028// Auto generated C++ code started by $__file__:$__line__
1029// ${ident}: ${{self.short}}
1030
1031#include <cassert>
1032
1033#include "base/misc.hh"
1034#include "base/trace.hh"
1035#include "debug/ProtocolTrace.hh"
1036#include "debug/RubyGenerated.hh"
1037#include "mem/protocol/${ident}_Controller.hh"
1038#include "mem/protocol/${ident}_Event.hh"
1039#include "mem/protocol/${ident}_State.hh"
1040#include "mem/protocol/Types.hh"
1041#include "mem/ruby/common/Global.hh"
1042#include "mem/ruby/system/System.hh"
1043
1044#define HASH_FUN(state, event)  ((int(state)*${ident}_Event_NUM)+int(event))
1045
1046#define GET_TRANSITION_COMMENT() (${ident}_transitionComment.str())
1047#define CLEAR_TRANSITION_COMMENT() (${ident}_transitionComment.str(""))
1048
1049TransitionResult
1050${ident}_Controller::doTransition(${ident}_Event event,
1051''')
1052        if self.EntryType != None:
1053            code('''
1054                                  ${{self.EntryType.c_ident}}* m_cache_entry_ptr,
1055''')
1056        if self.TBEType != None:
1057            code('''
1058                                  ${{self.TBEType.c_ident}}* m_tbe_ptr,
1059''')
1060        code('''
1061                                  const Address &addr)
1062{
1063''')
1064        if self.TBEType != None and self.EntryType != None:
1065            code('${ident}_State state = getState(m_tbe_ptr, m_cache_entry_ptr, addr);')
1066        elif self.TBEType != None:
1067            code('${ident}_State state = getState(m_tbe_ptr, addr);')
1068        elif self.EntryType != None:
1069            code('${ident}_State state = getState(m_cache_entry_ptr, addr);')
1070        else:
1071            code('${ident}_State state = getState(addr);')
1072
1073        code('''
1074    ${ident}_State next_state = state;
1075
1076    DPRINTF(RubyGenerated, "%s, Time: %lld, state: %s, event: %s, addr: %s\\n",
1077            *this,
1078            g_eventQueue_ptr->getTime(),
1079            ${ident}_State_to_string(state),
1080            ${ident}_Event_to_string(event),
1081            addr);
1082
1083    TransitionResult result =
1084''')
1085        if self.TBEType != None and self.EntryType != None:
1086            code('doTransitionWorker(event, state, next_state, m_tbe_ptr, m_cache_entry_ptr, addr);')
1087        elif self.TBEType != None:
1088            code('doTransitionWorker(event, state, next_state, m_tbe_ptr, addr);')
1089        elif self.EntryType != None:
1090            code('doTransitionWorker(event, state, next_state, m_cache_entry_ptr, addr);')
1091        else:
1092            code('doTransitionWorker(event, state, next_state, addr);')
1093
1094        code('''
1095    if (result == TransitionResult_Valid) {
1096        DPRINTF(RubyGenerated, "next_state: %s\\n",
1097                ${ident}_State_to_string(next_state));
1098        m_profiler.countTransition(state, event);
1099        DPRINTFR(ProtocolTrace, "%15d %3s %10s%20s %6s>%-6s %s %s\\n",
1100                 curTick(), m_version, "${ident}",
1101                 ${ident}_Event_to_string(event),
1102                 ${ident}_State_to_string(state),
1103                 ${ident}_State_to_string(next_state),
1104                 addr, GET_TRANSITION_COMMENT());
1105
1106        CLEAR_TRANSITION_COMMENT();
1107''')
1108        if self.TBEType != None and self.EntryType != None:
1109            code('setState(m_tbe_ptr, m_cache_entry_ptr, addr, next_state);')
1110            code('setAccessPermission(m_cache_entry_ptr, addr, next_state);')
1111        elif self.TBEType != None:
1112            code('setState(m_tbe_ptr, addr, next_state);')
1113            code('setAccessPermission(addr, next_state);')
1114        elif self.EntryType != None:
1115            code('setState(m_cache_entry_ptr, addr, next_state);')
1116            code('setAccessPermission(m_cache_entry_ptr, addr, next_state);')
1117        else:
1118            code('setState(addr, next_state);')
1119            code('setAccessPermission(addr, next_state);')
1120
1121        code('''
1122    } else if (result == TransitionResult_ResourceStall) {
1123        DPRINTFR(ProtocolTrace, "%15s %3s %10s%20s %6s>%-6s %s %s\\n",
1124                 curTick(), m_version, "${ident}",
1125                 ${ident}_Event_to_string(event),
1126                 ${ident}_State_to_string(state),
1127                 ${ident}_State_to_string(next_state),
1128                 addr, "Resource Stall");
1129    } else if (result == TransitionResult_ProtocolStall) {
1130        DPRINTF(RubyGenerated, "stalling\\n");
1131        DPRINTFR(ProtocolTrace, "%15s %3s %10s%20s %6s>%-6s %s %s\\n",
1132                 curTick(), m_version, "${ident}",
1133                 ${ident}_Event_to_string(event),
1134                 ${ident}_State_to_string(state),
1135                 ${ident}_State_to_string(next_state),
1136                 addr, "Protocol Stall");
1137    }
1138
1139    return result;
1140}
1141
1142TransitionResult
1143${ident}_Controller::doTransitionWorker(${ident}_Event event,
1144                                        ${ident}_State state,
1145                                        ${ident}_State& next_state,
1146''')
1147
1148        if self.TBEType != None:
1149            code('''
1150                                        ${{self.TBEType.c_ident}}*& m_tbe_ptr,
1151''')
1152        if self.EntryType != None:
1153                  code('''
1154                                        ${{self.EntryType.c_ident}}*& m_cache_entry_ptr,
1155''')
1156        code('''
1157                                        const Address& addr)
1158{
1159    switch(HASH_FUN(state, event)) {
1160''')
1161
1162        # This map will allow suppress generating duplicate code
1163        cases = orderdict()
1164
1165        for trans in self.transitions:
1166            case_string = "%s_State_%s, %s_Event_%s" % \
1167                (self.ident, trans.state.ident, self.ident, trans.event.ident)
1168
1169            case = self.symtab.codeFormatter()
1170            # Only set next_state if it changes
1171            if trans.state != trans.nextState:
1172                ns_ident = trans.nextState.ident
1173                case('next_state = ${ident}_State_${ns_ident};')
1174
1175            actions = trans.actions
1176
1177            # Check for resources
1178            case_sorter = []
1179            res = trans.resources
1180            for key,val in res.iteritems():
1181                if key.type.ident != "DNUCAStopTable":
1182                    val = '''
1183if (!%s.areNSlotsAvailable(%s))
1184    return TransitionResult_ResourceStall;
1185''' % (key.code, val)
1186                case_sorter.append(val)
1187
1188
1189            # Emit the code sequences in a sorted order.  This makes the
1190            # output deterministic (without this the output order can vary
1191            # since Map's keys() on a vector of pointers is not deterministic
1192            for c in sorted(case_sorter):
1193                case("$c")
1194
1195            # Figure out if we stall
1196            stall = False
1197            for action in actions:
1198                if action.ident == "z_stall":
1199                    stall = True
1200                    break
1201
1202            if stall:
1203                case('return TransitionResult_ProtocolStall;')
1204            else:
1205                if self.TBEType != None and self.EntryType != None:
1206                    for action in actions:
1207                        case('${{action.ident}}(m_tbe_ptr, m_cache_entry_ptr, addr);')
1208                elif self.TBEType != None:
1209                    for action in actions:
1210                        case('${{action.ident}}(m_tbe_ptr, addr);')
1211                elif self.EntryType != None:
1212                    for action in actions:
1213                        case('${{action.ident}}(m_cache_entry_ptr, addr);')
1214                else:
1215                    for action in actions:
1216                        case('${{action.ident}}(addr);')
1217                case('return TransitionResult_Valid;')
1218
1219            case = str(case)
1220
1221            # Look to see if this transition code is unique.
1222            if case not in cases:
1223                cases[case] = []
1224
1225            cases[case].append(case_string)
1226
1227        # Walk through all of the unique code blocks and spit out the
1228        # corresponding case statement elements
1229        for case,transitions in cases.iteritems():
1230            # Iterative over all the multiple transitions that share
1231            # the same code
1232            for trans in transitions:
1233                code('  case HASH_FUN($trans):')
1234            code('    $case')
1235
1236        code('''
1237      default:
1238        fatal("Invalid transition\\n"
1239              "%s time: %d addr: %s event: %s state: %s\\n",
1240              name(), g_eventQueue_ptr->getTime(), addr, event, state);
1241    }
1242    return TransitionResult_Valid;
1243}
1244''')
1245        code.write(path, "%s_Transitions.cc" % self.ident)
1246
1247    def printProfileDumperHH(self, path):
1248        code = self.symtab.codeFormatter()
1249        ident = self.ident
1250
1251        code('''
1252// Auto generated C++ code started by $__file__:$__line__
1253// ${ident}: ${{self.short}}
1254
1255#ifndef __${ident}_PROFILE_DUMPER_HH__
1256#define __${ident}_PROFILE_DUMPER_HH__
1257
1258#include <cassert>
1259#include <iostream>
1260#include <vector>
1261
1262#include "${ident}_Event.hh"
1263#include "${ident}_Profiler.hh"
1264
1265typedef std::vector<${ident}_Profiler *> ${ident}_profilers;
1266
1267class ${ident}_ProfileDumper
1268{
1269  public:
1270    ${ident}_ProfileDumper();
1271    void registerProfiler(${ident}_Profiler* profiler);
1272    void dumpStats(std::ostream& out) const;
1273
1274  private:
1275    ${ident}_profilers m_profilers;
1276};
1277
1278#endif // __${ident}_PROFILE_DUMPER_HH__
1279''')
1280        code.write(path, "%s_ProfileDumper.hh" % self.ident)
1281
1282    def printProfileDumperCC(self, path):
1283        code = self.symtab.codeFormatter()
1284        ident = self.ident
1285
1286        code('''
1287// Auto generated C++ code started by $__file__:$__line__
1288// ${ident}: ${{self.short}}
1289
1290#include "mem/protocol/${ident}_ProfileDumper.hh"
1291
1292${ident}_ProfileDumper::${ident}_ProfileDumper()
1293{
1294}
1295
1296void
1297${ident}_ProfileDumper::registerProfiler(${ident}_Profiler* profiler)
1298{
1299    m_profilers.push_back(profiler);
1300}
1301
1302void
1303${ident}_ProfileDumper::dumpStats(std::ostream& out) const
1304{
1305    out << " --- ${ident} ---\\n";
1306    out << " - Event Counts -\\n";
1307    for (${ident}_Event event = ${ident}_Event_FIRST;
1308         event < ${ident}_Event_NUM;
1309         ++event) {
1310        out << (${ident}_Event) event << " [";
1311        uint64 total = 0;
1312        for (int i = 0; i < m_profilers.size(); i++) {
1313             out << m_profilers[i]->getEventCount(event) << " ";
1314             total += m_profilers[i]->getEventCount(event);
1315        }
1316        out << "] " << total << "\\n";
1317    }
1318    out << "\\n";
1319    out << " - Transitions -\\n";
1320    for (${ident}_State state = ${ident}_State_FIRST;
1321         state < ${ident}_State_NUM;
1322         ++state) {
1323        for (${ident}_Event event = ${ident}_Event_FIRST;
1324             event < ${ident}_Event_NUM;
1325             ++event) {
1326            if (m_profilers[0]->isPossible(state, event)) {
1327                out << (${ident}_State) state << "  "
1328                    << (${ident}_Event) event << " [";
1329                uint64 total = 0;
1330                for (int i = 0; i < m_profilers.size(); i++) {
1331                     out << m_profilers[i]->getTransitionCount(state, event) << " ";
1332                     total += m_profilers[i]->getTransitionCount(state, event);
1333                }
1334                out << "] " << total << "\\n";
1335            }
1336        }
1337        out << "\\n";
1338    }
1339}
1340''')
1341        code.write(path, "%s_ProfileDumper.cc" % self.ident)
1342
1343    def printProfilerHH(self, path):
1344        code = self.symtab.codeFormatter()
1345        ident = self.ident
1346
1347        code('''
1348// Auto generated C++ code started by $__file__:$__line__
1349// ${ident}: ${{self.short}}
1350
1351#ifndef __${ident}_PROFILER_HH__
1352#define __${ident}_PROFILER_HH__
1353
1354#include <cassert>
1355#include <iostream>
1356
1357#include "mem/protocol/${ident}_Event.hh"
1358#include "mem/protocol/${ident}_State.hh"
1359#include "mem/ruby/common/TypeDefines.hh"
1360
1361class ${ident}_Profiler
1362{
1363  public:
1364    ${ident}_Profiler();
1365    void setVersion(int version);
1366    void countTransition(${ident}_State state, ${ident}_Event event);
1367    void possibleTransition(${ident}_State state, ${ident}_Event event);
1368    uint64 getEventCount(${ident}_Event event);
1369    bool isPossible(${ident}_State state, ${ident}_Event event);
1370    uint64 getTransitionCount(${ident}_State state, ${ident}_Event event);
1371    void clearStats();
1372
1373  private:
1374    int m_counters[${ident}_State_NUM][${ident}_Event_NUM];
1375    int m_event_counters[${ident}_Event_NUM];
1376    bool m_possible[${ident}_State_NUM][${ident}_Event_NUM];
1377    int m_version;
1378};
1379
1380#endif // __${ident}_PROFILER_HH__
1381''')
1382        code.write(path, "%s_Profiler.hh" % self.ident)
1383
1384    def printProfilerCC(self, path):
1385        code = self.symtab.codeFormatter()
1386        ident = self.ident
1387
1388        code('''
1389// Auto generated C++ code started by $__file__:$__line__
1390// ${ident}: ${{self.short}}
1391
1392#include <cassert>
1393
1394#include "mem/protocol/${ident}_Profiler.hh"
1395
1396${ident}_Profiler::${ident}_Profiler()
1397{
1398    for (int state = 0; state < ${ident}_State_NUM; state++) {
1399        for (int event = 0; event < ${ident}_Event_NUM; event++) {
1400            m_possible[state][event] = false;
1401            m_counters[state][event] = 0;
1402        }
1403    }
1404    for (int event = 0; event < ${ident}_Event_NUM; event++) {
1405        m_event_counters[event] = 0;
1406    }
1407}
1408
1409void
1410${ident}_Profiler::setVersion(int version)
1411{
1412    m_version = version;
1413}
1414
1415void
1416${ident}_Profiler::clearStats()
1417{
1418    for (int state = 0; state < ${ident}_State_NUM; state++) {
1419        for (int event = 0; event < ${ident}_Event_NUM; event++) {
1420            m_counters[state][event] = 0;
1421        }
1422    }
1423
1424    for (int event = 0; event < ${ident}_Event_NUM; event++) {
1425        m_event_counters[event] = 0;
1426    }
1427}
1428void
1429${ident}_Profiler::countTransition(${ident}_State state, ${ident}_Event event)
1430{
1431    assert(m_possible[state][event]);
1432    m_counters[state][event]++;
1433    m_event_counters[event]++;
1434}
1435void
1436${ident}_Profiler::possibleTransition(${ident}_State state,
1437                                      ${ident}_Event event)
1438{
1439    m_possible[state][event] = true;
1440}
1441
1442uint64
1443${ident}_Profiler::getEventCount(${ident}_Event event)
1444{
1445    return m_event_counters[event];
1446}
1447
1448bool
1449${ident}_Profiler::isPossible(${ident}_State state, ${ident}_Event event)
1450{
1451    return m_possible[state][event];
1452}
1453
1454uint64
1455${ident}_Profiler::getTransitionCount(${ident}_State state,
1456                                      ${ident}_Event event)
1457{
1458    return m_counters[state][event];
1459}
1460
1461''')
1462        code.write(path, "%s_Profiler.cc" % self.ident)
1463
1464    # **************************
1465    # ******* HTML Files *******
1466    # **************************
1467    def frameRef(self, click_href, click_target, over_href, over_num, text):
1468        code = self.symtab.codeFormatter(fix_newlines=False)
1469        code("""<A href=\"$click_href\" target=\"$click_target\" onmouseover=\"
1470    if (parent.frames[$over_num].location != parent.location + '$over_href') {
1471        parent.frames[$over_num].location='$over_href'
1472    }\">
1473    ${{html.formatShorthand(text)}}
1474    </A>""")
1475        return str(code)
1476
1477    def writeHTMLFiles(self, path):
1478        # Create table with no row hilighted
1479        self.printHTMLTransitions(path, None)
1480
1481        # Generate transition tables
1482        for state in self.states.itervalues():
1483            self.printHTMLTransitions(path, state)
1484
1485        # Generate action descriptions
1486        for action in self.actions.itervalues():
1487            name = "%s_action_%s.html" % (self.ident, action.ident)
1488            code = html.createSymbol(action, "Action")
1489            code.write(path, name)
1490
1491        # Generate state descriptions
1492        for state in self.states.itervalues():
1493            name = "%s_State_%s.html" % (self.ident, state.ident)
1494            code = html.createSymbol(state, "State")
1495            code.write(path, name)
1496
1497        # Generate event descriptions
1498        for event in self.events.itervalues():
1499            name = "%s_Event_%s.html" % (self.ident, event.ident)
1500            code = html.createSymbol(event, "Event")
1501            code.write(path, name)
1502
1503    def printHTMLTransitions(self, path, active_state):
1504        code = self.symtab.codeFormatter()
1505
1506        code('''
1507<HTML>
1508<BODY link="blue" vlink="blue">
1509
1510<H1 align="center">${{html.formatShorthand(self.short)}}:
1511''')
1512        code.indent()
1513        for i,machine in enumerate(self.symtab.getAllType(StateMachine)):
1514            mid = machine.ident
1515            if i != 0:
1516                extra = " - "
1517            else:
1518                extra = ""
1519            if machine == self:
1520                code('$extra$mid')
1521            else:
1522                code('$extra<A target="Table" href="${mid}_table.html">$mid</A>')
1523        code.dedent()
1524
1525        code("""
1526</H1>
1527
1528<TABLE border=1>
1529<TR>
1530  <TH> </TH>
1531""")
1532
1533        for event in self.events.itervalues():
1534            href = "%s_Event_%s.html" % (self.ident, event.ident)
1535            ref = self.frameRef(href, "Status", href, "1", event.short)
1536            code('<TH bgcolor=white>$ref</TH>')
1537
1538        code('</TR>')
1539        # -- Body of table
1540        for state in self.states.itervalues():
1541            # -- Each row
1542            if state == active_state:
1543                color = "yellow"
1544            else:
1545                color = "white"
1546
1547            click = "%s_table_%s.html" % (self.ident, state.ident)
1548            over = "%s_State_%s.html" % (self.ident, state.ident)
1549            text = html.formatShorthand(state.short)
1550            ref = self.frameRef(click, "Table", over, "1", state.short)
1551            code('''
1552<TR>
1553  <TH bgcolor=$color>$ref</TH>
1554''')
1555
1556            # -- One column for each event
1557            for event in self.events.itervalues():
1558                trans = self.table.get((state,event), None)
1559                if trans is None:
1560                    # This is the no transition case
1561                    if state == active_state:
1562                        color = "#C0C000"
1563                    else:
1564                        color = "lightgrey"
1565
1566                    code('<TD bgcolor=$color>&nbsp;</TD>')
1567                    continue
1568
1569                next = trans.nextState
1570                stall_action = False
1571
1572                # -- Get the actions
1573                for action in trans.actions:
1574                    if action.ident == "z_stall" or \
1575                       action.ident == "zz_recycleMandatoryQueue":
1576                        stall_action = True
1577
1578                # -- Print out "actions/next-state"
1579                if stall_action:
1580                    if state == active_state:
1581                        color = "#C0C000"
1582                    else:
1583                        color = "lightgrey"
1584
1585                elif active_state and next.ident == active_state.ident:
1586                    color = "aqua"
1587                elif state == active_state:
1588                    color = "yellow"
1589                else:
1590                    color = "white"
1591
1592                code('<TD bgcolor=$color>')
1593                for action in trans.actions:
1594                    href = "%s_action_%s.html" % (self.ident, action.ident)
1595                    ref = self.frameRef(href, "Status", href, "1",
1596                                        action.short)
1597                    code('  $ref')
1598                if next != state:
1599                    if trans.actions:
1600                        code('/')
1601                    click = "%s_table_%s.html" % (self.ident, next.ident)
1602                    over = "%s_State_%s.html" % (self.ident, next.ident)
1603                    ref = self.frameRef(click, "Table", over, "1", next.short)
1604                    code("$ref")
1605                code("</TD>")
1606
1607            # -- Each row
1608            if state == active_state:
1609                color = "yellow"
1610            else:
1611                color = "white"
1612
1613            click = "%s_table_%s.html" % (self.ident, state.ident)
1614            over = "%s_State_%s.html" % (self.ident, state.ident)
1615            ref = self.frameRef(click, "Table", over, "1", state.short)
1616            code('''
1617  <TH bgcolor=$color>$ref</TH>
1618</TR>
1619''')
1620        code('''
1621<!- Column footer->
1622<TR>
1623  <TH> </TH>
1624''')
1625
1626        for event in self.events.itervalues():
1627            href = "%s_Event_%s.html" % (self.ident, event.ident)
1628            ref = self.frameRef(href, "Status", href, "1", event.short)
1629            code('<TH bgcolor=white>$ref</TH>')
1630        code('''
1631</TR>
1632</TABLE>
1633</BODY></HTML>
1634''')
1635
1636
1637        if active_state:
1638            name = "%s_table_%s.html" % (self.ident, active_state.ident)
1639        else:
1640            name = "%s_table.html" % self.ident
1641        code.write(path, name)
1642
1643__all__ = [ "StateMachine" ]
1644