Deleted Added
sdiff udiff text old ( 11108:6342ddf6d733 ) new ( 11111:6da33e720481 )
full compact
1# Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
2# Copyright (c) 2009 The Hewlett-Packard Development Company
3# Copyright (c) 2013 Advanced Micro Devices, Inc.
4# All rights reserved.
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions are
8# met: redistributions of source code must retain the above copyright
9# notice, this list of conditions and the following disclaimer;
10# redistributions in binary form must reproduce the above copyright
11# notice, this list of conditions and the following disclaimer in the
12# documentation and/or other materials provided with the distribution;
13# neither the name of the copyright holders nor the names of its
14# contributors may be used to endorse or promote products derived from
15# this software without specific prior written permission.
16#
17# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29from m5.util import orderdict
30
31from slicc.symbols.Symbol import Symbol
32from slicc.symbols.Var import Var
33import slicc.generate.html as html
34import re
35
36python_class_map = {
37 "int": "Int",
38 "uint32_t" : "UInt32",
39 "std::string": "String",
40 "bool": "Bool",
41 "CacheMemory": "RubyCache",
42 "WireBuffer": "RubyWireBuffer",
43 "Sequencer": "RubySequencer",
44 "DirectoryMemory": "RubyDirectoryMemory",
45 "MemoryControl": "MemoryControl",
46 "MessageBuffer": "MessageBuffer",
47 "DMASequencer": "DMASequencer",
48 "Prefetcher":"Prefetcher",
49 "Cycles":"Cycles",
50 }
51
52class StateMachine(Symbol):
53 def __init__(self, symtab, ident, location, pairs, config_parameters):
54 super(StateMachine, self).__init__(symtab, ident, location, pairs)
55 self.table = None
56
57 # Data members in the State Machine that have been declared before
58 # the opening brace '{' of the machine. Note that these along with
59 # the members in self.objects form the entire set of data members.
60 self.config_parameters = config_parameters
61
62 self.prefetchers = []
63
64 for param in config_parameters:
65 if param.pointer:
66 var = Var(symtab, param.ident, location, param.type_ast.type,
67 "(*m_%s_ptr)" % param.ident, {}, self)
68 else:
69 var = Var(symtab, param.ident, location, param.type_ast.type,
70 "m_%s" % param.ident, {}, self)
71
72 self.symtab.registerSym(param.ident, var)
73
74 if str(param.type_ast.type) == "Prefetcher":
75 self.prefetchers.append(var)
76
77 self.states = orderdict()
78 self.events = orderdict()
79 self.actions = orderdict()
80 self.request_types = orderdict()
81 self.transitions = []
82 self.in_ports = []
83 self.functions = []
84
85 # Data members in the State Machine that have been declared inside
86 # the {} machine. Note that these along with the config params
87 # form the entire set of data members of the machine.
88 self.objects = []
89 self.TBEType = None
90 self.EntryType = None
91 self.debug_flags = set()
92 self.debug_flags.add('RubyGenerated')
93 self.debug_flags.add('RubySlicc')
94
95 def __repr__(self):
96 return "[StateMachine: %s]" % self.ident
97
98 def addState(self, state):
99 assert self.table is None
100 self.states[state.ident] = state
101
102 def addEvent(self, event):
103 assert self.table is None
104 self.events[event.ident] = event
105
106 def addAction(self, action):
107 assert self.table is None
108
109 # Check for duplicate action
110 for other in self.actions.itervalues():
111 if action.ident == other.ident:
112 action.warning("Duplicate action definition: %s" % action.ident)
113 action.error("Duplicate action definition: %s" % action.ident)
114 if action.short == other.short:
115 other.warning("Duplicate action shorthand: %s" % other.ident)
116 other.warning(" shorthand = %s" % other.short)
117 action.warning("Duplicate action shorthand: %s" % action.ident)
118 action.error(" shorthand = %s" % action.short)
119
120 self.actions[action.ident] = action
121
122 def addDebugFlag(self, flag):
123 self.debug_flags.add(flag)
124
125 def addRequestType(self, request_type):
126 assert self.table is None
127 self.request_types[request_type.ident] = request_type
128
129 def addTransition(self, trans):
130 assert self.table is None
131 self.transitions.append(trans)
132
133 def addInPort(self, var):
134 self.in_ports.append(var)
135
136 def addFunc(self, func):
137 # register func in the symbol table
138 self.symtab.registerSym(str(func), func)
139 self.functions.append(func)
140
141 def addObject(self, obj):
142 self.symtab.registerSym(str(obj), obj)
143 self.objects.append(obj)
144
145 def addType(self, type):
146 type_ident = '%s' % type.c_ident
147
148 if type_ident == "%s_TBE" %self.ident:
149 if self.TBEType != None:
150 self.error("Multiple Transaction Buffer types in a " \
151 "single machine.");
152 self.TBEType = type
153
154 elif "interface" in type and "AbstractCacheEntry" == type["interface"]:
155 if "main" in type and "false" == type["main"].lower():
156 pass # this isn't the EntryType
157 else:
158 if self.EntryType != None:
159 self.error("Multiple AbstractCacheEntry types in a " \
160 "single machine.");
161 self.EntryType = type
162
163 # Needs to be called before accessing the table
164 def buildTable(self):
165 assert self.table is None
166
167 table = {}
168
169 for trans in self.transitions:
170 # Track which actions we touch so we know if we use them
171 # all -- really this should be done for all symbols as
172 # part of the symbol table, then only trigger it for
173 # Actions, States, Events, etc.
174
175 for action in trans.actions:
176 action.used = True
177
178 index = (trans.state, trans.event)
179 if index in table:
180 table[index].warning("Duplicate transition: %s" % table[index])
181 trans.error("Duplicate transition: %s" % trans)
182 table[index] = trans
183
184 # Look at all actions to make sure we used them all
185 for action in self.actions.itervalues():
186 if not action.used:
187 error_msg = "Unused action: %s" % action.ident
188 if "desc" in action:
189 error_msg += ", " + action.desc
190 action.warning(error_msg)
191 self.table = table
192
193 # determine the port->msg buffer mappings
194 def getBufferMaps(self, ident):
195 msg_bufs = []
196 port_to_buf_map = {}
197 in_msg_bufs = {}
198 for port in self.in_ports:
199 buf_name = "m_%s_ptr" % port.pairs["buffer_expr"].name
200 msg_bufs.append(buf_name)
201 port_to_buf_map[port] = msg_bufs.index(buf_name)
202 if buf_name not in in_msg_bufs:
203 in_msg_bufs[buf_name] = [port]
204 else:
205 in_msg_bufs[buf_name].append(port)
206 return port_to_buf_map, in_msg_bufs, msg_bufs
207
208 def writeCodeFiles(self, path, includes):
209 self.printControllerPython(path)
210 self.printControllerHH(path)
211 self.printControllerCC(path, includes)
212 self.printCSwitch(path)
213 self.printCWakeup(path, includes)
214
215 def printControllerPython(self, path):
216 code = self.symtab.codeFormatter()
217 ident = self.ident
218
219 py_ident = "%s_Controller" % ident
220 c_ident = "%s_Controller" % self.ident
221
222 code('''
223from m5.params import *
224from m5.SimObject import SimObject
225from Controller import RubyController
226
227class $py_ident(RubyController):
228 type = '$py_ident'
229 cxx_header = 'mem/protocol/${c_ident}.hh'
230''')
231 code.indent()
232 for param in self.config_parameters:
233 dflt_str = ''
234
235 if param.rvalue is not None:
236 dflt_str = str(param.rvalue.inline()) + ', '
237
238 if python_class_map.has_key(param.type_ast.type.c_ident):
239 python_type = python_class_map[param.type_ast.type.c_ident]
240 code('${{param.ident}} = Param.${{python_type}}(${dflt_str}"")')
241
242 else:
243 self.error("Unknown c++ to python class conversion for c++ " \
244 "type: '%s'. Please update the python_class_map " \
245 "in StateMachine.py", param.type_ast.type.c_ident)
246
247 code.dedent()
248 code.write(path, '%s.py' % py_ident)
249
250
251 def printControllerHH(self, path):
252 '''Output the method declarations for the class declaration'''
253 code = self.symtab.codeFormatter()
254 ident = self.ident
255 c_ident = "%s_Controller" % self.ident
256
257 code('''
258/** \\file $c_ident.hh
259 *
260 * Auto generated C++ code started by $__file__:$__line__
261 * Created by slicc definition of Module "${{self.short}}"
262 */
263
264#ifndef __${ident}_CONTROLLER_HH__
265#define __${ident}_CONTROLLER_HH__
266
267#include <iostream>
268#include <sstream>
269#include <string>
270
271#include "mem/protocol/TransitionResult.hh"
272#include "mem/protocol/Types.hh"
273#include "mem/ruby/common/Consumer.hh"
274#include "mem/ruby/slicc_interface/AbstractController.hh"
275#include "params/$c_ident.hh"
276
277''')
278
279 seen_types = set()
280 for var in self.objects:
281 if var.type.ident not in seen_types and not var.type.isPrimitive:
282 code('#include "mem/protocol/${{var.type.c_ident}}.hh"')
283 seen_types.add(var.type.ident)
284
285 # for adding information to the protocol debug trace
286 code('''
287extern std::stringstream ${ident}_transitionComment;
288
289class $c_ident : public AbstractController
290{
291 public:
292 typedef ${c_ident}Params Params;
293 $c_ident(const Params *p);
294 static int getNumControllers();
295 void init();
296
297 MessageBuffer *getMandatoryQueue() const;
298 MessageBuffer *getMemoryQueue() const;
299 void initNetQueues();
300
301 void print(std::ostream& out) const;
302 void wakeup();
303 void resetStats();
304 void regStats();
305 void collateStats();
306
307 void recordCacheTrace(int cntrl, CacheRecorder* tr);
308 Sequencer* getSequencer() const;
309
310 int functionalWriteBuffers(PacketPtr&);
311
312 void countTransition(${ident}_State state, ${ident}_Event event);
313 void possibleTransition(${ident}_State state, ${ident}_Event event);
314 uint64_t getEventCount(${ident}_Event event);
315 bool isPossible(${ident}_State state, ${ident}_Event event);
316 uint64_t getTransitionCount(${ident}_State state, ${ident}_Event event);
317
318private:
319''')
320
321 code.indent()
322 # added by SS
323 for param in self.config_parameters:
324 if param.pointer:
325 code('${{param.type_ast.type}}* m_${{param.ident}}_ptr;')
326 else:
327 code('${{param.type_ast.type}} m_${{param.ident}};')
328
329 code('''
330TransitionResult doTransition(${ident}_Event event,
331''')
332
333 if self.EntryType != None:
334 code('''
335 ${{self.EntryType.c_ident}}* m_cache_entry_ptr,
336''')
337 if self.TBEType != None:
338 code('''
339 ${{self.TBEType.c_ident}}* m_tbe_ptr,
340''')
341
342 code('''
343 Addr addr);
344
345TransitionResult doTransitionWorker(${ident}_Event event,
346 ${ident}_State state,
347 ${ident}_State& next_state,
348''')
349
350 if self.TBEType != None:
351 code('''
352 ${{self.TBEType.c_ident}}*& m_tbe_ptr,
353''')
354 if self.EntryType != None:
355 code('''
356 ${{self.EntryType.c_ident}}*& m_cache_entry_ptr,
357''')
358
359 code('''
360 Addr addr);
361
362int m_counters[${ident}_State_NUM][${ident}_Event_NUM];
363int m_event_counters[${ident}_Event_NUM];
364bool m_possible[${ident}_State_NUM][${ident}_Event_NUM];
365
366static std::vector<Stats::Vector *> eventVec;
367static std::vector<std::vector<Stats::Vector *> > transVec;
368static int m_num_controllers;
369
370// Internal functions
371''')
372
373 for func in self.functions:
374 proto = func.prototype
375 if proto:
376 code('$proto')
377
378 if self.EntryType != None:
379 code('''
380
381// Set and Reset for cache_entry variable
382void set_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, AbstractCacheEntry* m_new_cache_entry);
383void unset_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr);
384''')
385
386 if self.TBEType != None:
387 code('''
388
389// Set and Reset for tbe variable
390void set_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${ident}_TBE* m_new_tbe);
391void unset_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr);
392''')
393
394 # Prototype the actions that the controller can take
395 code('''
396
397// Actions
398''')
399 if self.TBEType != None and self.EntryType != None:
400 for action in self.actions.itervalues():
401 code('/** \\brief ${{action.desc}} */')
402 code('void ${{action.ident}}(${{self.TBEType.c_ident}}*& '
403 'm_tbe_ptr, ${{self.EntryType.c_ident}}*& '
404 'm_cache_entry_ptr, Addr addr);')
405 elif self.TBEType != None:
406 for action in self.actions.itervalues():
407 code('/** \\brief ${{action.desc}} */')
408 code('void ${{action.ident}}(${{self.TBEType.c_ident}}*& '
409 'm_tbe_ptr, Addr addr);')
410 elif self.EntryType != None:
411 for action in self.actions.itervalues():
412 code('/** \\brief ${{action.desc}} */')
413 code('void ${{action.ident}}(${{self.EntryType.c_ident}}*& '
414 'm_cache_entry_ptr, Addr addr);')
415 else:
416 for action in self.actions.itervalues():
417 code('/** \\brief ${{action.desc}} */')
418 code('void ${{action.ident}}(Addr addr);')
419
420 # the controller internal variables
421 code('''
422
423// Objects
424''')
425 for var in self.objects:
426 th = var.get("template", "")
427 code('${{var.type.c_ident}}$th* m_${{var.ident}}_ptr;')
428
429 code.dedent()
430 code('};')
431 code('#endif // __${ident}_CONTROLLER_H__')
432 code.write(path, '%s.hh' % c_ident)
433
434 def printControllerCC(self, path, includes):
435 '''Output the actions for performing the actions'''
436
437 code = self.symtab.codeFormatter()
438 ident = self.ident
439 c_ident = "%s_Controller" % self.ident
440
441 code('''
442/** \\file $c_ident.cc
443 *
444 * Auto generated C++ code started by $__file__:$__line__
445 * Created by slicc definition of Module "${{self.short}}"
446 */
447
448#include <sys/types.h>
449#include <unistd.h>
450
451#include <cassert>
452#include <sstream>
453#include <string>
454#include <typeinfo>
455
456#include "base/compiler.hh"
457#include "base/cprintf.hh"
458
459''')
460 for f in self.debug_flags:
461 code('#include "debug/${{f}}.hh"')
462 code('''
463#include "mem/protocol/${ident}_Controller.hh"
464#include "mem/protocol/${ident}_Event.hh"
465#include "mem/protocol/${ident}_State.hh"
466#include "mem/protocol/Types.hh"
467#include "mem/ruby/system/RubySystem.hh"
468
469''')
470 for include_path in includes:
471 code('#include "${{include_path}}"')
472
473 code('''
474
475using namespace std;
476''')
477
478 # include object classes
479 seen_types = set()
480 for var in self.objects:
481 if var.type.ident not in seen_types and not var.type.isPrimitive:
482 code('#include "mem/protocol/${{var.type.c_ident}}.hh"')
483 seen_types.add(var.type.ident)
484
485 num_in_ports = len(self.in_ports)
486
487 code('''
488$c_ident *
489${c_ident}Params::create()
490{
491 return new $c_ident(this);
492}
493
494int $c_ident::m_num_controllers = 0;
495std::vector<Stats::Vector *> $c_ident::eventVec;
496std::vector<std::vector<Stats::Vector *> > $c_ident::transVec;
497
498// for adding information to the protocol debug trace
499stringstream ${ident}_transitionComment;
500
501#ifndef NDEBUG
502#define APPEND_TRANSITION_COMMENT(str) (${ident}_transitionComment << str)
503#else
504#define APPEND_TRANSITION_COMMENT(str) do {} while (0)
505#endif
506
507/** \\brief constructor */
508$c_ident::$c_ident(const Params *p)
509 : AbstractController(p)
510{
511 m_machineID.type = MachineType_${ident};
512 m_machineID.num = m_version;
513 m_num_controllers++;
514
515 m_in_ports = $num_in_ports;
516''')
517 code.indent()
518
519 #
520 # After initializing the universal machine parameters, initialize the
521 # this machines config parameters. Also if these configuration params
522 # include a sequencer, connect the it to the controller.
523 #
524 for param in self.config_parameters:
525 if param.pointer:
526 code('m_${{param.ident}}_ptr = p->${{param.ident}};')
527 else:
528 code('m_${{param.ident}} = p->${{param.ident}};')
529
530 if re.compile("sequencer").search(param.ident):
531 code('m_${{param.ident}}_ptr->setController(this);')
532
533 code('''
534
535for (int state = 0; state < ${ident}_State_NUM; state++) {
536 for (int event = 0; event < ${ident}_Event_NUM; event++) {
537 m_possible[state][event] = false;
538 m_counters[state][event] = 0;
539 }
540}
541for (int event = 0; event < ${ident}_Event_NUM; event++) {
542 m_event_counters[event] = 0;
543}
544''')
545 code.dedent()
546 code('''
547}
548
549void
550$c_ident::initNetQueues()
551{
552 MachineType machine_type = string_to_MachineType("${{self.ident}}");
553 int base M5_VAR_USED = MachineType_base_number(machine_type);
554
555''')
556 code.indent()
557
558 # set for maintaining the vnet, direction pairs already seen for this
559 # machine. This map helps in implementing the check for avoiding
560 # multiple message buffers being mapped to the same vnet.
561 vnet_dir_set = set()
562
563 for var in self.config_parameters:
564 vid = "m_%s_ptr" % var.ident
565 if "network" in var:
566 vtype = var.type_ast.type
567 code('assert($vid != NULL);')
568
569 # Network port object
570 network = var["network"]
571
572 if "virtual_network" in var:
573 vnet = var["virtual_network"]
574 vnet_type = var["vnet_type"]
575
576 assert (vnet, network) not in vnet_dir_set
577 vnet_dir_set.add((vnet,network))
578
579 code('''
580m_net_ptr->set${network}NetQueue(m_version + base, $vid->getOrdered(), $vnet,
581 "$vnet_type", $vid);
582''')
583 # Set Priority
584 if "rank" in var:
585 code('$vid->setPriority(${{var["rank"]}})')
586
587 code.dedent()
588 code('''
589}
590
591void
592$c_ident::init()
593{
594 // initialize objects
595 initNetQueues();
596''')
597
598 code.indent()
599
600 for var in self.objects:
601 vtype = var.type
602 vid = "m_%s_ptr" % var.ident
603 if "network" not in var:
604 # Not a network port object
605 if "primitive" in vtype:
606 code('$vid = new ${{vtype.c_ident}};')
607 if "default" in var:
608 code('(*$vid) = ${{var["default"]}};')
609 else:
610 # Normal Object
611 th = var.get("template", "")
612 expr = "%s = new %s%s" % (vid, vtype.c_ident, th)
613 args = ""
614 if "non_obj" not in vtype and not vtype.isEnumeration:
615 args = var.get("constructor", "")
616
617 code('$expr($args);')
618 code('assert($vid != NULL);')
619
620 if "default" in var:
621 code('*$vid = ${{var["default"]}}; // Object default')
622 elif "default" in vtype:
623 comment = "Type %s default" % vtype.ident
624 code('*$vid = ${{vtype["default"]}}; // $comment')
625
626 # Set the prefetchers
627 code()
628 for prefetcher in self.prefetchers:
629 code('${{prefetcher.code}}.setController(this);')
630
631 code()
632 for port in self.in_ports:
633 # Set the queue consumers
634 code('${{port.code}}.setConsumer(this);')
635
636 # Initialize the transition profiling
637 code()
638 for trans in self.transitions:
639 # Figure out if we stall
640 stall = False
641 for action in trans.actions:
642 if action.ident == "z_stall":
643 stall = True
644
645 # Only possible if it is not a 'z' case
646 if not stall:
647 state = "%s_State_%s" % (self.ident, trans.state.ident)
648 event = "%s_Event_%s" % (self.ident, trans.event.ident)
649 code('possibleTransition($state, $event);')
650
651 code.dedent()
652 code('''
653 AbstractController::init();
654 resetStats();
655}
656''')
657
658 mq_ident = "NULL"
659 for port in self.in_ports:
660 if port.code.find("mandatoryQueue_ptr") >= 0:
661 mq_ident = "m_mandatoryQueue_ptr"
662
663 memq_ident = "NULL"
664 for port in self.in_ports:
665 if port.code.find("responseFromMemory_ptr") >= 0:
666 memq_ident = "m_responseFromMemory_ptr"
667
668 seq_ident = "NULL"
669 for param in self.config_parameters:
670 if param.ident == "sequencer":
671 assert(param.pointer)
672 seq_ident = "m_%s_ptr" % param.ident
673
674 code('''
675
676void
677$c_ident::regStats()
678{
679 AbstractController::regStats();
680
681 if (m_version == 0) {
682 for (${ident}_Event event = ${ident}_Event_FIRST;
683 event < ${ident}_Event_NUM; ++event) {
684 Stats::Vector *t = new Stats::Vector();
685 t->init(m_num_controllers);
686 t->name(params()->ruby_system->name() + ".${c_ident}." +
687 ${ident}_Event_to_string(event));
688 t->flags(Stats::pdf | Stats::total | Stats::oneline |
689 Stats::nozero);
690
691 eventVec.push_back(t);
692 }
693
694 for (${ident}_State state = ${ident}_State_FIRST;
695 state < ${ident}_State_NUM; ++state) {
696
697 transVec.push_back(std::vector<Stats::Vector *>());
698
699 for (${ident}_Event event = ${ident}_Event_FIRST;
700 event < ${ident}_Event_NUM; ++event) {
701
702 Stats::Vector *t = new Stats::Vector();
703 t->init(m_num_controllers);
704 t->name(params()->ruby_system->name() + ".${c_ident}." +
705 ${ident}_State_to_string(state) +
706 "." + ${ident}_Event_to_string(event));
707
708 t->flags(Stats::pdf | Stats::total | Stats::oneline |
709 Stats::nozero);
710 transVec[state].push_back(t);
711 }
712 }
713 }
714}
715
716void
717$c_ident::collateStats()
718{
719 for (${ident}_Event event = ${ident}_Event_FIRST;
720 event < ${ident}_Event_NUM; ++event) {
721 for (unsigned int i = 0; i < m_num_controllers; ++i) {
722 RubySystem *rs = params()->ruby_system;
723 std::map<uint32_t, AbstractController *>::iterator it =
724 rs->m_abstract_controls[MachineType_${ident}].find(i);
725 assert(it != rs->m_abstract_controls[MachineType_${ident}].end());
726 (*eventVec[event])[i] =
727 (($c_ident *)(*it).second)->getEventCount(event);
728 }
729 }
730
731 for (${ident}_State state = ${ident}_State_FIRST;
732 state < ${ident}_State_NUM; ++state) {
733
734 for (${ident}_Event event = ${ident}_Event_FIRST;
735 event < ${ident}_Event_NUM; ++event) {
736
737 for (unsigned int i = 0; i < m_num_controllers; ++i) {
738 RubySystem *rs = params()->ruby_system;
739 std::map<uint32_t, AbstractController *>::iterator it =
740 rs->m_abstract_controls[MachineType_${ident}].find(i);
741 assert(it != rs->m_abstract_controls[MachineType_${ident}].end());
742 (*transVec[state][event])[i] =
743 (($c_ident *)(*it).second)->getTransitionCount(state, event);
744 }
745 }
746 }
747}
748
749void
750$c_ident::countTransition(${ident}_State state, ${ident}_Event event)
751{
752 assert(m_possible[state][event]);
753 m_counters[state][event]++;
754 m_event_counters[event]++;
755}
756void
757$c_ident::possibleTransition(${ident}_State state,
758 ${ident}_Event event)
759{
760 m_possible[state][event] = true;
761}
762
763uint64_t
764$c_ident::getEventCount(${ident}_Event event)
765{
766 return m_event_counters[event];
767}
768
769bool
770$c_ident::isPossible(${ident}_State state, ${ident}_Event event)
771{
772 return m_possible[state][event];
773}
774
775uint64_t
776$c_ident::getTransitionCount(${ident}_State state,
777 ${ident}_Event event)
778{
779 return m_counters[state][event];
780}
781
782int
783$c_ident::getNumControllers()
784{
785 return m_num_controllers;
786}
787
788MessageBuffer*
789$c_ident::getMandatoryQueue() const
790{
791 return $mq_ident;
792}
793
794MessageBuffer*
795$c_ident::getMemoryQueue() const
796{
797 return $memq_ident;
798}
799
800Sequencer*
801$c_ident::getSequencer() const
802{
803 return $seq_ident;
804}
805
806void
807$c_ident::print(ostream& out) const
808{
809 out << "[$c_ident " << m_version << "]";
810}
811
812void $c_ident::resetStats()
813{
814 for (int state = 0; state < ${ident}_State_NUM; state++) {
815 for (int event = 0; event < ${ident}_Event_NUM; event++) {
816 m_counters[state][event] = 0;
817 }
818 }
819
820 for (int event = 0; event < ${ident}_Event_NUM; event++) {
821 m_event_counters[event] = 0;
822 }
823
824 AbstractController::resetStats();
825}
826''')
827
828 if self.EntryType != None:
829 code('''
830
831// Set and Reset for cache_entry variable
832void
833$c_ident::set_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, AbstractCacheEntry* m_new_cache_entry)
834{
835 m_cache_entry_ptr = (${{self.EntryType.c_ident}}*)m_new_cache_entry;
836}
837
838void
839$c_ident::unset_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr)
840{
841 m_cache_entry_ptr = 0;
842}
843''')
844
845 if self.TBEType != None:
846 code('''
847
848// Set and Reset for tbe variable
849void
850$c_ident::set_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.TBEType.c_ident}}* m_new_tbe)
851{
852 m_tbe_ptr = m_new_tbe;
853}
854
855void
856$c_ident::unset_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr)
857{
858 m_tbe_ptr = NULL;
859}
860''')
861
862 code('''
863
864void
865$c_ident::recordCacheTrace(int cntrl, CacheRecorder* tr)
866{
867''')
868 #
869 # Record cache contents for all associated caches.
870 #
871 code.indent()
872 for param in self.config_parameters:
873 if param.type_ast.type.ident == "CacheMemory":
874 assert(param.pointer)
875 code('m_${{param.ident}}_ptr->recordCacheContents(cntrl, tr);')
876
877 code.dedent()
878 code('''
879}
880
881// Actions
882''')
883 if self.TBEType != None and self.EntryType != None:
884 for action in self.actions.itervalues():
885 if "c_code" not in action:
886 continue
887
888 code('''
889/** \\brief ${{action.desc}} */
890void
891$c_ident::${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.EntryType.c_ident}}*& m_cache_entry_ptr, Addr addr)
892{
893 DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
894 try {
895 ${{action["c_code"]}}
896 } catch (const RejectException & e) {
897 fatal("Error in action ${{ident}}:${{action.ident}}: "
898 "executed a peek statement with the wrong message "
899 "type specified. ");
900 }
901}
902
903''')
904 elif self.TBEType != None:
905 for action in self.actions.itervalues():
906 if "c_code" not in action:
907 continue
908
909 code('''
910/** \\brief ${{action.desc}} */
911void
912$c_ident::${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, Addr addr)
913{
914 DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
915 ${{action["c_code"]}}
916}
917
918''')
919 elif self.EntryType != None:
920 for action in self.actions.itervalues():
921 if "c_code" not in action:
922 continue
923
924 code('''
925/** \\brief ${{action.desc}} */
926void
927$c_ident::${{action.ident}}(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, Addr addr)
928{
929 DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
930 ${{action["c_code"]}}
931}
932
933''')
934 else:
935 for action in self.actions.itervalues():
936 if "c_code" not in action:
937 continue
938
939 code('''
940/** \\brief ${{action.desc}} */
941void
942$c_ident::${{action.ident}}(Addr addr)
943{
944 DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
945 ${{action["c_code"]}}
946}
947
948''')
949 for func in self.functions:
950 code(func.generateCode())
951
952 # Function for functional writes to messages buffered in the controller
953 code('''
954int
955$c_ident::functionalWriteBuffers(PacketPtr& pkt)
956{
957 int num_functional_writes = 0;
958''')
959 for var in self.objects:
960 vtype = var.type
961 if vtype.isBuffer:
962 vid = "m_%s_ptr" % var.ident
963 code('num_functional_writes += $vid->functionalWrite(pkt);')
964
965 for var in self.config_parameters:
966 vtype = var.type_ast.type
967 if vtype.isBuffer:
968 vid = "m_%s_ptr" % var.ident
969 code('num_functional_writes += $vid->functionalWrite(pkt);')
970
971 code('''
972 return num_functional_writes;
973}
974''')
975
976 code.write(path, "%s.cc" % c_ident)
977
978 def printCWakeup(self, path, includes):
979 '''Output the wakeup loop for the events'''
980
981 code = self.symtab.codeFormatter()
982 ident = self.ident
983
984 outputRequest_types = True
985 if len(self.request_types) == 0:
986 outputRequest_types = False
987
988 code('''
989// Auto generated C++ code started by $__file__:$__line__
990// ${ident}: ${{self.short}}
991
992#include <sys/types.h>
993#include <unistd.h>
994
995#include <cassert>
996#include <typeinfo>
997
998#include "base/misc.hh"
999
1000''')
1001 for f in self.debug_flags:
1002 code('#include "debug/${{f}}.hh"')
1003 code('''
1004#include "mem/protocol/${ident}_Controller.hh"
1005#include "mem/protocol/${ident}_Event.hh"
1006#include "mem/protocol/${ident}_State.hh"
1007
1008''')
1009
1010 if outputRequest_types:
1011 code('''#include "mem/protocol/${ident}_RequestType.hh"''')
1012
1013 code('''
1014#include "mem/protocol/Types.hh"
1015#include "mem/ruby/system/RubySystem.hh"
1016
1017''')
1018
1019
1020 for include_path in includes:
1021 code('#include "${{include_path}}"')
1022
1023 port_to_buf_map, in_msg_bufs, msg_bufs = self.getBufferMaps(ident)
1024
1025 code('''
1026
1027using namespace std;
1028
1029void
1030${ident}_Controller::wakeup()
1031{
1032 int counter = 0;
1033 while (true) {
1034 unsigned char rejected[${{len(msg_bufs)}}];
1035 memset(rejected, 0, sizeof(unsigned char)*${{len(msg_bufs)}});
1036 // Some cases will put us into an infinite loop without this limit
1037 assert(counter <= m_transitions_per_cycle);
1038 if (counter == m_transitions_per_cycle) {
1039 // Count how often we are fully utilized
1040 m_fully_busy_cycles++;
1041
1042 // Wakeup in another cycle and try again
1043 scheduleEvent(Cycles(1));
1044 break;
1045 }
1046''')
1047
1048 code.indent()
1049 code.indent()
1050
1051 # InPorts
1052 #
1053 for port in self.in_ports:
1054 code.indent()
1055 code('// ${ident}InPort $port')
1056 if port.pairs.has_key("rank"):
1057 code('m_cur_in_port = ${{port.pairs["rank"]}};')
1058 else:
1059 code('m_cur_in_port = 0;')
1060 if port in port_to_buf_map:
1061 code('try {')
1062 code.indent()
1063 code('${{port["c_code_in_port"]}}')
1064
1065 if port in port_to_buf_map:
1066 code.dedent()
1067 code('''
1068 } catch (const RejectException & e) {
1069 rejected[${{port_to_buf_map[port]}}]++;
1070 }
1071''')
1072 code.dedent()
1073 code('')
1074
1075 code.dedent()
1076 code.dedent()
1077 code('''
1078 // If we got this far, we have nothing left todo or something went
1079 // wrong''')
1080 for buf_name, ports in in_msg_bufs.items():
1081 if len(ports) > 1:
1082 # only produce checks when a buffer is shared by multiple ports
1083 code('''
1084 if (${{buf_name}}->isReady() && rejected[${{port_to_buf_map[ports[0]]}}] == ${{len(ports)}})
1085 {
1086 // no port claimed the message on the top of this buffer
1087 panic("Runtime Error at Ruby Time: %d. "
1088 "All ports rejected a message. "
1089 "You are probably sending a message type to this controller "
1090 "over a virtual network that do not define an in_port for "
1091 "the incoming message type.\\n",
1092 Cycles(1));
1093 }
1094''')
1095 code('''
1096 break;
1097 }
1098}
1099''')
1100
1101 code.write(path, "%s_Wakeup.cc" % self.ident)
1102
1103 def printCSwitch(self, path):
1104 '''Output switch statement for transition table'''
1105
1106 code = self.symtab.codeFormatter()
1107 ident = self.ident
1108
1109 code('''
1110// Auto generated C++ code started by $__file__:$__line__
1111// ${ident}: ${{self.short}}
1112
1113#include <cassert>
1114
1115#include "base/misc.hh"
1116#include "base/trace.hh"
1117#include "debug/ProtocolTrace.hh"
1118#include "debug/RubyGenerated.hh"
1119#include "mem/protocol/${ident}_Controller.hh"
1120#include "mem/protocol/${ident}_Event.hh"
1121#include "mem/protocol/${ident}_State.hh"
1122#include "mem/protocol/Types.hh"
1123#include "mem/ruby/system/RubySystem.hh"
1124
1125#define HASH_FUN(state, event) ((int(state)*${ident}_Event_NUM)+int(event))
1126
1127#define GET_TRANSITION_COMMENT() (${ident}_transitionComment.str())
1128#define CLEAR_TRANSITION_COMMENT() (${ident}_transitionComment.str(""))
1129
1130TransitionResult
1131${ident}_Controller::doTransition(${ident}_Event event,
1132''')
1133 if self.EntryType != None:
1134 code('''
1135 ${{self.EntryType.c_ident}}* m_cache_entry_ptr,
1136''')
1137 if self.TBEType != None:
1138 code('''
1139 ${{self.TBEType.c_ident}}* m_tbe_ptr,
1140''')
1141 code('''
1142 Addr addr)
1143{
1144''')
1145 code.indent()
1146
1147 if self.TBEType != None and self.EntryType != None:
1148 code('${ident}_State state = getState(m_tbe_ptr, m_cache_entry_ptr, addr);')
1149 elif self.TBEType != None:
1150 code('${ident}_State state = getState(m_tbe_ptr, addr);')
1151 elif self.EntryType != None:
1152 code('${ident}_State state = getState(m_cache_entry_ptr, addr);')
1153 else:
1154 code('${ident}_State state = getState(addr);')
1155
1156 code('''
1157${ident}_State next_state = state;
1158
1159DPRINTF(RubyGenerated, "%s, Time: %lld, state: %s, event: %s, addr: %s\\n",
1160 *this, curCycle(), ${ident}_State_to_string(state),
1161 ${ident}_Event_to_string(event), addr);
1162
1163TransitionResult result =
1164''')
1165 if self.TBEType != None and self.EntryType != None:
1166 code('doTransitionWorker(event, state, next_state, m_tbe_ptr, m_cache_entry_ptr, addr);')
1167 elif self.TBEType != None:
1168 code('doTransitionWorker(event, state, next_state, m_tbe_ptr, addr);')
1169 elif self.EntryType != None:
1170 code('doTransitionWorker(event, state, next_state, m_cache_entry_ptr, addr);')
1171 else:
1172 code('doTransitionWorker(event, state, next_state, addr);')
1173
1174 port_to_buf_map, in_msg_bufs, msg_bufs = self.getBufferMaps(ident)
1175
1176 code('''
1177
1178if (result == TransitionResult_Valid) {
1179 DPRINTF(RubyGenerated, "next_state: %s\\n",
1180 ${ident}_State_to_string(next_state));
1181 countTransition(state, event);
1182
1183 DPRINTFR(ProtocolTrace, "%15d %3s %10s%20s %6s>%-6s %#x %s\\n",
1184 curTick(), m_version, "${ident}",
1185 ${ident}_Event_to_string(event),
1186 ${ident}_State_to_string(state),
1187 ${ident}_State_to_string(next_state),
1188 addr, GET_TRANSITION_COMMENT());
1189
1190 CLEAR_TRANSITION_COMMENT();
1191''')
1192 if self.TBEType != None and self.EntryType != None:
1193 code('setState(m_tbe_ptr, m_cache_entry_ptr, addr, next_state);')
1194 code('setAccessPermission(m_cache_entry_ptr, addr, next_state);')
1195 elif self.TBEType != None:
1196 code('setState(m_tbe_ptr, addr, next_state);')
1197 code('setAccessPermission(addr, next_state);')
1198 elif self.EntryType != None:
1199 code('setState(m_cache_entry_ptr, addr, next_state);')
1200 code('setAccessPermission(m_cache_entry_ptr, addr, next_state);')
1201 else:
1202 code('setState(addr, next_state);')
1203 code('setAccessPermission(addr, next_state);')
1204
1205 code('''
1206} else if (result == TransitionResult_ResourceStall) {
1207 DPRINTFR(ProtocolTrace, "%15s %3s %10s%20s %6s>%-6s %#x %s\\n",
1208 curTick(), m_version, "${ident}",
1209 ${ident}_Event_to_string(event),
1210 ${ident}_State_to_string(state),
1211 ${ident}_State_to_string(next_state),
1212 addr, "Resource Stall");
1213} else if (result == TransitionResult_ProtocolStall) {
1214 DPRINTF(RubyGenerated, "stalling\\n");
1215 DPRINTFR(ProtocolTrace, "%15s %3s %10s%20s %6s>%-6s %#x %s\\n",
1216 curTick(), m_version, "${ident}",
1217 ${ident}_Event_to_string(event),
1218 ${ident}_State_to_string(state),
1219 ${ident}_State_to_string(next_state),
1220 addr, "Protocol Stall");
1221}
1222
1223return result;
1224''')
1225 code.dedent()
1226 code('''
1227}
1228
1229TransitionResult
1230${ident}_Controller::doTransitionWorker(${ident}_Event event,
1231 ${ident}_State state,
1232 ${ident}_State& next_state,
1233''')
1234
1235 if self.TBEType != None:
1236 code('''
1237 ${{self.TBEType.c_ident}}*& m_tbe_ptr,
1238''')
1239 if self.EntryType != None:
1240 code('''
1241 ${{self.EntryType.c_ident}}*& m_cache_entry_ptr,
1242''')
1243 code('''
1244 Addr addr)
1245{
1246 switch(HASH_FUN(state, event)) {
1247''')
1248
1249 # This map will allow suppress generating duplicate code
1250 cases = orderdict()
1251
1252 for trans in self.transitions:
1253 case_string = "%s_State_%s, %s_Event_%s" % \
1254 (self.ident, trans.state.ident, self.ident, trans.event.ident)
1255
1256 case = self.symtab.codeFormatter()
1257 # Only set next_state if it changes
1258 if trans.state != trans.nextState:
1259 if trans.nextState.isWildcard():
1260 # When * is encountered as an end state of a transition,
1261 # the next state is determined by calling the
1262 # machine-specific getNextState function. The next state
1263 # is determined before any actions of the transition
1264 # execute, and therefore the next state calculation cannot
1265 # depend on any of the transitionactions.
1266 case('next_state = getNextState(addr);')
1267 else:
1268 ns_ident = trans.nextState.ident
1269 case('next_state = ${ident}_State_${ns_ident};')
1270
1271 actions = trans.actions
1272 request_types = trans.request_types
1273
1274 # Check for resources
1275 case_sorter = []
1276 res = trans.resources
1277 for key,val in res.iteritems():
1278 val = '''
1279if (!%s.areNSlotsAvailable(%s, clockEdge()))
1280 return TransitionResult_ResourceStall;
1281''' % (key.code, val)
1282 case_sorter.append(val)
1283
1284 # Check all of the request_types for resource constraints
1285 for request_type in request_types:
1286 val = '''
1287if (!checkResourceAvailable(%s_RequestType_%s, addr)) {
1288 return TransitionResult_ResourceStall;
1289}
1290''' % (self.ident, request_type.ident)
1291 case_sorter.append(val)
1292
1293 # Emit the code sequences in a sorted order. This makes the
1294 # output deterministic (without this the output order can vary
1295 # since Map's keys() on a vector of pointers is not deterministic
1296 for c in sorted(case_sorter):
1297 case("$c")
1298
1299 # Record access types for this transition
1300 for request_type in request_types:
1301 case('recordRequestType(${ident}_RequestType_${{request_type.ident}}, addr);')
1302
1303 # Figure out if we stall
1304 stall = False
1305 for action in actions:
1306 if action.ident == "z_stall":
1307 stall = True
1308 break
1309
1310 if stall:
1311 case('return TransitionResult_ProtocolStall;')
1312 else:
1313 if self.TBEType != None and self.EntryType != None:
1314 for action in actions:
1315 case('${{action.ident}}(m_tbe_ptr, m_cache_entry_ptr, addr);')
1316 elif self.TBEType != None:
1317 for action in actions:
1318 case('${{action.ident}}(m_tbe_ptr, addr);')
1319 elif self.EntryType != None:
1320 for action in actions:
1321 case('${{action.ident}}(m_cache_entry_ptr, addr);')
1322 else:
1323 for action in actions:
1324 case('${{action.ident}}(addr);')
1325 case('return TransitionResult_Valid;')
1326
1327 case = str(case)
1328
1329 # Look to see if this transition code is unique.
1330 if case not in cases:
1331 cases[case] = []
1332
1333 cases[case].append(case_string)
1334
1335 # Walk through all of the unique code blocks and spit out the
1336 # corresponding case statement elements
1337 for case,transitions in cases.iteritems():
1338 # Iterative over all the multiple transitions that share
1339 # the same code
1340 for trans in transitions:
1341 code(' case HASH_FUN($trans):')
1342 code(' $case\n')
1343
1344 code('''
1345 default:
1346 panic("Invalid transition\\n"
1347 "%s time: %d addr: %s event: %s state: %s\\n",
1348 name(), curCycle(), addr, event, state);
1349 }
1350
1351 return TransitionResult_Valid;
1352}
1353''')
1354 code.write(path, "%s_Transitions.cc" % self.ident)
1355
1356
1357 # **************************
1358 # ******* HTML Files *******
1359 # **************************
1360 def frameRef(self, click_href, click_target, over_href, over_num, text):
1361 code = self.symtab.codeFormatter(fix_newlines=False)
1362 code("""<A href=\"$click_href\" target=\"$click_target\" onmouseover=\"
1363 if (parent.frames[$over_num].location != parent.location + '$over_href') {
1364 parent.frames[$over_num].location='$over_href'
1365 }\">
1366 ${{html.formatShorthand(text)}}
1367 </A>""")
1368 return str(code)
1369
1370 def writeHTMLFiles(self, path):
1371 # Create table with no row hilighted
1372 self.printHTMLTransitions(path, None)
1373
1374 # Generate transition tables
1375 for state in self.states.itervalues():
1376 self.printHTMLTransitions(path, state)
1377
1378 # Generate action descriptions
1379 for action in self.actions.itervalues():
1380 name = "%s_action_%s.html" % (self.ident, action.ident)
1381 code = html.createSymbol(action, "Action")
1382 code.write(path, name)
1383
1384 # Generate state descriptions
1385 for state in self.states.itervalues():
1386 name = "%s_State_%s.html" % (self.ident, state.ident)
1387 code = html.createSymbol(state, "State")
1388 code.write(path, name)
1389
1390 # Generate event descriptions
1391 for event in self.events.itervalues():
1392 name = "%s_Event_%s.html" % (self.ident, event.ident)
1393 code = html.createSymbol(event, "Event")
1394 code.write(path, name)
1395
1396 def printHTMLTransitions(self, path, active_state):
1397 code = self.symtab.codeFormatter()
1398
1399 code('''
1400<HTML>
1401<BODY link="blue" vlink="blue">
1402
1403<H1 align="center">${{html.formatShorthand(self.short)}}:
1404''')
1405 code.indent()
1406 for i,machine in enumerate(self.symtab.getAllType(StateMachine)):
1407 mid = machine.ident
1408 if i != 0:
1409 extra = " - "
1410 else:
1411 extra = ""
1412 if machine == self:
1413 code('$extra$mid')
1414 else:
1415 code('$extra<A target="Table" href="${mid}_table.html">$mid</A>')
1416 code.dedent()
1417
1418 code("""
1419</H1>
1420
1421<TABLE border=1>
1422<TR>
1423 <TH> </TH>
1424""")
1425
1426 for event in self.events.itervalues():
1427 href = "%s_Event_%s.html" % (self.ident, event.ident)
1428 ref = self.frameRef(href, "Status", href, "1", event.short)
1429 code('<TH bgcolor=white>$ref</TH>')
1430
1431 code('</TR>')
1432 # -- Body of table
1433 for state in self.states.itervalues():
1434 # -- Each row
1435 if state == active_state:
1436 color = "yellow"
1437 else:
1438 color = "white"
1439
1440 click = "%s_table_%s.html" % (self.ident, state.ident)
1441 over = "%s_State_%s.html" % (self.ident, state.ident)
1442 text = html.formatShorthand(state.short)
1443 ref = self.frameRef(click, "Table", over, "1", state.short)
1444 code('''
1445<TR>
1446 <TH bgcolor=$color>$ref</TH>
1447''')
1448
1449 # -- One column for each event
1450 for event in self.events.itervalues():
1451 trans = self.table.get((state,event), None)
1452 if trans is None:
1453 # This is the no transition case
1454 if state == active_state:
1455 color = "#C0C000"
1456 else:
1457 color = "lightgrey"
1458
1459 code('<TD bgcolor=$color>&nbsp;</TD>')
1460 continue
1461
1462 next = trans.nextState
1463 stall_action = False
1464
1465 # -- Get the actions
1466 for action in trans.actions:
1467 if action.ident == "z_stall" or \
1468 action.ident == "zz_recycleMandatoryQueue":
1469 stall_action = True
1470
1471 # -- Print out "actions/next-state"
1472 if stall_action:
1473 if state == active_state:
1474 color = "#C0C000"
1475 else:
1476 color = "lightgrey"
1477
1478 elif active_state and next.ident == active_state.ident:
1479 color = "aqua"
1480 elif state == active_state:
1481 color = "yellow"
1482 else:
1483 color = "white"
1484
1485 code('<TD bgcolor=$color>')
1486 for action in trans.actions:
1487 href = "%s_action_%s.html" % (self.ident, action.ident)
1488 ref = self.frameRef(href, "Status", href, "1",
1489 action.short)
1490 code(' $ref')
1491 if next != state:
1492 if trans.actions:
1493 code('/')
1494 click = "%s_table_%s.html" % (self.ident, next.ident)
1495 over = "%s_State_%s.html" % (self.ident, next.ident)
1496 ref = self.frameRef(click, "Table", over, "1", next.short)
1497 code("$ref")
1498 code("</TD>")
1499
1500 # -- Each row
1501 if state == active_state:
1502 color = "yellow"
1503 else:
1504 color = "white"
1505
1506 click = "%s_table_%s.html" % (self.ident, state.ident)
1507 over = "%s_State_%s.html" % (self.ident, state.ident)
1508 ref = self.frameRef(click, "Table", over, "1", state.short)
1509 code('''
1510 <TH bgcolor=$color>$ref</TH>
1511</TR>
1512''')
1513 code('''
1514<!- Column footer->
1515<TR>
1516 <TH> </TH>
1517''')
1518
1519 for event in self.events.itervalues():
1520 href = "%s_Event_%s.html" % (self.ident, event.ident)
1521 ref = self.frameRef(href, "Status", href, "1", event.short)
1522 code('<TH bgcolor=white>$ref</TH>')
1523 code('''
1524</TR>
1525</TABLE>
1526</BODY></HTML>
1527''')
1528
1529
1530 if active_state:
1531 name = "%s_table_%s.html" % (self.ident, active_state.ident)
1532 else:
1533 name = "%s_table.html" % self.ident
1534 code.write(path, name)
1535
1536__all__ = [ "StateMachine" ]