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