StateMachine.py revision 8229:78bf55f23338
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        for func in self.functions:
172            func.writeCodeFiles(path)
173
174    def printControllerPython(self, path):
175        code = self.symtab.codeFormatter()
176        ident = self.ident
177        py_ident = "%s_Controller" % ident
178        c_ident = "%s_Controller" % self.ident
179        code('''
180from m5.params import *
181from m5.SimObject import SimObject
182from Controller import RubyController
183
184class $py_ident(RubyController):
185    type = '$py_ident'
186''')
187        code.indent()
188        for param in self.config_parameters:
189            dflt_str = ''
190            if param.default is not None:
191                dflt_str = str(param.default) + ', '
192            if python_class_map.has_key(param.type_ast.type.c_ident):
193                python_type = python_class_map[param.type_ast.type.c_ident]
194                code('${{param.name}} = Param.${{python_type}}(${dflt_str}"")')
195            else:
196                self.error("Unknown c++ to python class conversion for c++ " \
197                           "type: '%s'. Please update the python_class_map " \
198                           "in StateMachine.py", param.type_ast.type.c_ident)
199        code.dedent()
200        code.write(path, '%s.py' % py_ident)
201
202
203    def printControllerHH(self, path):
204        '''Output the method declarations for the class declaration'''
205        code = self.symtab.codeFormatter()
206        ident = self.ident
207        c_ident = "%s_Controller" % self.ident
208
209        self.message_buffer_names = []
210
211        code('''
212/** \\file $c_ident.hh
213 *
214 * Auto generated C++ code started by $__file__:$__line__
215 * Created by slicc definition of Module "${{self.short}}"
216 */
217
218#ifndef __${ident}_CONTROLLER_HH__
219#define __${ident}_CONTROLLER_HH__
220
221#include <iostream>
222#include <sstream>
223#include <string>
224
225#include "mem/protocol/${ident}_ProfileDumper.hh"
226#include "mem/protocol/${ident}_Profiler.hh"
227#include "mem/protocol/TransitionResult.hh"
228#include "mem/protocol/Types.hh"
229#include "mem/ruby/common/Consumer.hh"
230#include "mem/ruby/common/Global.hh"
231#include "mem/ruby/slicc_interface/AbstractController.hh"
232#include "params/$c_ident.hh"
233''')
234
235        seen_types = set()
236        for var in self.objects:
237            if var.type.ident not in seen_types and not var.type.isPrimitive:
238                code('#include "mem/protocol/${{var.type.c_ident}}.hh"')
239            seen_types.add(var.type.ident)
240
241        # for adding information to the protocol debug trace
242        code('''
243extern std::stringstream ${ident}_transitionComment;
244
245class $c_ident : public AbstractController
246{
247// the coherence checker needs to call isBlockExclusive() and isBlockShared()
248// making the Chip a friend class is an easy way to do this for now
249
250public:
251    typedef ${c_ident}Params Params;
252    $c_ident(const Params *p);
253    static int getNumControllers();
254    void init();
255    MessageBuffer* getMandatoryQueue() const;
256    const int & getVersion() const;
257    const std::string toString() const;
258    const std::string getName() const;
259    const MachineType getMachineType() const;
260    void stallBuffer(MessageBuffer* buf, Address addr);
261    void wakeUpBuffers(Address addr);
262    void wakeUpAllBuffers();
263    void initNetworkPtr(Network* net_ptr) { m_net_ptr = net_ptr; }
264    void print(std::ostream& out) const;
265    void printConfig(std::ostream& out) const;
266    void wakeup();
267    void printStats(std::ostream& out) const;
268    void clearStats();
269    void blockOnQueue(Address addr, MessageBuffer* port);
270    void unblock(Address addr);
271
272private:
273''')
274
275        code.indent()
276        # added by SS
277        for param in self.config_parameters:
278            if param.pointer:
279                code('${{param.type_ast.type}}* m_${{param.ident}}_ptr;')
280            else:
281                code('${{param.type_ast.type}} m_${{param.ident}};')
282
283        code('''
284int m_number_of_TBEs;
285
286TransitionResult doTransition(${ident}_Event event,
287''')
288
289        if self.EntryType != None:
290            code('''
291                              ${{self.EntryType.c_ident}}* m_cache_entry_ptr,
292''')
293        if self.TBEType != None:
294            code('''
295                              ${{self.TBEType.c_ident}}* m_tbe_ptr,
296''')
297
298        code('''
299                              const Address& addr);
300
301TransitionResult doTransitionWorker(${ident}_Event event,
302                                    ${ident}_State state,
303                                    ${ident}_State& next_state,
304''')
305
306        if self.TBEType != None:
307            code('''
308                                    ${{self.TBEType.c_ident}}*& m_tbe_ptr,
309''')
310        if self.EntryType != None:
311            code('''
312                                    ${{self.EntryType.c_ident}}*& m_cache_entry_ptr,
313''')
314
315        code('''
316                                    const Address& addr);
317
318std::string m_name;
319int m_transitions_per_cycle;
320int m_buffer_size;
321int m_recycle_latency;
322std::map<std::string, std::string> m_cfg;
323NodeID m_version;
324Network* m_net_ptr;
325MachineID m_machineID;
326bool m_is_blocking;
327std::map<Address, MessageBuffer*> m_block_map;
328typedef std::vector<MessageBuffer*> MsgVecType;
329typedef m5::hash_map< Address, MsgVecType* > WaitingBufType;
330WaitingBufType m_waiting_buffers;
331int m_max_in_port_rank;
332int m_cur_in_port_rank;
333static ${ident}_ProfileDumper s_profileDumper;
334${ident}_Profiler m_profiler;
335static int m_num_controllers;
336
337// Internal functions
338''')
339
340        for func in self.functions:
341            proto = func.prototype
342            if proto:
343                code('$proto')
344
345        if self.EntryType != None:
346            code('''
347
348// Set and Reset for cache_entry variable
349void set_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, AbstractCacheEntry* m_new_cache_entry);
350void unset_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr);
351// Set permissions for the cache_entry
352void set_permission(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, AccessPermission perm);
353''')
354
355        if self.TBEType != None:
356            code('''
357
358// Set and Reset for tbe variable
359void set_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${ident}_TBE* m_new_tbe);
360void unset_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr);
361''')
362
363        code('''
364
365// Actions
366''')
367        if self.TBEType != None and self.EntryType != None:
368            for action in self.actions.itervalues():
369                code('/** \\brief ${{action.desc}} */')
370                code('void ${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr);')
371        elif self.TBEType != None:
372            for action in self.actions.itervalues():
373                code('/** \\brief ${{action.desc}} */')
374                code('void ${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, const Address& addr);')
375        elif self.EntryType != None:
376            for action in self.actions.itervalues():
377                code('/** \\brief ${{action.desc}} */')
378                code('void ${{action.ident}}(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr);')
379        else:
380            for action in self.actions.itervalues():
381                code('/** \\brief ${{action.desc}} */')
382                code('void ${{action.ident}}(const Address& addr);')
383
384        # the controller internal variables
385        code('''
386
387// Objects
388''')
389        for var in self.objects:
390            th = var.get("template_hack", "")
391            code('${{var.type.c_ident}}$th* m_${{var.c_ident}}_ptr;')
392
393            if var.type.ident == "MessageBuffer":
394                self.message_buffer_names.append("m_%s_ptr" % var.c_ident)
395
396        code.dedent()
397        code('};')
398        code('#endif // __${ident}_CONTROLLER_H__')
399        code.write(path, '%s.hh' % c_ident)
400
401    def printControllerCC(self, path):
402        '''Output the actions for performing the actions'''
403
404        code = self.symtab.codeFormatter()
405        ident = self.ident
406        c_ident = "%s_Controller" % self.ident
407
408        code('''
409/** \\file $c_ident.cc
410 *
411 * Auto generated C++ code started by $__file__:$__line__
412 * Created by slicc definition of Module "${{self.short}}"
413 */
414
415#include <cassert>
416#include <sstream>
417#include <string>
418
419#include "base/cprintf.hh"
420#include "mem/protocol/${ident}_Controller.hh"
421#include "mem/protocol/${ident}_Event.hh"
422#include "mem/protocol/${ident}_State.hh"
423#include "mem/protocol/Types.hh"
424#include "mem/ruby/common/Global.hh"
425#include "mem/ruby/slicc_interface/RubySlicc_includes.hh"
426#include "mem/ruby/system/System.hh"
427
428using namespace std;
429''')
430
431        # include object classes
432        seen_types = set()
433        for var in self.objects:
434            if var.type.ident not in seen_types and not var.type.isPrimitive:
435                code('#include "mem/protocol/${{var.type.c_ident}}.hh"')
436            seen_types.add(var.type.ident)
437
438        code('''
439$c_ident *
440${c_ident}Params::create()
441{
442    return new $c_ident(this);
443}
444
445int $c_ident::m_num_controllers = 0;
446${ident}_ProfileDumper $c_ident::s_profileDumper;
447
448// for adding information to the protocol debug trace
449stringstream ${ident}_transitionComment;
450#define APPEND_TRANSITION_COMMENT(str) (${ident}_transitionComment << str)
451
452/** \\brief constructor */
453$c_ident::$c_ident(const Params *p)
454    : AbstractController(p)
455{
456    m_version = p->version;
457    m_transitions_per_cycle = p->transitions_per_cycle;
458    m_buffer_size = p->buffer_size;
459    m_recycle_latency = p->recycle_latency;
460    m_number_of_TBEs = p->number_of_TBEs;
461    m_is_blocking = false;
462''')
463        #
464        # max_port_rank is used to size vectors and thus should be one plus the
465        # largest port rank
466        #
467        max_port_rank = self.in_ports[0].pairs["max_port_rank"] + 1
468        code('    m_max_in_port_rank = $max_port_rank;')
469        code.indent()
470
471        #
472        # After initializing the universal machine parameters, initialize the
473        # this machines config parameters.  Also detemine if these configuration
474        # params include a sequencer.  This information will be used later for
475        # contecting the sequencer back to the L1 cache controller.
476        #
477        contains_dma_sequencer = False
478        sequencers = []
479        for param in self.config_parameters:
480            if param.name == "dma_sequencer":
481                contains_dma_sequencer = True
482            elif re.compile("sequencer").search(param.name):
483                sequencers.append(param.name)
484            if param.pointer:
485                code('m_${{param.name}}_ptr = p->${{param.name}};')
486            else:
487                code('m_${{param.name}} = p->${{param.name}};')
488
489        #
490        # For the l1 cache controller, add the special atomic support which
491        # includes passing the sequencer a pointer to the controller.
492        #
493        if self.ident == "L1Cache":
494            if not sequencers:
495                self.error("The L1Cache controller must include the sequencer " \
496                           "configuration parameter")
497
498            for seq in sequencers:
499                code('''
500m_${{seq}}_ptr->setController(this);
501    ''')
502        #
503        # For the DMA controller, pass the sequencer a pointer to the
504        # controller.
505        #
506        if self.ident == "DMA":
507            if not contains_dma_sequencer:
508                self.error("The DMA controller must include the sequencer " \
509                           "configuration parameter")
510
511            code('''
512m_dma_sequencer_ptr->setController(this);
513''')
514
515        code('m_num_controllers++;')
516        for var in self.objects:
517            if var.ident.find("mandatoryQueue") >= 0:
518                code('m_${{var.c_ident}}_ptr = new ${{var.type.c_ident}}();')
519
520        code.dedent()
521        code('''
522}
523
524void
525$c_ident::init()
526{
527    MachineType machine_type;
528    int base;
529
530    m_machineID.type = MachineType_${ident};
531    m_machineID.num = m_version;
532
533    // initialize objects
534    m_profiler.setVersion(m_version);
535    s_profileDumper.registerProfiler(&m_profiler);
536
537''')
538
539        code.indent()
540        for var in self.objects:
541            vtype = var.type
542            vid = "m_%s_ptr" % var.c_ident
543            if "network" not in var:
544                # Not a network port object
545                if "primitive" in vtype:
546                    code('$vid = new ${{vtype.c_ident}};')
547                    if "default" in var:
548                        code('(*$vid) = ${{var["default"]}};')
549                else:
550                    # Normal Object
551                    # added by SS
552                    if "factory" in var:
553                        code('$vid = ${{var["factory"]}};')
554                    elif var.ident.find("mandatoryQueue") < 0:
555                        th = var.get("template_hack", "")
556                        expr = "%s  = new %s%s" % (vid, vtype.c_ident, th)
557
558                        args = ""
559                        if "non_obj" not in vtype and not vtype.isEnumeration:
560                            if expr.find("TBETable") >= 0:
561                                args = "m_number_of_TBEs"
562                            else:
563                                args = var.get("constructor_hack", "")
564
565                        code('$expr($args);')
566
567                    code('assert($vid != NULL);')
568
569                    if "default" in var:
570                        code('*$vid = ${{var["default"]}}; // Object default')
571                    elif "default" in vtype:
572                        comment = "Type %s default" % vtype.ident
573                        code('*$vid = ${{vtype["default"]}}; // $comment')
574
575                    # Set ordering
576                    if "ordered" in var and "trigger_queue" not in var:
577                        # A buffer
578                        code('$vid->setOrdering(${{var["ordered"]}});')
579
580                    # Set randomization
581                    if "random" in var:
582                        # A buffer
583                        code('$vid->setRandomization(${{var["random"]}});')
584
585                    # Set Priority
586                    if vtype.isBuffer and \
587                           "rank" in var and "trigger_queue" not in var:
588                        code('$vid->setPriority(${{var["rank"]}});')
589
590            else:
591                # Network port object
592                network = var["network"]
593                ordered =  var["ordered"]
594                vnet = var["virtual_network"]
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);
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
865void
866$c_ident::set_permission(${{self.EntryType.c_ident}}*& m_cache_entry_ptr,
867                         AccessPermission perm)
868{
869    if (m_cache_entry_ptr != NULL) {
870       m_cache_entry_ptr->changePermission(perm);
871    }
872}
873''')
874
875        if self.TBEType != None:
876            code('''
877
878// Set and Reset for tbe variable
879void
880$c_ident::set_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.TBEType.c_ident}}* m_new_tbe)
881{
882  m_tbe_ptr = m_new_tbe;
883}
884
885void
886$c_ident::unset_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr)
887{
888  m_tbe_ptr = NULL;
889}
890''')
891
892        code('''
893
894// Actions
895''')
896        if self.TBEType != None and self.EntryType != None:
897            for action in self.actions.itervalues():
898                if "c_code" not in action:
899                 continue
900
901                code('''
902/** \\brief ${{action.desc}} */
903void
904$c_ident::${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr)
905{
906    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
907    ${{action["c_code"]}}
908}
909
910''')
911        elif self.TBEType != None:
912            for action in self.actions.itervalues():
913                if "c_code" not in action:
914                 continue
915
916                code('''
917/** \\brief ${{action.desc}} */
918void
919$c_ident::${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, const Address& addr)
920{
921    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
922    ${{action["c_code"]}}
923}
924
925''')
926        elif self.EntryType != None:
927            for action in self.actions.itervalues():
928                if "c_code" not in action:
929                 continue
930
931                code('''
932/** \\brief ${{action.desc}} */
933void
934$c_ident::${{action.ident}}(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr)
935{
936    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
937    ${{action["c_code"]}}
938}
939
940''')
941        else:
942            for action in self.actions.itervalues():
943                if "c_code" not in action:
944                 continue
945
946                code('''
947/** \\brief ${{action.desc}} */
948void
949$c_ident::${{action.ident}}(const Address& addr)
950{
951    DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
952    ${{action["c_code"]}}
953}
954
955''')
956        code.write(path, "%s.cc" % c_ident)
957
958    def printCWakeup(self, path):
959        '''Output the wakeup loop for the events'''
960
961        code = self.symtab.codeFormatter()
962        ident = self.ident
963
964        code('''
965// Auto generated C++ code started by $__file__:$__line__
966// ${ident}: ${{self.short}}
967
968#include <cassert>
969
970#include "base/misc.hh"
971#include "mem/protocol/${ident}_Controller.hh"
972#include "mem/protocol/${ident}_Event.hh"
973#include "mem/protocol/${ident}_State.hh"
974#include "mem/protocol/Types.hh"
975#include "mem/ruby/common/Global.hh"
976#include "mem/ruby/slicc_interface/RubySlicc_includes.hh"
977#include "mem/ruby/system/System.hh"
978
979using namespace std;
980
981void
982${ident}_Controller::wakeup()
983{
984    int counter = 0;
985    while (true) {
986        // Some cases will put us into an infinite loop without this limit
987        assert(counter <= m_transitions_per_cycle);
988        if (counter == m_transitions_per_cycle) {
989            // Count how often we are fully utilized
990            g_system_ptr->getProfiler()->controllerBusy(m_machineID);
991
992            // Wakeup in another cycle and try again
993            g_eventQueue_ptr->scheduleEvent(this, 1);
994            break;
995        }
996''')
997
998        code.indent()
999        code.indent()
1000
1001        # InPorts
1002        #
1003        for port in self.in_ports:
1004            code.indent()
1005            code('// ${ident}InPort $port')
1006            if port.pairs.has_key("rank"):
1007                code('m_cur_in_port_rank = ${{port.pairs["rank"]}};')
1008            else:
1009                code('m_cur_in_port_rank = 0;')
1010            code('${{port["c_code_in_port"]}}')
1011            code.dedent()
1012
1013            code('')
1014
1015        code.dedent()
1016        code.dedent()
1017        code('''
1018        break;  // If we got this far, we have nothing left todo
1019    }
1020    // g_eventQueue_ptr->scheduleEvent(this, 1);
1021}
1022''')
1023
1024        code.write(path, "%s_Wakeup.cc" % self.ident)
1025
1026    def printCSwitch(self, path):
1027        '''Output switch statement for transition table'''
1028
1029        code = self.symtab.codeFormatter()
1030        ident = self.ident
1031
1032        code('''
1033// Auto generated C++ code started by $__file__:$__line__
1034// ${ident}: ${{self.short}}
1035
1036#include <cassert>
1037
1038#include "base/misc.hh"
1039#include "base/trace.hh"
1040#include "mem/protocol/${ident}_Controller.hh"
1041#include "mem/protocol/${ident}_Event.hh"
1042#include "mem/protocol/${ident}_State.hh"
1043#include "mem/protocol/Types.hh"
1044#include "mem/ruby/common/Global.hh"
1045#include "mem/ruby/system/System.hh"
1046
1047#define HASH_FUN(state, event)  ((int(state)*${ident}_Event_NUM)+int(event))
1048
1049#define GET_TRANSITION_COMMENT() (${ident}_transitionComment.str())
1050#define CLEAR_TRANSITION_COMMENT() (${ident}_transitionComment.str(""))
1051
1052TransitionResult
1053${ident}_Controller::doTransition(${ident}_Event event,
1054''')
1055        if self.EntryType != None:
1056            code('''
1057                                  ${{self.EntryType.c_ident}}* m_cache_entry_ptr,
1058''')
1059        if self.TBEType != None:
1060            code('''
1061                                  ${{self.TBEType.c_ident}}* m_tbe_ptr,
1062''')
1063        code('''
1064                                  const Address &addr)
1065{
1066''')
1067        if self.TBEType != None and self.EntryType != None:
1068            code('${ident}_State state = ${ident}_getState(m_tbe_ptr, m_cache_entry_ptr, addr);')
1069        elif self.TBEType != None:
1070            code('${ident}_State state = ${ident}_getState(m_tbe_ptr, addr);')
1071        elif self.EntryType != None:
1072            code('${ident}_State state = ${ident}_getState(m_cache_entry_ptr, addr);')
1073        else:
1074            code('${ident}_State state = ${ident}_getState(addr);')
1075
1076        code('''
1077    ${ident}_State next_state = state;
1078
1079    DPRINTF(RubyGenerated, "%s, Time: %lld, state: %s, event: %s, addr: %s\\n",
1080            *this,
1081            g_eventQueue_ptr->getTime(),
1082            ${ident}_State_to_string(state),
1083            ${ident}_Event_to_string(event),
1084            addr);
1085
1086    TransitionResult result =
1087''')
1088        if self.TBEType != None and self.EntryType != None:
1089            code('doTransitionWorker(event, state, next_state, m_tbe_ptr, m_cache_entry_ptr, addr);')
1090        elif self.TBEType != None:
1091            code('doTransitionWorker(event, state, next_state, m_tbe_ptr, addr);')
1092        elif self.EntryType != None:
1093            code('doTransitionWorker(event, state, next_state, m_cache_entry_ptr, addr);')
1094        else:
1095            code('doTransitionWorker(event, state, next_state, addr);')
1096
1097        code('''
1098    if (result == TransitionResult_Valid) {
1099        DPRINTF(RubyGenerated, "next_state: %s\\n",
1100                ${ident}_State_to_string(next_state));
1101        m_profiler.countTransition(state, event);
1102        DPRINTFR(ProtocolTrace, "%7d %3s %10s%20s %6s>%-6s %s %s\\n",
1103            g_eventQueue_ptr->getTime(), m_version, "${ident}",
1104            ${ident}_Event_to_string(event),
1105            ${ident}_State_to_string(state),
1106            ${ident}_State_to_string(next_state),
1107            addr, GET_TRANSITION_COMMENT());
1108
1109        CLEAR_TRANSITION_COMMENT();
1110''')
1111        if self.TBEType != None and self.EntryType != None:
1112            code('${ident}_setState(m_tbe_ptr, m_cache_entry_ptr, addr, next_state);')
1113            code('set_permission(m_cache_entry_ptr, ${ident}_State_to_permission(next_state));')
1114        elif self.TBEType != None:
1115            code('${ident}_setState(m_tbe_ptr, addr, next_state);')
1116        elif self.EntryType != None:
1117            code('${ident}_setState(m_cache_entry_ptr, addr, next_state);')
1118            code('set_permission(m_cache_entry_ptr, ${ident}_State_to_permission(next_state));')
1119        else:
1120            code('${ident}_setState(addr, next_state);')
1121
1122        code('''
1123    } else if (result == TransitionResult_ResourceStall) {
1124        DPRINTFR(ProtocolTrace, "%7s %3s %10s%20s %6s>%-6s %s %s\\n",
1125            g_eventQueue_ptr->getTime(), m_version, "${ident}",
1126            ${ident}_Event_to_string(event),
1127            ${ident}_State_to_string(state),
1128            ${ident}_State_to_string(next_state),
1129            addr, "Resource Stall");
1130    } else if (result == TransitionResult_ProtocolStall) {
1131        DPRINTF(RubyGenerated, "stalling\\n");
1132        DPRINTFR(ProtocolTrace, "%7s %3s %10s%20s %6s>%-6s %s %s\\n",
1133            g_eventQueue_ptr->getTime(), m_version, "${ident}",
1134            ${ident}_Event_to_string(event),
1135            ${ident}_State_to_string(state),
1136            ${ident}_State_to_string(next_state),
1137            addr, "Protocol Stall");
1138    }
1139
1140    return result;
1141}
1142
1143TransitionResult
1144${ident}_Controller::doTransitionWorker(${ident}_Event event,
1145                                        ${ident}_State state,
1146                                        ${ident}_State& next_state,
1147''')
1148
1149        if self.TBEType != None:
1150            code('''
1151                                        ${{self.TBEType.c_ident}}*& m_tbe_ptr,
1152''')
1153        if self.EntryType != None:
1154                  code('''
1155                                        ${{self.EntryType.c_ident}}*& m_cache_entry_ptr,
1156''')
1157        code('''
1158                                        const Address& addr)
1159{
1160    switch(HASH_FUN(state, event)) {
1161''')
1162
1163        # This map will allow suppress generating duplicate code
1164        cases = orderdict()
1165
1166        for trans in self.transitions:
1167            case_string = "%s_State_%s, %s_Event_%s" % \
1168                (self.ident, trans.state.ident, self.ident, trans.event.ident)
1169
1170            case = self.symtab.codeFormatter()
1171            # Only set next_state if it changes
1172            if trans.state != trans.nextState:
1173                ns_ident = trans.nextState.ident
1174                case('next_state = ${ident}_State_${ns_ident};')
1175
1176            actions = trans.actions
1177
1178            # Check for resources
1179            case_sorter = []
1180            res = trans.resources
1181            for key,val in res.iteritems():
1182                if key.type.ident != "DNUCAStopTable":
1183                    val = '''
1184if (!%s.areNSlotsAvailable(%s))
1185    return TransitionResult_ResourceStall;
1186''' % (key.code, val)
1187                case_sorter.append(val)
1188
1189
1190            # Emit the code sequences in a sorted order.  This makes the
1191            # output deterministic (without this the output order can vary
1192            # since Map's keys() on a vector of pointers is not deterministic
1193            for c in sorted(case_sorter):
1194                case("$c")
1195
1196            # Figure out if we stall
1197            stall = False
1198            for action in actions:
1199                if action.ident == "z_stall":
1200                    stall = True
1201                    break
1202
1203            if stall:
1204                case('return TransitionResult_ProtocolStall;')
1205            else:
1206                if self.TBEType != None and self.EntryType != None:
1207                    for action in actions:
1208                        case('${{action.ident}}(m_tbe_ptr, m_cache_entry_ptr, addr);')
1209                elif self.TBEType != None:
1210                    for action in actions:
1211                        case('${{action.ident}}(m_tbe_ptr, addr);')
1212                elif self.EntryType != None:
1213                    for action in actions:
1214                        case('${{action.ident}}(m_cache_entry_ptr, addr);')
1215                else:
1216                    for action in actions:
1217                        case('${{action.ident}}(addr);')
1218                case('return TransitionResult_Valid;')
1219
1220            case = str(case)
1221
1222            # Look to see if this transition code is unique.
1223            if case not in cases:
1224                cases[case] = []
1225
1226            cases[case].append(case_string)
1227
1228        # Walk through all of the unique code blocks and spit out the
1229        # corresponding case statement elements
1230        for case,transitions in cases.iteritems():
1231            # Iterative over all the multiple transitions that share
1232            # the same code
1233            for trans in transitions:
1234                code('  case HASH_FUN($trans):')
1235            code('    $case')
1236
1237        code('''
1238      default:
1239        fatal("Invalid transition\\n"
1240              "%s time: %d addr: %s event: %s state: %s\\n",
1241              name(), g_eventQueue_ptr->getTime(), addr, event, state);
1242    }
1243    return TransitionResult_Valid;
1244}
1245''')
1246        code.write(path, "%s_Transitions.cc" % self.ident)
1247
1248    def printProfileDumperHH(self, path):
1249        code = self.symtab.codeFormatter()
1250        ident = self.ident
1251
1252        code('''
1253// Auto generated C++ code started by $__file__:$__line__
1254// ${ident}: ${{self.short}}
1255
1256#ifndef __${ident}_PROFILE_DUMPER_HH__
1257#define __${ident}_PROFILE_DUMPER_HH__
1258
1259#include <cassert>
1260#include <iostream>
1261#include <vector>
1262
1263#include "${ident}_Event.hh"
1264#include "${ident}_Profiler.hh"
1265
1266typedef std::vector<${ident}_Profiler *> ${ident}_profilers;
1267
1268class ${ident}_ProfileDumper
1269{
1270  public:
1271    ${ident}_ProfileDumper();
1272    void registerProfiler(${ident}_Profiler* profiler);
1273    void dumpStats(std::ostream& out) const;
1274
1275  private:
1276    ${ident}_profilers m_profilers;
1277};
1278
1279#endif // __${ident}_PROFILE_DUMPER_HH__
1280''')
1281        code.write(path, "%s_ProfileDumper.hh" % self.ident)
1282
1283    def printProfileDumperCC(self, path):
1284        code = self.symtab.codeFormatter()
1285        ident = self.ident
1286
1287        code('''
1288// Auto generated C++ code started by $__file__:$__line__
1289// ${ident}: ${{self.short}}
1290
1291#include "mem/protocol/${ident}_ProfileDumper.hh"
1292
1293${ident}_ProfileDumper::${ident}_ProfileDumper()
1294{
1295}
1296
1297void
1298${ident}_ProfileDumper::registerProfiler(${ident}_Profiler* profiler)
1299{
1300    m_profilers.push_back(profiler);
1301}
1302
1303void
1304${ident}_ProfileDumper::dumpStats(std::ostream& out) const
1305{
1306    out << " --- ${ident} ---\\n";
1307    out << " - Event Counts -\\n";
1308    for (${ident}_Event event = ${ident}_Event_FIRST;
1309         event < ${ident}_Event_NUM;
1310         ++event) {
1311        out << (${ident}_Event) event << " [";
1312        uint64 total = 0;
1313        for (int i = 0; i < m_profilers.size(); i++) {
1314             out << m_profilers[i]->getEventCount(event) << " ";
1315             total += m_profilers[i]->getEventCount(event);
1316        }
1317        out << "] " << total << "\\n";
1318    }
1319    out << "\\n";
1320    out << " - Transitions -\\n";
1321    for (${ident}_State state = ${ident}_State_FIRST;
1322         state < ${ident}_State_NUM;
1323         ++state) {
1324        for (${ident}_Event event = ${ident}_Event_FIRST;
1325             event < ${ident}_Event_NUM;
1326             ++event) {
1327            if (m_profilers[0]->isPossible(state, event)) {
1328                out << (${ident}_State) state << "  "
1329                    << (${ident}_Event) event << " [";
1330                uint64 total = 0;
1331                for (int i = 0; i < m_profilers.size(); i++) {
1332                     out << m_profilers[i]->getTransitionCount(state, event) << " ";
1333                     total += m_profilers[i]->getTransitionCount(state, event);
1334                }
1335                out << "] " << total << "\\n";
1336            }
1337        }
1338        out << "\\n";
1339    }
1340}
1341''')
1342        code.write(path, "%s_ProfileDumper.cc" % self.ident)
1343
1344    def printProfilerHH(self, path):
1345        code = self.symtab.codeFormatter()
1346        ident = self.ident
1347
1348        code('''
1349// Auto generated C++ code started by $__file__:$__line__
1350// ${ident}: ${{self.short}}
1351
1352#ifndef __${ident}_PROFILER_HH__
1353#define __${ident}_PROFILER_HH__
1354
1355#include <cassert>
1356#include <iostream>
1357
1358#include "mem/protocol/${ident}_Event.hh"
1359#include "mem/protocol/${ident}_State.hh"
1360#include "mem/ruby/common/Global.hh"
1361
1362class ${ident}_Profiler
1363{
1364  public:
1365    ${ident}_Profiler();
1366    void setVersion(int version);
1367    void countTransition(${ident}_State state, ${ident}_Event event);
1368    void possibleTransition(${ident}_State state, ${ident}_Event event);
1369    uint64 getEventCount(${ident}_Event event);
1370    bool isPossible(${ident}_State state, ${ident}_Event event);
1371    uint64 getTransitionCount(${ident}_State state, ${ident}_Event event);
1372    void clearStats();
1373
1374  private:
1375    int m_counters[${ident}_State_NUM][${ident}_Event_NUM];
1376    int m_event_counters[${ident}_Event_NUM];
1377    bool m_possible[${ident}_State_NUM][${ident}_Event_NUM];
1378    int m_version;
1379};
1380
1381#endif // __${ident}_PROFILER_HH__
1382''')
1383        code.write(path, "%s_Profiler.hh" % self.ident)
1384
1385    def printProfilerCC(self, path):
1386        code = self.symtab.codeFormatter()
1387        ident = self.ident
1388
1389        code('''
1390// Auto generated C++ code started by $__file__:$__line__
1391// ${ident}: ${{self.short}}
1392
1393#include <cassert>
1394
1395#include "mem/protocol/${ident}_Profiler.hh"
1396
1397${ident}_Profiler::${ident}_Profiler()
1398{
1399    for (int state = 0; state < ${ident}_State_NUM; state++) {
1400        for (int event = 0; event < ${ident}_Event_NUM; event++) {
1401            m_possible[state][event] = false;
1402            m_counters[state][event] = 0;
1403        }
1404    }
1405    for (int event = 0; event < ${ident}_Event_NUM; event++) {
1406        m_event_counters[event] = 0;
1407    }
1408}
1409
1410void
1411${ident}_Profiler::setVersion(int version)
1412{
1413    m_version = version;
1414}
1415
1416void
1417${ident}_Profiler::clearStats()
1418{
1419    for (int state = 0; state < ${ident}_State_NUM; state++) {
1420        for (int event = 0; event < ${ident}_Event_NUM; event++) {
1421            m_counters[state][event] = 0;
1422        }
1423    }
1424
1425    for (int event = 0; event < ${ident}_Event_NUM; event++) {
1426        m_event_counters[event] = 0;
1427    }
1428}
1429void
1430${ident}_Profiler::countTransition(${ident}_State state, ${ident}_Event event)
1431{
1432    assert(m_possible[state][event]);
1433    m_counters[state][event]++;
1434    m_event_counters[event]++;
1435}
1436void
1437${ident}_Profiler::possibleTransition(${ident}_State state,
1438                                      ${ident}_Event event)
1439{
1440    m_possible[state][event] = true;
1441}
1442
1443uint64
1444${ident}_Profiler::getEventCount(${ident}_Event event)
1445{
1446    return m_event_counters[event];
1447}
1448
1449bool
1450${ident}_Profiler::isPossible(${ident}_State state, ${ident}_Event event)
1451{
1452    return m_possible[state][event];
1453}
1454
1455uint64
1456${ident}_Profiler::getTransitionCount(${ident}_State state,
1457                                      ${ident}_Event event)
1458{
1459    return m_counters[state][event];
1460}
1461
1462''')
1463        code.write(path, "%s_Profiler.cc" % self.ident)
1464
1465    # **************************
1466    # ******* HTML Files *******
1467    # **************************
1468    def frameRef(self, click_href, click_target, over_href, over_num, text):
1469        code = self.symtab.codeFormatter(fix_newlines=False)
1470        code("""<A href=\"$click_href\" target=\"$click_target\" onmouseover=\"
1471    if (parent.frames[$over_num].location != parent.location + '$over_href') {
1472        parent.frames[$over_num].location='$over_href'
1473    }\">
1474    ${{html.formatShorthand(text)}}
1475    </A>""")
1476        return str(code)
1477
1478    def writeHTMLFiles(self, path):
1479        # Create table with no row hilighted
1480        self.printHTMLTransitions(path, None)
1481
1482        # Generate transition tables
1483        for state in self.states.itervalues():
1484            self.printHTMLTransitions(path, state)
1485
1486        # Generate action descriptions
1487        for action in self.actions.itervalues():
1488            name = "%s_action_%s.html" % (self.ident, action.ident)
1489            code = html.createSymbol(action, "Action")
1490            code.write(path, name)
1491
1492        # Generate state descriptions
1493        for state in self.states.itervalues():
1494            name = "%s_State_%s.html" % (self.ident, state.ident)
1495            code = html.createSymbol(state, "State")
1496            code.write(path, name)
1497
1498        # Generate event descriptions
1499        for event in self.events.itervalues():
1500            name = "%s_Event_%s.html" % (self.ident, event.ident)
1501            code = html.createSymbol(event, "Event")
1502            code.write(path, name)
1503
1504    def printHTMLTransitions(self, path, active_state):
1505        code = self.symtab.codeFormatter()
1506
1507        code('''
1508<HTML>
1509<BODY link="blue" vlink="blue">
1510
1511<H1 align="center">${{html.formatShorthand(self.short)}}:
1512''')
1513        code.indent()
1514        for i,machine in enumerate(self.symtab.getAllType(StateMachine)):
1515            mid = machine.ident
1516            if i != 0:
1517                extra = " - "
1518            else:
1519                extra = ""
1520            if machine == self:
1521                code('$extra$mid')
1522            else:
1523                code('$extra<A target="Table" href="${mid}_table.html">$mid</A>')
1524        code.dedent()
1525
1526        code("""
1527</H1>
1528
1529<TABLE border=1>
1530<TR>
1531  <TH> </TH>
1532""")
1533
1534        for event in self.events.itervalues():
1535            href = "%s_Event_%s.html" % (self.ident, event.ident)
1536            ref = self.frameRef(href, "Status", href, "1", event.short)
1537            code('<TH bgcolor=white>$ref</TH>')
1538
1539        code('</TR>')
1540        # -- Body of table
1541        for state in self.states.itervalues():
1542            # -- Each row
1543            if state == active_state:
1544                color = "yellow"
1545            else:
1546                color = "white"
1547
1548            click = "%s_table_%s.html" % (self.ident, state.ident)
1549            over = "%s_State_%s.html" % (self.ident, state.ident)
1550            text = html.formatShorthand(state.short)
1551            ref = self.frameRef(click, "Table", over, "1", state.short)
1552            code('''
1553<TR>
1554  <TH bgcolor=$color>$ref</TH>
1555''')
1556
1557            # -- One column for each event
1558            for event in self.events.itervalues():
1559                trans = self.table.get((state,event), None)
1560                if trans is None:
1561                    # This is the no transition case
1562                    if state == active_state:
1563                        color = "#C0C000"
1564                    else:
1565                        color = "lightgrey"
1566
1567                    code('<TD bgcolor=$color>&nbsp;</TD>')
1568                    continue
1569
1570                next = trans.nextState
1571                stall_action = False
1572
1573                # -- Get the actions
1574                for action in trans.actions:
1575                    if action.ident == "z_stall" or \
1576                       action.ident == "zz_recycleMandatoryQueue":
1577                        stall_action = True
1578
1579                # -- Print out "actions/next-state"
1580                if stall_action:
1581                    if state == active_state:
1582                        color = "#C0C000"
1583                    else:
1584                        color = "lightgrey"
1585
1586                elif active_state and next.ident == active_state.ident:
1587                    color = "aqua"
1588                elif state == active_state:
1589                    color = "yellow"
1590                else:
1591                    color = "white"
1592
1593                code('<TD bgcolor=$color>')
1594                for action in trans.actions:
1595                    href = "%s_action_%s.html" % (self.ident, action.ident)
1596                    ref = self.frameRef(href, "Status", href, "1",
1597                                        action.short)
1598                    code('  $ref')
1599                if next != state:
1600                    if trans.actions:
1601                        code('/')
1602                    click = "%s_table_%s.html" % (self.ident, next.ident)
1603                    over = "%s_State_%s.html" % (self.ident, next.ident)
1604                    ref = self.frameRef(click, "Table", over, "1", next.short)
1605                    code("$ref")
1606                code("</TD>")
1607
1608            # -- Each row
1609            if state == active_state:
1610                color = "yellow"
1611            else:
1612                color = "white"
1613
1614            click = "%s_table_%s.html" % (self.ident, state.ident)
1615            over = "%s_State_%s.html" % (self.ident, state.ident)
1616            ref = self.frameRef(click, "Table", over, "1", state.short)
1617            code('''
1618  <TH bgcolor=$color>$ref</TH>
1619</TR>
1620''')
1621        code('''
1622<!- Column footer->
1623<TR>
1624  <TH> </TH>
1625''')
1626
1627        for event in self.events.itervalues():
1628            href = "%s_Event_%s.html" % (self.ident, event.ident)
1629            ref = self.frameRef(href, "Status", href, "1", event.short)
1630            code('<TH bgcolor=white>$ref</TH>')
1631        code('''
1632</TR>
1633</TABLE>
1634</BODY></HTML>
1635''')
1636
1637
1638        if active_state:
1639            name = "%s_table_%s.html" % (self.ident, active_state.ident)
1640        else:
1641            name = "%s_table.html" % self.ident
1642        code.write(path, name)
1643
1644__all__ = [ "StateMachine" ]
1645