StateMachine.py (6888:de8e755aca4f) StateMachine.py (6902:b5baf1dc44b4)
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 code_formatter, orderdict
29
30from slicc.symbols.Symbol import Symbol
31from slicc.symbols.Var import Var
32import slicc.generate.html as html
33
34python_class_map = {"int": "Int",
35 "string": "String",
36 "bool": "Bool",
37 "CacheMemory": "RubyCache",
38 "Sequencer": "RubySequencer",
39 "DirectoryMemory": "RubyDirectoryMemory",
40 "MemoryControl": "RubyMemoryControl",
41 "DMASequencer": "DMASequencer"
42 }
43
44class StateMachine(Symbol):
45 def __init__(self, symtab, ident, location, pairs, config_parameters):
46 super(StateMachine, self).__init__(symtab, ident, location, pairs)
47 self.table = None
48 self.config_parameters = config_parameters
49 for param in config_parameters:
50 if param.pointer:
51 var = Var(symtab, param.name, location, param.type_ast.type,
52 "(*m_%s_ptr)" % param.name, {}, self)
53 else:
54 var = Var(symtab, param.name, location, param.type_ast.type,
55 "m_%s" % param.name, {}, self)
56 self.symtab.registerSym(param.name, var)
57
58 self.states = orderdict()
59 self.events = orderdict()
60 self.actions = orderdict()
61 self.transitions = []
62 self.in_ports = []
63 self.functions = []
64 self.objects = []
65
66 self.message_buffer_names = []
67
68 def __repr__(self):
69 return "[StateMachine: %s]" % self.ident
70
71 def addState(self, state):
72 assert self.table is None
73 self.states[state.ident] = state
74
75 def addEvent(self, event):
76 assert self.table is None
77 self.events[event.ident] = event
78
79 def addAction(self, action):
80 assert self.table is None
81
82 # Check for duplicate action
83 for other in self.actions.itervalues():
84 if action.ident == other.ident:
85 action.warning("Duplicate action definition: %s" % action.ident)
86 action.error("Duplicate action definition: %s" % action.ident)
87 if action.short == other.short:
88 other.warning("Duplicate action shorthand: %s" % other.ident)
89 other.warning(" shorthand = %s" % other.short)
90 action.warning("Duplicate action shorthand: %s" % action.ident)
91 action.error(" shorthand = %s" % action.short)
92
93 self.actions[action.ident] = action
94
95 def addTransition(self, trans):
96 assert self.table is None
97 self.transitions.append(trans)
98
99 def addInPort(self, var):
100 self.in_ports.append(var)
101
102 def addFunc(self, func):
103 # register func in the symbol table
104 self.symtab.registerSym(str(func), func)
105 self.functions.append(func)
106
107 def addObject(self, obj):
108 self.objects.append(obj)
109
110 # Needs to be called before accessing the table
111 def buildTable(self):
112 assert self.table is None
113
114 table = {}
115
116 for trans in self.transitions:
117 # Track which actions we touch so we know if we use them
118 # all -- really this should be done for all symbols as
119 # part of the symbol table, then only trigger it for
120 # Actions, States, Events, etc.
121
122 for action in trans.actions:
123 action.used = True
124
125 index = (trans.state, trans.event)
126 if index in table:
127 table[index].warning("Duplicate transition: %s" % table[index])
128 trans.error("Duplicate transition: %s" % trans)
129 table[index] = trans
130
131 # Look at all actions to make sure we used them all
132 for action in self.actions.itervalues():
133 if not action.used:
134 error_msg = "Unused action: %s" % action.ident
135 if "desc" in action:
136 error_msg += ", " + action.desc
137 action.warning(error_msg)
138 self.table = table
139
140 def writeCodeFiles(self, path):
141 self.printControllerPython(path)
142 self.printControllerHH(path)
143 self.printControllerCC(path)
144 self.printCSwitch(path)
145 self.printCWakeup(path)
146 self.printProfilerCC(path)
147 self.printProfilerHH(path)
148
149 for func in self.functions:
150 func.writeCodeFiles(path)
151
152 def printControllerPython(self, path):
153 code = code_formatter()
154 ident = self.ident
155 py_ident = "%s_Controller" % ident
156 c_ident = "%s_Controller" % self.ident
157 code('''
158from m5.params import *
159from m5.SimObject import SimObject
160from Controller import RubyController
161
162class $py_ident(RubyController):
163 type = '$py_ident'
164''')
165 code.indent()
166 for param in self.config_parameters:
167 dflt_str = ''
168 if param.default is not None:
169 dflt_str = str(param.default) + ', '
170 if python_class_map.has_key(param.type_ast.type.c_ident):
171 python_type = python_class_map[param.type_ast.type.c_ident]
172 code('${{param.name}} = Param.${{python_type}}(${dflt_str}"")')
173 else:
174 self.error("Unknown c++ to python class conversion for c++ " \
175 "type: '%s'. Please update the python_class_map " \
176 "in StateMachine.py", param.type_ast.type.c_ident)
177 code.dedent()
178 code.write(path, '%s.py' % py_ident)
179
180
181 def printControllerHH(self, path):
182 '''Output the method declarations for the class declaration'''
183 code = code_formatter()
184 ident = self.ident
185 c_ident = "%s_Controller" % self.ident
186
187 self.message_buffer_names = []
188
189 code('''
190/** \\file $ident.hh
191 *
192 * Auto generated C++ code started by $__file__:$__line__
193 * Created by slicc definition of Module "${{self.short}}"
194 */
195
196#ifndef ${ident}_CONTROLLER_H
197#define ${ident}_CONTROLLER_H
198
199#include "params/$c_ident.hh"
200
201#include "mem/ruby/common/Global.hh"
202#include "mem/ruby/common/Consumer.hh"
203#include "mem/ruby/slicc_interface/AbstractController.hh"
204#include "mem/protocol/TransitionResult.hh"
205#include "mem/protocol/Types.hh"
206#include "mem/protocol/${ident}_Profiler.hh"
207''')
208
209 seen_types = set()
210 for var in self.objects:
211 if var.type.ident not in seen_types and not var.type.isPrimitive:
212 code('#include "mem/protocol/${{var.type.c_ident}}.hh"')
213 seen_types.add(var.type.ident)
214
215 # for adding information to the protocol debug trace
216 code('''
217extern stringstream ${ident}_transitionComment;
218
219class $c_ident : public AbstractController {
220#ifdef CHECK_COHERENCE
221#endif /* CHECK_COHERENCE */
222public:
223 typedef ${c_ident}Params Params;
224 $c_ident(const Params *p);
225 static int getNumControllers();
226 void init();
227 MessageBuffer* getMandatoryQueue() const;
228 const int & getVersion() const;
229 const string toString() const;
230 const string getName() const;
231 const MachineType getMachineType() const;
232 void initNetworkPtr(Network* net_ptr) { m_net_ptr = net_ptr; }
233 void print(ostream& out) const;
234 void printConfig(ostream& out) const;
235 void wakeup();
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 code_formatter, orderdict
29
30from slicc.symbols.Symbol import Symbol
31from slicc.symbols.Var import Var
32import slicc.generate.html as html
33
34python_class_map = {"int": "Int",
35 "string": "String",
36 "bool": "Bool",
37 "CacheMemory": "RubyCache",
38 "Sequencer": "RubySequencer",
39 "DirectoryMemory": "RubyDirectoryMemory",
40 "MemoryControl": "RubyMemoryControl",
41 "DMASequencer": "DMASequencer"
42 }
43
44class StateMachine(Symbol):
45 def __init__(self, symtab, ident, location, pairs, config_parameters):
46 super(StateMachine, self).__init__(symtab, ident, location, pairs)
47 self.table = None
48 self.config_parameters = config_parameters
49 for param in config_parameters:
50 if param.pointer:
51 var = Var(symtab, param.name, location, param.type_ast.type,
52 "(*m_%s_ptr)" % param.name, {}, self)
53 else:
54 var = Var(symtab, param.name, location, param.type_ast.type,
55 "m_%s" % param.name, {}, self)
56 self.symtab.registerSym(param.name, var)
57
58 self.states = orderdict()
59 self.events = orderdict()
60 self.actions = orderdict()
61 self.transitions = []
62 self.in_ports = []
63 self.functions = []
64 self.objects = []
65
66 self.message_buffer_names = []
67
68 def __repr__(self):
69 return "[StateMachine: %s]" % self.ident
70
71 def addState(self, state):
72 assert self.table is None
73 self.states[state.ident] = state
74
75 def addEvent(self, event):
76 assert self.table is None
77 self.events[event.ident] = event
78
79 def addAction(self, action):
80 assert self.table is None
81
82 # Check for duplicate action
83 for other in self.actions.itervalues():
84 if action.ident == other.ident:
85 action.warning("Duplicate action definition: %s" % action.ident)
86 action.error("Duplicate action definition: %s" % action.ident)
87 if action.short == other.short:
88 other.warning("Duplicate action shorthand: %s" % other.ident)
89 other.warning(" shorthand = %s" % other.short)
90 action.warning("Duplicate action shorthand: %s" % action.ident)
91 action.error(" shorthand = %s" % action.short)
92
93 self.actions[action.ident] = action
94
95 def addTransition(self, trans):
96 assert self.table is None
97 self.transitions.append(trans)
98
99 def addInPort(self, var):
100 self.in_ports.append(var)
101
102 def addFunc(self, func):
103 # register func in the symbol table
104 self.symtab.registerSym(str(func), func)
105 self.functions.append(func)
106
107 def addObject(self, obj):
108 self.objects.append(obj)
109
110 # Needs to be called before accessing the table
111 def buildTable(self):
112 assert self.table is None
113
114 table = {}
115
116 for trans in self.transitions:
117 # Track which actions we touch so we know if we use them
118 # all -- really this should be done for all symbols as
119 # part of the symbol table, then only trigger it for
120 # Actions, States, Events, etc.
121
122 for action in trans.actions:
123 action.used = True
124
125 index = (trans.state, trans.event)
126 if index in table:
127 table[index].warning("Duplicate transition: %s" % table[index])
128 trans.error("Duplicate transition: %s" % trans)
129 table[index] = trans
130
131 # Look at all actions to make sure we used them all
132 for action in self.actions.itervalues():
133 if not action.used:
134 error_msg = "Unused action: %s" % action.ident
135 if "desc" in action:
136 error_msg += ", " + action.desc
137 action.warning(error_msg)
138 self.table = table
139
140 def writeCodeFiles(self, path):
141 self.printControllerPython(path)
142 self.printControllerHH(path)
143 self.printControllerCC(path)
144 self.printCSwitch(path)
145 self.printCWakeup(path)
146 self.printProfilerCC(path)
147 self.printProfilerHH(path)
148
149 for func in self.functions:
150 func.writeCodeFiles(path)
151
152 def printControllerPython(self, path):
153 code = code_formatter()
154 ident = self.ident
155 py_ident = "%s_Controller" % ident
156 c_ident = "%s_Controller" % self.ident
157 code('''
158from m5.params import *
159from m5.SimObject import SimObject
160from Controller import RubyController
161
162class $py_ident(RubyController):
163 type = '$py_ident'
164''')
165 code.indent()
166 for param in self.config_parameters:
167 dflt_str = ''
168 if param.default is not None:
169 dflt_str = str(param.default) + ', '
170 if python_class_map.has_key(param.type_ast.type.c_ident):
171 python_type = python_class_map[param.type_ast.type.c_ident]
172 code('${{param.name}} = Param.${{python_type}}(${dflt_str}"")')
173 else:
174 self.error("Unknown c++ to python class conversion for c++ " \
175 "type: '%s'. Please update the python_class_map " \
176 "in StateMachine.py", param.type_ast.type.c_ident)
177 code.dedent()
178 code.write(path, '%s.py' % py_ident)
179
180
181 def printControllerHH(self, path):
182 '''Output the method declarations for the class declaration'''
183 code = code_formatter()
184 ident = self.ident
185 c_ident = "%s_Controller" % self.ident
186
187 self.message_buffer_names = []
188
189 code('''
190/** \\file $ident.hh
191 *
192 * Auto generated C++ code started by $__file__:$__line__
193 * Created by slicc definition of Module "${{self.short}}"
194 */
195
196#ifndef ${ident}_CONTROLLER_H
197#define ${ident}_CONTROLLER_H
198
199#include "params/$c_ident.hh"
200
201#include "mem/ruby/common/Global.hh"
202#include "mem/ruby/common/Consumer.hh"
203#include "mem/ruby/slicc_interface/AbstractController.hh"
204#include "mem/protocol/TransitionResult.hh"
205#include "mem/protocol/Types.hh"
206#include "mem/protocol/${ident}_Profiler.hh"
207''')
208
209 seen_types = set()
210 for var in self.objects:
211 if var.type.ident not in seen_types and not var.type.isPrimitive:
212 code('#include "mem/protocol/${{var.type.c_ident}}.hh"')
213 seen_types.add(var.type.ident)
214
215 # for adding information to the protocol debug trace
216 code('''
217extern stringstream ${ident}_transitionComment;
218
219class $c_ident : public AbstractController {
220#ifdef CHECK_COHERENCE
221#endif /* CHECK_COHERENCE */
222public:
223 typedef ${c_ident}Params Params;
224 $c_ident(const Params *p);
225 static int getNumControllers();
226 void init();
227 MessageBuffer* getMandatoryQueue() const;
228 const int & getVersion() const;
229 const string toString() const;
230 const string getName() const;
231 const MachineType getMachineType() const;
232 void initNetworkPtr(Network* net_ptr) { m_net_ptr = net_ptr; }
233 void print(ostream& out) const;
234 void printConfig(ostream& out) const;
235 void wakeup();
236 void printStats(ostream& out) const { s_profiler.dumpStats(out); }
237 void clearStats() { s_profiler.clearStats(); }
236 void printStats(ostream& out) const;
237 void clearStats();
238 void blockOnQueue(Address addr, MessageBuffer* port);
239 void unblock(Address addr);
240private:
241''')
242
243 code.indent()
244 # added by SS
245 for param in self.config_parameters:
246 if param.pointer:
247 code('${{param.type_ast.type}}* m_${{param.ident}}_ptr;')
248 else:
249 code('${{param.type_ast.type}} m_${{param.ident}};')
250
251 code('''
252int m_number_of_TBEs;
253
254TransitionResult doTransition(${ident}_Event event, ${ident}_State state, const Address& addr); // in ${ident}_Transitions.cc
255TransitionResult doTransitionWorker(${ident}_Event event, ${ident}_State state, ${ident}_State& next_state, const Address& addr); // in ${ident}_Transitions.cc
256string m_name;
257int m_transitions_per_cycle;
258int m_buffer_size;
259int m_recycle_latency;
260map< string, string > m_cfg;
261NodeID m_version;
262Network* m_net_ptr;
263MachineID m_machineID;
264bool m_is_blocking;
265map< Address, MessageBuffer* > m_block_map;
266${ident}_Profiler s_profiler;
267static int m_num_controllers;
268// Internal functions
269''')
270
271 for func in self.functions:
272 proto = func.prototype
273 if proto:
274 code('$proto')
275
276 code('''
277
278// Actions
279''')
280 for action in self.actions.itervalues():
281 code('/** \\brief ${{action.desc}} */')
282 code('void ${{action.ident}}(const Address& addr);')
283
284 # the controller internal variables
285 code('''
286
287// Object
288''')
289 for var in self.objects:
290 th = var.get("template_hack", "")
291 code('${{var.type.c_ident}}$th* m_${{var.c_ident}}_ptr;')
292
293 if var.type.ident == "MessageBuffer":
294 self.message_buffer_names.append("m_%s_ptr" % var.c_ident)
295
296 code.dedent()
297 code('};')
298 code('#endif // ${ident}_CONTROLLER_H')
299 code.write(path, '%s.hh' % c_ident)
300
301 def printControllerCC(self, path):
302 '''Output the actions for performing the actions'''
303
304 code = code_formatter()
305 ident = self.ident
306 c_ident = "%s_Controller" % self.ident
307
308 code('''
309/** \\file $ident.cc
310 *
311 * Auto generated C++ code started by $__file__:$__line__
312 * Created by slicc definition of Module "${{self.short}}"
313 */
314
315#include "mem/ruby/common/Global.hh"
316#include "mem/ruby/slicc_interface/RubySlicc_includes.hh"
317#include "mem/protocol/${ident}_Controller.hh"
318#include "mem/protocol/${ident}_State.hh"
319#include "mem/protocol/${ident}_Event.hh"
320#include "mem/protocol/Types.hh"
321#include "mem/ruby/system/System.hh"
322''')
323
324 # include object classes
325 seen_types = set()
326 for var in self.objects:
327 if var.type.ident not in seen_types and not var.type.isPrimitive:
328 code('#include "mem/protocol/${{var.type.c_ident}}.hh"')
329 seen_types.add(var.type.ident)
330
331 code('''
332$c_ident *
333${c_ident}Params::create()
334{
335 return new $c_ident(this);
336}
337
338
339int $c_ident::m_num_controllers = 0;
340
341stringstream ${ident}_transitionComment;
342#define APPEND_TRANSITION_COMMENT(str) (${ident}_transitionComment << str)
343/** \\brief constructor */
344$c_ident::$c_ident(const Params *p)
345 : AbstractController(p)
346{
347 m_version = p->version;
348 m_transitions_per_cycle = p->transitions_per_cycle;
349 m_buffer_size = p->buffer_size;
350 m_recycle_latency = p->recycle_latency;
351 m_number_of_TBEs = p->number_of_TBEs;
352''')
353 code.indent()
354
355 #
356 # After initializing the universal machine parameters, initialize the
357 # this machines config parameters. Also detemine if these configuration
358 # params include a sequencer. This information will be used later for
359 # contecting the sequencer back to the L1 cache controller.
360 #
361 contains_sequencer = False
362 for param in self.config_parameters:
363 if param.name == "sequencer" or param.name == "dma_sequencer":
364 contains_sequencer = True
365 if param.pointer:
366 code('m_${{param.name}}_ptr = p->${{param.name}};')
367 else:
368 code('m_${{param.name}} = p->${{param.name}};')
369
370 #
371 # For the l1 cache controller, add the special atomic support which
372 # includes passing the sequencer a pointer to the controller.
373 #
374 if self.ident == "L1Cache":
375 if not contains_sequencer:
376 self.error("The L1Cache controller must include the sequencer " \
377 "configuration parameter")
378
379 code('''
380m_sequencer_ptr->setController(this);
381''')
382 #
383 # For the DMA controller, pass the sequencer a pointer to the
384 # controller.
385 #
386 if self.ident == "DMA":
387 if not contains_sequencer:
388 self.error("The DMA controller must include the sequencer " \
389 "configuration parameter")
390
391 code('''
392m_dma_sequencer_ptr->setController(this);
393''')
394
395 code('m_num_controllers++;')
396 for var in self.objects:
397 if var.ident.find("mandatoryQueue") >= 0:
398 code('m_${{var.c_ident}}_ptr = new ${{var.type.c_ident}}();')
399
400 code.dedent()
401 code('''
402}
403
404void $c_ident::init()
405{
406 m_machineID.type = MachineType_${ident};
407 m_machineID.num = m_version;
408
409 // Objects
410 s_profiler.setVersion(m_version);
411''')
412
413 code.indent()
414 for var in self.objects:
415 vtype = var.type
416 vid = "m_%s_ptr" % var.c_ident
417 if "network" not in var:
418 # Not a network port object
419 if "primitive" in vtype:
420 code('$vid = new ${{vtype.c_ident}};')
421 if "default" in var:
422 code('(*$vid) = ${{var["default"]}};')
423 else:
424 # Normal Object
425 # added by SS
426 if "factory" in var:
427 code('$vid = ${{var["factory"]}};')
428 elif var.ident.find("mandatoryQueue") < 0:
429 th = var.get("template_hack", "")
430 expr = "%s = new %s%s" % (vid, vtype.c_ident, th)
431
432 args = ""
433 if "non_obj" not in vtype and not vtype.isEnumeration:
434 if expr.find("TBETable") >= 0:
435 args = "m_number_of_TBEs"
436 else:
437 args = var.get("constructor_hack", "")
438 args = "(%s)" % args
439
440 code('$expr$args;')
441 else:
442 code(';')
443
444 code('assert($vid != NULL);')
445
446 if "default" in var:
447 code('(*$vid) = ${{var["default"]}}; // Object default')
448 elif "default" in vtype:
449 code('(*$vid) = ${{vtype["default"]}}; // Type ${{vtype.ident}} default')
450
451 # Set ordering
452 if "ordered" in var and "trigger_queue" not in var:
453 # A buffer
454 code('$vid->setOrdering(${{var["ordered"]}});')
455
456 # Set randomization
457 if "random" in var:
458 # A buffer
459 code('$vid->setRandomization(${{var["random"]}});')
460
461 # Set Priority
462 if vtype.isBuffer and \
463 "rank" in var and "trigger_queue" not in var:
464 code('$vid->setPriority(${{var["rank"]}});')
465 else:
466 # Network port object
467 network = var["network"]
468 ordered = var["ordered"]
469 vnet = var["virtual_network"]
470
471 assert var.machine is not None
472 code('''
473$vid = m_net_ptr->get${network}NetQueue(m_version+MachineType_base_number(string_to_MachineType("${{var.machine.ident}}")), $ordered, $vnet);
474''')
475
476 code('assert($vid != NULL);')
477
478 # Set ordering
479 if "ordered" in var:
480 # A buffer
481 code('$vid->setOrdering(${{var["ordered"]}});')
482
483 # Set randomization
484 if "random" in var:
485 # A buffer
486 code('$vid->setRandomization(${{var["random"]}})')
487
488 # Set Priority
489 if "rank" in var:
490 code('$vid->setPriority(${{var["rank"]}})')
491
492 # Set buffer size
493 if vtype.isBuffer:
494 code('''
495if (m_buffer_size > 0) {
496 $vid->setSize(m_buffer_size);
497}
498''')
499
500 # set description (may be overriden later by port def)
501 code('$vid->setDescription("[Version " + int_to_string(m_version) + ", ${ident}, name=${{var.c_ident}}]");')
502
503 # Set the queue consumers
504 code.insert_newline()
505 for port in self.in_ports:
506 code('${{port.code}}.setConsumer(this);')
507
508 # Set the queue descriptions
509 code.insert_newline()
510 for port in self.in_ports:
511 code('${{port.code}}.setDescription("[Version " + int_to_string(m_version) + ", $ident, $port]");')
512
513 # Initialize the transition profiling
514 code.insert_newline()
515 for trans in self.transitions:
516 # Figure out if we stall
517 stall = False
518 for action in trans.actions:
519 if action.ident == "z_stall":
520 stall = True
521
522 # Only possible if it is not a 'z' case
523 if not stall:
524 state = "%s_State_%s" % (self.ident, trans.state.ident)
525 event = "%s_Event_%s" % (self.ident, trans.event.ident)
526 code('s_profiler.possibleTransition($state, $event);')
527
528 # added by SS to initialize recycle_latency of message buffers
529 for buf in self.message_buffer_names:
530 code("$buf->setRecycleLatency(m_recycle_latency);")
531
532 code.dedent()
533 code('}')
534
535 has_mandatory_q = False
536 for port in self.in_ports:
537 if port.code.find("mandatoryQueue_ptr") >= 0:
538 has_mandatory_q = True
539
540 if has_mandatory_q:
541 mq_ident = "m_%s_mandatoryQueue_ptr" % self.ident
542 else:
543 mq_ident = "NULL"
544
545 code('''
546int $c_ident::getNumControllers() {
547 return m_num_controllers;
548}
549
550MessageBuffer* $c_ident::getMandatoryQueue() const {
551 return $mq_ident;
552}
553
554const int & $c_ident::getVersion() const{
555 return m_version;
556}
557
558const string $c_ident::toString() const{
559 return "$c_ident";
560}
561
562const string $c_ident::getName() const{
563 return m_name;
564}
565const MachineType $c_ident::getMachineType() const{
566 return MachineType_${ident};
567}
568
569void $c_ident::blockOnQueue(Address addr, MessageBuffer* port) {
570 m_is_blocking = true;
571 m_block_map[addr] = port;
572}
573void $c_ident::unblock(Address addr) {
574 m_block_map.erase(addr);
575 if (m_block_map.size() == 0) {
576 m_is_blocking = false;
577 }
578}
579
580void $c_ident::print(ostream& out) const { out << "[$c_ident " << m_version << "]"; }
581
582void $c_ident::printConfig(ostream& out) const {
583 out << "$c_ident config: " << m_name << endl;
584 out << " version: " << m_version << endl;
585 for (map<string, string>::const_iterator it = m_cfg.begin(); it != m_cfg.end(); it++) {
586 out << " " << (*it).first << ": " << (*it).second << endl;
587 }
588}
589
238 void blockOnQueue(Address addr, MessageBuffer* port);
239 void unblock(Address addr);
240private:
241''')
242
243 code.indent()
244 # added by SS
245 for param in self.config_parameters:
246 if param.pointer:
247 code('${{param.type_ast.type}}* m_${{param.ident}}_ptr;')
248 else:
249 code('${{param.type_ast.type}} m_${{param.ident}};')
250
251 code('''
252int m_number_of_TBEs;
253
254TransitionResult doTransition(${ident}_Event event, ${ident}_State state, const Address& addr); // in ${ident}_Transitions.cc
255TransitionResult doTransitionWorker(${ident}_Event event, ${ident}_State state, ${ident}_State& next_state, const Address& addr); // in ${ident}_Transitions.cc
256string m_name;
257int m_transitions_per_cycle;
258int m_buffer_size;
259int m_recycle_latency;
260map< string, string > m_cfg;
261NodeID m_version;
262Network* m_net_ptr;
263MachineID m_machineID;
264bool m_is_blocking;
265map< Address, MessageBuffer* > m_block_map;
266${ident}_Profiler s_profiler;
267static int m_num_controllers;
268// Internal functions
269''')
270
271 for func in self.functions:
272 proto = func.prototype
273 if proto:
274 code('$proto')
275
276 code('''
277
278// Actions
279''')
280 for action in self.actions.itervalues():
281 code('/** \\brief ${{action.desc}} */')
282 code('void ${{action.ident}}(const Address& addr);')
283
284 # the controller internal variables
285 code('''
286
287// Object
288''')
289 for var in self.objects:
290 th = var.get("template_hack", "")
291 code('${{var.type.c_ident}}$th* m_${{var.c_ident}}_ptr;')
292
293 if var.type.ident == "MessageBuffer":
294 self.message_buffer_names.append("m_%s_ptr" % var.c_ident)
295
296 code.dedent()
297 code('};')
298 code('#endif // ${ident}_CONTROLLER_H')
299 code.write(path, '%s.hh' % c_ident)
300
301 def printControllerCC(self, path):
302 '''Output the actions for performing the actions'''
303
304 code = code_formatter()
305 ident = self.ident
306 c_ident = "%s_Controller" % self.ident
307
308 code('''
309/** \\file $ident.cc
310 *
311 * Auto generated C++ code started by $__file__:$__line__
312 * Created by slicc definition of Module "${{self.short}}"
313 */
314
315#include "mem/ruby/common/Global.hh"
316#include "mem/ruby/slicc_interface/RubySlicc_includes.hh"
317#include "mem/protocol/${ident}_Controller.hh"
318#include "mem/protocol/${ident}_State.hh"
319#include "mem/protocol/${ident}_Event.hh"
320#include "mem/protocol/Types.hh"
321#include "mem/ruby/system/System.hh"
322''')
323
324 # include object classes
325 seen_types = set()
326 for var in self.objects:
327 if var.type.ident not in seen_types and not var.type.isPrimitive:
328 code('#include "mem/protocol/${{var.type.c_ident}}.hh"')
329 seen_types.add(var.type.ident)
330
331 code('''
332$c_ident *
333${c_ident}Params::create()
334{
335 return new $c_ident(this);
336}
337
338
339int $c_ident::m_num_controllers = 0;
340
341stringstream ${ident}_transitionComment;
342#define APPEND_TRANSITION_COMMENT(str) (${ident}_transitionComment << str)
343/** \\brief constructor */
344$c_ident::$c_ident(const Params *p)
345 : AbstractController(p)
346{
347 m_version = p->version;
348 m_transitions_per_cycle = p->transitions_per_cycle;
349 m_buffer_size = p->buffer_size;
350 m_recycle_latency = p->recycle_latency;
351 m_number_of_TBEs = p->number_of_TBEs;
352''')
353 code.indent()
354
355 #
356 # After initializing the universal machine parameters, initialize the
357 # this machines config parameters. Also detemine if these configuration
358 # params include a sequencer. This information will be used later for
359 # contecting the sequencer back to the L1 cache controller.
360 #
361 contains_sequencer = False
362 for param in self.config_parameters:
363 if param.name == "sequencer" or param.name == "dma_sequencer":
364 contains_sequencer = True
365 if param.pointer:
366 code('m_${{param.name}}_ptr = p->${{param.name}};')
367 else:
368 code('m_${{param.name}} = p->${{param.name}};')
369
370 #
371 # For the l1 cache controller, add the special atomic support which
372 # includes passing the sequencer a pointer to the controller.
373 #
374 if self.ident == "L1Cache":
375 if not contains_sequencer:
376 self.error("The L1Cache controller must include the sequencer " \
377 "configuration parameter")
378
379 code('''
380m_sequencer_ptr->setController(this);
381''')
382 #
383 # For the DMA controller, pass the sequencer a pointer to the
384 # controller.
385 #
386 if self.ident == "DMA":
387 if not contains_sequencer:
388 self.error("The DMA controller must include the sequencer " \
389 "configuration parameter")
390
391 code('''
392m_dma_sequencer_ptr->setController(this);
393''')
394
395 code('m_num_controllers++;')
396 for var in self.objects:
397 if var.ident.find("mandatoryQueue") >= 0:
398 code('m_${{var.c_ident}}_ptr = new ${{var.type.c_ident}}();')
399
400 code.dedent()
401 code('''
402}
403
404void $c_ident::init()
405{
406 m_machineID.type = MachineType_${ident};
407 m_machineID.num = m_version;
408
409 // Objects
410 s_profiler.setVersion(m_version);
411''')
412
413 code.indent()
414 for var in self.objects:
415 vtype = var.type
416 vid = "m_%s_ptr" % var.c_ident
417 if "network" not in var:
418 # Not a network port object
419 if "primitive" in vtype:
420 code('$vid = new ${{vtype.c_ident}};')
421 if "default" in var:
422 code('(*$vid) = ${{var["default"]}};')
423 else:
424 # Normal Object
425 # added by SS
426 if "factory" in var:
427 code('$vid = ${{var["factory"]}};')
428 elif var.ident.find("mandatoryQueue") < 0:
429 th = var.get("template_hack", "")
430 expr = "%s = new %s%s" % (vid, vtype.c_ident, th)
431
432 args = ""
433 if "non_obj" not in vtype and not vtype.isEnumeration:
434 if expr.find("TBETable") >= 0:
435 args = "m_number_of_TBEs"
436 else:
437 args = var.get("constructor_hack", "")
438 args = "(%s)" % args
439
440 code('$expr$args;')
441 else:
442 code(';')
443
444 code('assert($vid != NULL);')
445
446 if "default" in var:
447 code('(*$vid) = ${{var["default"]}}; // Object default')
448 elif "default" in vtype:
449 code('(*$vid) = ${{vtype["default"]}}; // Type ${{vtype.ident}} default')
450
451 # Set ordering
452 if "ordered" in var and "trigger_queue" not in var:
453 # A buffer
454 code('$vid->setOrdering(${{var["ordered"]}});')
455
456 # Set randomization
457 if "random" in var:
458 # A buffer
459 code('$vid->setRandomization(${{var["random"]}});')
460
461 # Set Priority
462 if vtype.isBuffer and \
463 "rank" in var and "trigger_queue" not in var:
464 code('$vid->setPriority(${{var["rank"]}});')
465 else:
466 # Network port object
467 network = var["network"]
468 ordered = var["ordered"]
469 vnet = var["virtual_network"]
470
471 assert var.machine is not None
472 code('''
473$vid = m_net_ptr->get${network}NetQueue(m_version+MachineType_base_number(string_to_MachineType("${{var.machine.ident}}")), $ordered, $vnet);
474''')
475
476 code('assert($vid != NULL);')
477
478 # Set ordering
479 if "ordered" in var:
480 # A buffer
481 code('$vid->setOrdering(${{var["ordered"]}});')
482
483 # Set randomization
484 if "random" in var:
485 # A buffer
486 code('$vid->setRandomization(${{var["random"]}})')
487
488 # Set Priority
489 if "rank" in var:
490 code('$vid->setPriority(${{var["rank"]}})')
491
492 # Set buffer size
493 if vtype.isBuffer:
494 code('''
495if (m_buffer_size > 0) {
496 $vid->setSize(m_buffer_size);
497}
498''')
499
500 # set description (may be overriden later by port def)
501 code('$vid->setDescription("[Version " + int_to_string(m_version) + ", ${ident}, name=${{var.c_ident}}]");')
502
503 # Set the queue consumers
504 code.insert_newline()
505 for port in self.in_ports:
506 code('${{port.code}}.setConsumer(this);')
507
508 # Set the queue descriptions
509 code.insert_newline()
510 for port in self.in_ports:
511 code('${{port.code}}.setDescription("[Version " + int_to_string(m_version) + ", $ident, $port]");')
512
513 # Initialize the transition profiling
514 code.insert_newline()
515 for trans in self.transitions:
516 # Figure out if we stall
517 stall = False
518 for action in trans.actions:
519 if action.ident == "z_stall":
520 stall = True
521
522 # Only possible if it is not a 'z' case
523 if not stall:
524 state = "%s_State_%s" % (self.ident, trans.state.ident)
525 event = "%s_Event_%s" % (self.ident, trans.event.ident)
526 code('s_profiler.possibleTransition($state, $event);')
527
528 # added by SS to initialize recycle_latency of message buffers
529 for buf in self.message_buffer_names:
530 code("$buf->setRecycleLatency(m_recycle_latency);")
531
532 code.dedent()
533 code('}')
534
535 has_mandatory_q = False
536 for port in self.in_ports:
537 if port.code.find("mandatoryQueue_ptr") >= 0:
538 has_mandatory_q = True
539
540 if has_mandatory_q:
541 mq_ident = "m_%s_mandatoryQueue_ptr" % self.ident
542 else:
543 mq_ident = "NULL"
544
545 code('''
546int $c_ident::getNumControllers() {
547 return m_num_controllers;
548}
549
550MessageBuffer* $c_ident::getMandatoryQueue() const {
551 return $mq_ident;
552}
553
554const int & $c_ident::getVersion() const{
555 return m_version;
556}
557
558const string $c_ident::toString() const{
559 return "$c_ident";
560}
561
562const string $c_ident::getName() const{
563 return m_name;
564}
565const MachineType $c_ident::getMachineType() const{
566 return MachineType_${ident};
567}
568
569void $c_ident::blockOnQueue(Address addr, MessageBuffer* port) {
570 m_is_blocking = true;
571 m_block_map[addr] = port;
572}
573void $c_ident::unblock(Address addr) {
574 m_block_map.erase(addr);
575 if (m_block_map.size() == 0) {
576 m_is_blocking = false;
577 }
578}
579
580void $c_ident::print(ostream& out) const { out << "[$c_ident " << m_version << "]"; }
581
582void $c_ident::printConfig(ostream& out) const {
583 out << "$c_ident config: " << m_name << endl;
584 out << " version: " << m_version << endl;
585 for (map<string, string>::const_iterator it = m_cfg.begin(); it != m_cfg.end(); it++) {
586 out << " " << (*it).first << ": " << (*it).second << endl;
587 }
588}
589
590void $c_ident::printStats(ostream& out) const {
591''')
592 #
593 # Cache and Memory Controllers have specific profilers associated with
594 # them. Print out these stats before dumping state transition stats.
595 #
596 for param in self.config_parameters:
597 if param.type_ast.type.ident == "CacheMemory" or \
598 param.type_ast.type.ident == "MemoryControl":
599 assert(param.pointer)
600 code(' m_${{param.ident}}_ptr->printStats(out);')
601
602 code('''
603 s_profiler.dumpStats(out);
604}
605
606void $c_ident::clearStats() {
607''')
608 #
609 # Cache and Memory Controllers have specific profilers associated with
610 # them. These stats must be cleared too.
611 #
612 for param in self.config_parameters:
613 if param.type_ast.type.ident == "CacheMemory" or \
614 param.type_ast.type.ident == "MemoryControl":
615 assert(param.pointer)
616 code(' m_${{param.ident}}_ptr->clearStats();')
617
618 code('''
619 s_profiler.clearStats();
620}
621
590// Actions
591''')
592
593 for action in self.actions.itervalues():
594 if "c_code" not in action:
595 continue
596
597 code('''
598/** \\brief ${{action.desc}} */
599void $c_ident::${{action.ident}}(const Address& addr)
600{
601 DEBUG_MSG(GENERATED_COMP, HighPrio, "executing");
602 ${{action["c_code"]}}
603}
604
605''')
606 code.write(path, "%s.cc" % c_ident)
607
608 def printCWakeup(self, path):
609 '''Output the wakeup loop for the events'''
610
611 code = code_formatter()
612 ident = self.ident
613
614 code('''
615// Auto generated C++ code started by $__file__:$__line__
616// ${ident}: ${{self.short}}
617
618#include "mem/ruby/common/Global.hh"
619#include "mem/ruby/slicc_interface/RubySlicc_includes.hh"
620#include "mem/protocol/${ident}_Controller.hh"
621#include "mem/protocol/${ident}_State.hh"
622#include "mem/protocol/${ident}_Event.hh"
623#include "mem/protocol/Types.hh"
624#include "mem/ruby/system/System.hh"
625
626void ${ident}_Controller::wakeup()
627{
628
629 int counter = 0;
630 while (true) {
631 // Some cases will put us into an infinite loop without this limit
632 assert(counter <= m_transitions_per_cycle);
633 if (counter == m_transitions_per_cycle) {
634 g_system_ptr->getProfiler()->controllerBusy(m_machineID); // Count how often we\'re fully utilized
635 g_eventQueue_ptr->scheduleEvent(this, 1); // Wakeup in another cycle and try again
636 break;
637 }
638''')
639
640 code.indent()
641 code.indent()
642
643 # InPorts
644 #
645 for port in self.in_ports:
646 code.indent()
647 code('// ${ident}InPort $port')
648 code('${{port["c_code_in_port"]}}')
649 code.dedent()
650
651 code('')
652
653 code.dedent()
654 code.dedent()
655 code('''
656 break; // If we got this far, we have nothing left todo
657 }
658}
659''')
660
661 code.write(path, "%s_Wakeup.cc" % self.ident)
662
663 def printCSwitch(self, path):
664 '''Output switch statement for transition table'''
665
666 code = code_formatter()
667 ident = self.ident
668
669 code('''
670// Auto generated C++ code started by $__file__:$__line__
671// ${ident}: ${{self.short}}
672
673#include "mem/ruby/common/Global.hh"
674#include "mem/protocol/${ident}_Controller.hh"
675#include "mem/protocol/${ident}_State.hh"
676#include "mem/protocol/${ident}_Event.hh"
677#include "mem/protocol/Types.hh"
678#include "mem/ruby/system/System.hh"
679
680#define HASH_FUN(state, event) ((int(state)*${ident}_Event_NUM)+int(event))
681
682#define GET_TRANSITION_COMMENT() (${ident}_transitionComment.str())
683#define CLEAR_TRANSITION_COMMENT() (${ident}_transitionComment.str(""))
684
685TransitionResult ${ident}_Controller::doTransition(${ident}_Event event, ${ident}_State state, const Address& addr
686)
687{
688 ${ident}_State next_state = state;
689
690 DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
691 DEBUG_MSG(GENERATED_COMP, MedPrio, *this);
692 DEBUG_EXPR(GENERATED_COMP, MedPrio, g_eventQueue_ptr->getTime());
693 DEBUG_EXPR(GENERATED_COMP, MedPrio,state);
694 DEBUG_EXPR(GENERATED_COMP, MedPrio,event);
695 DEBUG_EXPR(GENERATED_COMP, MedPrio,addr);
696
697 TransitionResult result = doTransitionWorker(event, state, next_state, addr);
698
699 if (result == TransitionResult_Valid) {
700 DEBUG_EXPR(GENERATED_COMP, MedPrio, next_state);
701 DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
702 s_profiler.countTransition(state, event);
703 if (Debug::getProtocolTrace()) {
704 g_system_ptr->getProfiler()->profileTransition("${ident}", m_version, addr,
705 ${ident}_State_to_string(state),
706 ${ident}_Event_to_string(event),
707 ${ident}_State_to_string(next_state), GET_TRANSITION_COMMENT());
708 }
709 CLEAR_TRANSITION_COMMENT();
710 ${ident}_setState(addr, next_state);
711
712 } else if (result == TransitionResult_ResourceStall) {
713 if (Debug::getProtocolTrace()) {
714 g_system_ptr->getProfiler()->profileTransition("${ident}", m_version, addr,
715 ${ident}_State_to_string(state),
716 ${ident}_Event_to_string(event),
717 ${ident}_State_to_string(next_state),
718 "Resource Stall");
719 }
720 } else if (result == TransitionResult_ProtocolStall) {
721 DEBUG_MSG(GENERATED_COMP, HighPrio, "stalling");
722 DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
723 if (Debug::getProtocolTrace()) {
724 g_system_ptr->getProfiler()->profileTransition("${ident}", m_version, addr,
725 ${ident}_State_to_string(state),
726 ${ident}_Event_to_string(event),
727 ${ident}_State_to_string(next_state),
728 "Protocol Stall");
729 }
730 }
731
732 return result;
733}
734
735TransitionResult ${ident}_Controller::doTransitionWorker(${ident}_Event event, ${ident}_State state, ${ident}_State& next_state, const Address& addr
736)
737{
738 switch(HASH_FUN(state, event)) {
739''')
740
741 # This map will allow suppress generating duplicate code
742 cases = orderdict()
743
744 for trans in self.transitions:
745 case_string = "%s_State_%s, %s_Event_%s" % \
746 (self.ident, trans.state.ident, self.ident, trans.event.ident)
747
748 case = code_formatter()
749 # Only set next_state if it changes
750 if trans.state != trans.nextState:
751 ns_ident = trans.nextState.ident
752 case('next_state = ${ident}_State_${ns_ident};')
753
754 actions = trans.actions
755
756 # Check for resources
757 case_sorter = []
758 res = trans.resources
759 for key,val in res.iteritems():
760 if key.type.ident != "DNUCAStopTable":
761 val = '''
762if (!%s.areNSlotsAvailable(%s)) {
763 return TransitionResult_ResourceStall;
764}
765''' % (key.code, val)
766 case_sorter.append(val)
767
768
769 # Emit the code sequences in a sorted order. This makes the
770 # output deterministic (without this the output order can vary
771 # since Map's keys() on a vector of pointers is not deterministic
772 for c in sorted(case_sorter):
773 case("$c")
774
775 # Figure out if we stall
776 stall = False
777 for action in actions:
778 if action.ident == "z_stall":
779 stall = True
780 break
781
782 if stall:
783 case('return TransitionResult_ProtocolStall;')
784 else:
785 for action in actions:
786 case('${{action.ident}}(addr);')
787 case('return TransitionResult_Valid;')
788
789 case = str(case)
790
791 # Look to see if this transition code is unique.
792 if case not in cases:
793 cases[case] = []
794
795 cases[case].append(case_string)
796
797 # Walk through all of the unique code blocks and spit out the
798 # corresponding case statement elements
799 for case,transitions in cases.iteritems():
800 # Iterative over all the multiple transitions that share
801 # the same code
802 for trans in transitions:
803 code(' case HASH_FUN($trans):')
804 code(' {')
805 code(' $case')
806 code(' }')
807
808 code('''
809 default:
810 WARN_EXPR(m_version);
811 WARN_EXPR(g_eventQueue_ptr->getTime());
812 WARN_EXPR(addr);
813 WARN_EXPR(event);
814 WARN_EXPR(state);
815 ERROR_MSG(\"Invalid transition\");
816 }
817 return TransitionResult_Valid;
818}
819''')
820 code.write(path, "%s_Transitions.cc" % self.ident)
821
822 def printProfilerHH(self, path):
823 code = code_formatter()
824 ident = self.ident
825
826 code('''
827// Auto generated C++ code started by $__file__:$__line__
828// ${ident}: ${{self.short}}
829
830#ifndef ${ident}_PROFILER_H
831#define ${ident}_PROFILER_H
832
833#include "mem/ruby/common/Global.hh"
834#include "mem/protocol/${ident}_State.hh"
835#include "mem/protocol/${ident}_Event.hh"
836
837class ${ident}_Profiler {
838 public:
839 ${ident}_Profiler();
840 void setVersion(int version);
841 void countTransition(${ident}_State state, ${ident}_Event event);
842 void possibleTransition(${ident}_State state, ${ident}_Event event);
843 void dumpStats(ostream& out) const;
844 void clearStats();
845
846 private:
847 int m_counters[${ident}_State_NUM][${ident}_Event_NUM];
848 int m_event_counters[${ident}_Event_NUM];
849 bool m_possible[${ident}_State_NUM][${ident}_Event_NUM];
850 int m_version;
851};
852
853#endif // ${ident}_PROFILER_H
854''')
855 code.write(path, "%s_Profiler.hh" % self.ident)
856
857 def printProfilerCC(self, path):
858 code = code_formatter()
859 ident = self.ident
860
861 code('''
862// Auto generated C++ code started by $__file__:$__line__
863// ${ident}: ${{self.short}}
864
865#include "mem/protocol/${ident}_Profiler.hh"
866
867${ident}_Profiler::${ident}_Profiler()
868{
869 for (int state = 0; state < ${ident}_State_NUM; state++) {
870 for (int event = 0; event < ${ident}_Event_NUM; event++) {
871 m_possible[state][event] = false;
872 m_counters[state][event] = 0;
873 }
874 }
875 for (int event = 0; event < ${ident}_Event_NUM; event++) {
876 m_event_counters[event] = 0;
877 }
878}
879void ${ident}_Profiler::setVersion(int version)
880{
881 m_version = version;
882}
883void ${ident}_Profiler::clearStats()
884{
885 for (int state = 0; state < ${ident}_State_NUM; state++) {
886 for (int event = 0; event < ${ident}_Event_NUM; event++) {
887 m_counters[state][event] = 0;
888 }
889 }
890
891 for (int event = 0; event < ${ident}_Event_NUM; event++) {
892 m_event_counters[event] = 0;
893 }
894}
895void ${ident}_Profiler::countTransition(${ident}_State state, ${ident}_Event event)
896{
897 assert(m_possible[state][event]);
898 m_counters[state][event]++;
899 m_event_counters[event]++;
900}
901void ${ident}_Profiler::possibleTransition(${ident}_State state, ${ident}_Event event)
902{
903 m_possible[state][event] = true;
904}
905void ${ident}_Profiler::dumpStats(ostream& out) const
906{
907 out << " --- ${ident} " << m_version << " ---" << endl;
908 out << " - Event Counts -" << endl;
909 for (int event = 0; event < ${ident}_Event_NUM; event++) {
910 int count = m_event_counters[event];
911 out << (${ident}_Event) event << " " << count << endl;
912 }
913 out << endl;
914 out << " - Transitions -" << endl;
915 for (int state = 0; state < ${ident}_State_NUM; state++) {
916 for (int event = 0; event < ${ident}_Event_NUM; event++) {
917 if (m_possible[state][event]) {
918 int count = m_counters[state][event];
919 out << (${ident}_State) state << " " << (${ident}_Event) event << " " << count;
920 if (count == 0) {
921 out << " <-- ";
922 }
923 out << endl;
924 }
925 }
926 out << endl;
927 }
928}
929''')
930 code.write(path, "%s_Profiler.cc" % self.ident)
931
932 # **************************
933 # ******* HTML Files *******
934 # **************************
935 def frameRef(self, click_href, click_target, over_href, over_target_num,
936 text):
937 code = code_formatter(fix_newlines=False)
938 code("""<A href=\"$click_href\" target=\"$click_target\" onMouseOver=\"if (parent.frames[$over_target_num].location != parent.location + '$over_href') { parent.frames[$over_target_num].location='$over_href' }\" >${{html.formatShorthand(text)}}</A>""")
939 return str(code)
940
941 def writeHTMLFiles(self, path):
942 # Create table with no row hilighted
943 self.printHTMLTransitions(path, None)
944
945 # Generate transition tables
946 for state in self.states.itervalues():
947 self.printHTMLTransitions(path, state)
948
949 # Generate action descriptions
950 for action in self.actions.itervalues():
951 name = "%s_action_%s.html" % (self.ident, action.ident)
952 code = html.createSymbol(action, "Action")
953 code.write(path, name)
954
955 # Generate state descriptions
956 for state in self.states.itervalues():
957 name = "%s_State_%s.html" % (self.ident, state.ident)
958 code = html.createSymbol(state, "State")
959 code.write(path, name)
960
961 # Generate event descriptions
962 for event in self.events.itervalues():
963 name = "%s_Event_%s.html" % (self.ident, event.ident)
964 code = html.createSymbol(event, "Event")
965 code.write(path, name)
966
967 def printHTMLTransitions(self, path, active_state):
968 code = code_formatter()
969
970 code('''
971<HTML><BODY link="blue" vlink="blue">
972
973<H1 align="center">${{html.formatShorthand(self.short)}}:
974''')
975 code.indent()
976 for i,machine in enumerate(self.symtab.getAllType(StateMachine)):
977 mid = machine.ident
978 if i != 0:
979 extra = " - "
980 else:
981 extra = ""
982 if machine == self:
983 code('$extra$mid')
984 else:
985 code('$extra<A target="Table" href="${mid}_table.html">$mid</A>')
986 code.dedent()
987
988 code("""
989</H1>
990
991<TABLE border=1>
992<TR>
993 <TH> </TH>
994""")
995
996 for event in self.events.itervalues():
997 href = "%s_Event_%s.html" % (self.ident, event.ident)
998 ref = self.frameRef(href, "Status", href, "1", event.short)
999 code('<TH bgcolor=white>$ref</TH>')
1000
1001 code('</TR>')
1002 # -- Body of table
1003 for state in self.states.itervalues():
1004 # -- Each row
1005 if state == active_state:
1006 color = "yellow"
1007 else:
1008 color = "white"
1009
1010 click = "%s_table_%s.html" % (self.ident, state.ident)
1011 over = "%s_State_%s.html" % (self.ident, state.ident)
1012 text = html.formatShorthand(state.short)
1013 ref = self.frameRef(click, "Table", over, "1", state.short)
1014 code('''
1015<TR>
1016 <TH bgcolor=$color>$ref</TH>
1017''')
1018
1019 # -- One column for each event
1020 for event in self.events.itervalues():
1021 trans = self.table.get((state,event), None)
1022 if trans is None:
1023 # This is the no transition case
1024 if state == active_state:
1025 color = "#C0C000"
1026 else:
1027 color = "lightgrey"
1028
1029 code('<TD bgcolor=$color>&nbsp;</TD>')
1030 continue
1031
1032 next = trans.nextState
1033 stall_action = False
1034
1035 # -- Get the actions
1036 for action in trans.actions:
1037 if action.ident == "z_stall" or \
1038 action.ident == "zz_recycleMandatoryQueue":
1039 stall_action = True
1040
1041 # -- Print out "actions/next-state"
1042 if stall_action:
1043 if state == active_state:
1044 color = "#C0C000"
1045 else:
1046 color = "lightgrey"
1047
1048 elif active_state and next.ident == active_state.ident:
1049 color = "aqua"
1050 elif state == active_state:
1051 color = "yellow"
1052 else:
1053 color = "white"
1054
1055 fix = code.nofix()
1056 code('<TD bgcolor=$color>')
1057 for action in trans.actions:
1058 href = "%s_action_%s.html" % (self.ident, action.ident)
1059 ref = self.frameRef(href, "Status", href, "1",
1060 action.short)
1061 code(' $ref\n')
1062 if next != state:
1063 if trans.actions:
1064 code('/')
1065 click = "%s_table_%s.html" % (self.ident, next.ident)
1066 over = "%s_State_%s.html" % (self.ident, next.ident)
1067 ref = self.frameRef(click, "Table", over, "1", next.short)
1068 code("$ref")
1069 code("</TD>\n")
1070 code.fix(fix)
1071
1072 # -- Each row
1073 if state == active_state:
1074 color = "yellow"
1075 else:
1076 color = "white"
1077
1078 click = "%s_table_%s.html" % (self.ident, state.ident)
1079 over = "%s_State_%s.html" % (self.ident, state.ident)
1080 ref = self.frameRef(click, "Table", over, "1", state.short)
1081 code('''
1082 <TH bgcolor=$color>$ref</TH>
1083</TR>
1084''')
1085 code('''
1086<TR>
1087 <TH> </TH>
1088''')
1089
1090 for event in self.events.itervalues():
1091 href = "%s_Event_%s.html" % (self.ident, event.ident)
1092 ref = self.frameRef(href, "Status", href, "1", event.short)
1093 code('<TH bgcolor=white>$ref</TH>')
1094 code('''
1095</TR>
1096</TABLE>
1097</BODY></HTML>
1098''')
1099
1100
1101 if active_state:
1102 name = "%s_table_%s.html" % (self.ident, active_state.ident)
1103 else:
1104 name = "%s_table.html" % self.ident
1105 code.write(path, name)
1106
1107__all__ = [ "StateMachine" ]
622// Actions
623''')
624
625 for action in self.actions.itervalues():
626 if "c_code" not in action:
627 continue
628
629 code('''
630/** \\brief ${{action.desc}} */
631void $c_ident::${{action.ident}}(const Address& addr)
632{
633 DEBUG_MSG(GENERATED_COMP, HighPrio, "executing");
634 ${{action["c_code"]}}
635}
636
637''')
638 code.write(path, "%s.cc" % c_ident)
639
640 def printCWakeup(self, path):
641 '''Output the wakeup loop for the events'''
642
643 code = code_formatter()
644 ident = self.ident
645
646 code('''
647// Auto generated C++ code started by $__file__:$__line__
648// ${ident}: ${{self.short}}
649
650#include "mem/ruby/common/Global.hh"
651#include "mem/ruby/slicc_interface/RubySlicc_includes.hh"
652#include "mem/protocol/${ident}_Controller.hh"
653#include "mem/protocol/${ident}_State.hh"
654#include "mem/protocol/${ident}_Event.hh"
655#include "mem/protocol/Types.hh"
656#include "mem/ruby/system/System.hh"
657
658void ${ident}_Controller::wakeup()
659{
660
661 int counter = 0;
662 while (true) {
663 // Some cases will put us into an infinite loop without this limit
664 assert(counter <= m_transitions_per_cycle);
665 if (counter == m_transitions_per_cycle) {
666 g_system_ptr->getProfiler()->controllerBusy(m_machineID); // Count how often we\'re fully utilized
667 g_eventQueue_ptr->scheduleEvent(this, 1); // Wakeup in another cycle and try again
668 break;
669 }
670''')
671
672 code.indent()
673 code.indent()
674
675 # InPorts
676 #
677 for port in self.in_ports:
678 code.indent()
679 code('// ${ident}InPort $port')
680 code('${{port["c_code_in_port"]}}')
681 code.dedent()
682
683 code('')
684
685 code.dedent()
686 code.dedent()
687 code('''
688 break; // If we got this far, we have nothing left todo
689 }
690}
691''')
692
693 code.write(path, "%s_Wakeup.cc" % self.ident)
694
695 def printCSwitch(self, path):
696 '''Output switch statement for transition table'''
697
698 code = code_formatter()
699 ident = self.ident
700
701 code('''
702// Auto generated C++ code started by $__file__:$__line__
703// ${ident}: ${{self.short}}
704
705#include "mem/ruby/common/Global.hh"
706#include "mem/protocol/${ident}_Controller.hh"
707#include "mem/protocol/${ident}_State.hh"
708#include "mem/protocol/${ident}_Event.hh"
709#include "mem/protocol/Types.hh"
710#include "mem/ruby/system/System.hh"
711
712#define HASH_FUN(state, event) ((int(state)*${ident}_Event_NUM)+int(event))
713
714#define GET_TRANSITION_COMMENT() (${ident}_transitionComment.str())
715#define CLEAR_TRANSITION_COMMENT() (${ident}_transitionComment.str(""))
716
717TransitionResult ${ident}_Controller::doTransition(${ident}_Event event, ${ident}_State state, const Address& addr
718)
719{
720 ${ident}_State next_state = state;
721
722 DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
723 DEBUG_MSG(GENERATED_COMP, MedPrio, *this);
724 DEBUG_EXPR(GENERATED_COMP, MedPrio, g_eventQueue_ptr->getTime());
725 DEBUG_EXPR(GENERATED_COMP, MedPrio,state);
726 DEBUG_EXPR(GENERATED_COMP, MedPrio,event);
727 DEBUG_EXPR(GENERATED_COMP, MedPrio,addr);
728
729 TransitionResult result = doTransitionWorker(event, state, next_state, addr);
730
731 if (result == TransitionResult_Valid) {
732 DEBUG_EXPR(GENERATED_COMP, MedPrio, next_state);
733 DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
734 s_profiler.countTransition(state, event);
735 if (Debug::getProtocolTrace()) {
736 g_system_ptr->getProfiler()->profileTransition("${ident}", m_version, addr,
737 ${ident}_State_to_string(state),
738 ${ident}_Event_to_string(event),
739 ${ident}_State_to_string(next_state), GET_TRANSITION_COMMENT());
740 }
741 CLEAR_TRANSITION_COMMENT();
742 ${ident}_setState(addr, next_state);
743
744 } else if (result == TransitionResult_ResourceStall) {
745 if (Debug::getProtocolTrace()) {
746 g_system_ptr->getProfiler()->profileTransition("${ident}", m_version, addr,
747 ${ident}_State_to_string(state),
748 ${ident}_Event_to_string(event),
749 ${ident}_State_to_string(next_state),
750 "Resource Stall");
751 }
752 } else if (result == TransitionResult_ProtocolStall) {
753 DEBUG_MSG(GENERATED_COMP, HighPrio, "stalling");
754 DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
755 if (Debug::getProtocolTrace()) {
756 g_system_ptr->getProfiler()->profileTransition("${ident}", m_version, addr,
757 ${ident}_State_to_string(state),
758 ${ident}_Event_to_string(event),
759 ${ident}_State_to_string(next_state),
760 "Protocol Stall");
761 }
762 }
763
764 return result;
765}
766
767TransitionResult ${ident}_Controller::doTransitionWorker(${ident}_Event event, ${ident}_State state, ${ident}_State& next_state, const Address& addr
768)
769{
770 switch(HASH_FUN(state, event)) {
771''')
772
773 # This map will allow suppress generating duplicate code
774 cases = orderdict()
775
776 for trans in self.transitions:
777 case_string = "%s_State_%s, %s_Event_%s" % \
778 (self.ident, trans.state.ident, self.ident, trans.event.ident)
779
780 case = code_formatter()
781 # Only set next_state if it changes
782 if trans.state != trans.nextState:
783 ns_ident = trans.nextState.ident
784 case('next_state = ${ident}_State_${ns_ident};')
785
786 actions = trans.actions
787
788 # Check for resources
789 case_sorter = []
790 res = trans.resources
791 for key,val in res.iteritems():
792 if key.type.ident != "DNUCAStopTable":
793 val = '''
794if (!%s.areNSlotsAvailable(%s)) {
795 return TransitionResult_ResourceStall;
796}
797''' % (key.code, val)
798 case_sorter.append(val)
799
800
801 # Emit the code sequences in a sorted order. This makes the
802 # output deterministic (without this the output order can vary
803 # since Map's keys() on a vector of pointers is not deterministic
804 for c in sorted(case_sorter):
805 case("$c")
806
807 # Figure out if we stall
808 stall = False
809 for action in actions:
810 if action.ident == "z_stall":
811 stall = True
812 break
813
814 if stall:
815 case('return TransitionResult_ProtocolStall;')
816 else:
817 for action in actions:
818 case('${{action.ident}}(addr);')
819 case('return TransitionResult_Valid;')
820
821 case = str(case)
822
823 # Look to see if this transition code is unique.
824 if case not in cases:
825 cases[case] = []
826
827 cases[case].append(case_string)
828
829 # Walk through all of the unique code blocks and spit out the
830 # corresponding case statement elements
831 for case,transitions in cases.iteritems():
832 # Iterative over all the multiple transitions that share
833 # the same code
834 for trans in transitions:
835 code(' case HASH_FUN($trans):')
836 code(' {')
837 code(' $case')
838 code(' }')
839
840 code('''
841 default:
842 WARN_EXPR(m_version);
843 WARN_EXPR(g_eventQueue_ptr->getTime());
844 WARN_EXPR(addr);
845 WARN_EXPR(event);
846 WARN_EXPR(state);
847 ERROR_MSG(\"Invalid transition\");
848 }
849 return TransitionResult_Valid;
850}
851''')
852 code.write(path, "%s_Transitions.cc" % self.ident)
853
854 def printProfilerHH(self, path):
855 code = code_formatter()
856 ident = self.ident
857
858 code('''
859// Auto generated C++ code started by $__file__:$__line__
860// ${ident}: ${{self.short}}
861
862#ifndef ${ident}_PROFILER_H
863#define ${ident}_PROFILER_H
864
865#include "mem/ruby/common/Global.hh"
866#include "mem/protocol/${ident}_State.hh"
867#include "mem/protocol/${ident}_Event.hh"
868
869class ${ident}_Profiler {
870 public:
871 ${ident}_Profiler();
872 void setVersion(int version);
873 void countTransition(${ident}_State state, ${ident}_Event event);
874 void possibleTransition(${ident}_State state, ${ident}_Event event);
875 void dumpStats(ostream& out) const;
876 void clearStats();
877
878 private:
879 int m_counters[${ident}_State_NUM][${ident}_Event_NUM];
880 int m_event_counters[${ident}_Event_NUM];
881 bool m_possible[${ident}_State_NUM][${ident}_Event_NUM];
882 int m_version;
883};
884
885#endif // ${ident}_PROFILER_H
886''')
887 code.write(path, "%s_Profiler.hh" % self.ident)
888
889 def printProfilerCC(self, path):
890 code = code_formatter()
891 ident = self.ident
892
893 code('''
894// Auto generated C++ code started by $__file__:$__line__
895// ${ident}: ${{self.short}}
896
897#include "mem/protocol/${ident}_Profiler.hh"
898
899${ident}_Profiler::${ident}_Profiler()
900{
901 for (int state = 0; state < ${ident}_State_NUM; state++) {
902 for (int event = 0; event < ${ident}_Event_NUM; event++) {
903 m_possible[state][event] = false;
904 m_counters[state][event] = 0;
905 }
906 }
907 for (int event = 0; event < ${ident}_Event_NUM; event++) {
908 m_event_counters[event] = 0;
909 }
910}
911void ${ident}_Profiler::setVersion(int version)
912{
913 m_version = version;
914}
915void ${ident}_Profiler::clearStats()
916{
917 for (int state = 0; state < ${ident}_State_NUM; state++) {
918 for (int event = 0; event < ${ident}_Event_NUM; event++) {
919 m_counters[state][event] = 0;
920 }
921 }
922
923 for (int event = 0; event < ${ident}_Event_NUM; event++) {
924 m_event_counters[event] = 0;
925 }
926}
927void ${ident}_Profiler::countTransition(${ident}_State state, ${ident}_Event event)
928{
929 assert(m_possible[state][event]);
930 m_counters[state][event]++;
931 m_event_counters[event]++;
932}
933void ${ident}_Profiler::possibleTransition(${ident}_State state, ${ident}_Event event)
934{
935 m_possible[state][event] = true;
936}
937void ${ident}_Profiler::dumpStats(ostream& out) const
938{
939 out << " --- ${ident} " << m_version << " ---" << endl;
940 out << " - Event Counts -" << endl;
941 for (int event = 0; event < ${ident}_Event_NUM; event++) {
942 int count = m_event_counters[event];
943 out << (${ident}_Event) event << " " << count << endl;
944 }
945 out << endl;
946 out << " - Transitions -" << endl;
947 for (int state = 0; state < ${ident}_State_NUM; state++) {
948 for (int event = 0; event < ${ident}_Event_NUM; event++) {
949 if (m_possible[state][event]) {
950 int count = m_counters[state][event];
951 out << (${ident}_State) state << " " << (${ident}_Event) event << " " << count;
952 if (count == 0) {
953 out << " <-- ";
954 }
955 out << endl;
956 }
957 }
958 out << endl;
959 }
960}
961''')
962 code.write(path, "%s_Profiler.cc" % self.ident)
963
964 # **************************
965 # ******* HTML Files *******
966 # **************************
967 def frameRef(self, click_href, click_target, over_href, over_target_num,
968 text):
969 code = code_formatter(fix_newlines=False)
970 code("""<A href=\"$click_href\" target=\"$click_target\" onMouseOver=\"if (parent.frames[$over_target_num].location != parent.location + '$over_href') { parent.frames[$over_target_num].location='$over_href' }\" >${{html.formatShorthand(text)}}</A>""")
971 return str(code)
972
973 def writeHTMLFiles(self, path):
974 # Create table with no row hilighted
975 self.printHTMLTransitions(path, None)
976
977 # Generate transition tables
978 for state in self.states.itervalues():
979 self.printHTMLTransitions(path, state)
980
981 # Generate action descriptions
982 for action in self.actions.itervalues():
983 name = "%s_action_%s.html" % (self.ident, action.ident)
984 code = html.createSymbol(action, "Action")
985 code.write(path, name)
986
987 # Generate state descriptions
988 for state in self.states.itervalues():
989 name = "%s_State_%s.html" % (self.ident, state.ident)
990 code = html.createSymbol(state, "State")
991 code.write(path, name)
992
993 # Generate event descriptions
994 for event in self.events.itervalues():
995 name = "%s_Event_%s.html" % (self.ident, event.ident)
996 code = html.createSymbol(event, "Event")
997 code.write(path, name)
998
999 def printHTMLTransitions(self, path, active_state):
1000 code = code_formatter()
1001
1002 code('''
1003<HTML><BODY link="blue" vlink="blue">
1004
1005<H1 align="center">${{html.formatShorthand(self.short)}}:
1006''')
1007 code.indent()
1008 for i,machine in enumerate(self.symtab.getAllType(StateMachine)):
1009 mid = machine.ident
1010 if i != 0:
1011 extra = " - "
1012 else:
1013 extra = ""
1014 if machine == self:
1015 code('$extra$mid')
1016 else:
1017 code('$extra<A target="Table" href="${mid}_table.html">$mid</A>')
1018 code.dedent()
1019
1020 code("""
1021</H1>
1022
1023<TABLE border=1>
1024<TR>
1025 <TH> </TH>
1026""")
1027
1028 for event in self.events.itervalues():
1029 href = "%s_Event_%s.html" % (self.ident, event.ident)
1030 ref = self.frameRef(href, "Status", href, "1", event.short)
1031 code('<TH bgcolor=white>$ref</TH>')
1032
1033 code('</TR>')
1034 # -- Body of table
1035 for state in self.states.itervalues():
1036 # -- Each row
1037 if state == active_state:
1038 color = "yellow"
1039 else:
1040 color = "white"
1041
1042 click = "%s_table_%s.html" % (self.ident, state.ident)
1043 over = "%s_State_%s.html" % (self.ident, state.ident)
1044 text = html.formatShorthand(state.short)
1045 ref = self.frameRef(click, "Table", over, "1", state.short)
1046 code('''
1047<TR>
1048 <TH bgcolor=$color>$ref</TH>
1049''')
1050
1051 # -- One column for each event
1052 for event in self.events.itervalues():
1053 trans = self.table.get((state,event), None)
1054 if trans is None:
1055 # This is the no transition case
1056 if state == active_state:
1057 color = "#C0C000"
1058 else:
1059 color = "lightgrey"
1060
1061 code('<TD bgcolor=$color>&nbsp;</TD>')
1062 continue
1063
1064 next = trans.nextState
1065 stall_action = False
1066
1067 # -- Get the actions
1068 for action in trans.actions:
1069 if action.ident == "z_stall" or \
1070 action.ident == "zz_recycleMandatoryQueue":
1071 stall_action = True
1072
1073 # -- Print out "actions/next-state"
1074 if stall_action:
1075 if state == active_state:
1076 color = "#C0C000"
1077 else:
1078 color = "lightgrey"
1079
1080 elif active_state and next.ident == active_state.ident:
1081 color = "aqua"
1082 elif state == active_state:
1083 color = "yellow"
1084 else:
1085 color = "white"
1086
1087 fix = code.nofix()
1088 code('<TD bgcolor=$color>')
1089 for action in trans.actions:
1090 href = "%s_action_%s.html" % (self.ident, action.ident)
1091 ref = self.frameRef(href, "Status", href, "1",
1092 action.short)
1093 code(' $ref\n')
1094 if next != state:
1095 if trans.actions:
1096 code('/')
1097 click = "%s_table_%s.html" % (self.ident, next.ident)
1098 over = "%s_State_%s.html" % (self.ident, next.ident)
1099 ref = self.frameRef(click, "Table", over, "1", next.short)
1100 code("$ref")
1101 code("</TD>\n")
1102 code.fix(fix)
1103
1104 # -- Each row
1105 if state == active_state:
1106 color = "yellow"
1107 else:
1108 color = "white"
1109
1110 click = "%s_table_%s.html" % (self.ident, state.ident)
1111 over = "%s_State_%s.html" % (self.ident, state.ident)
1112 ref = self.frameRef(click, "Table", over, "1", state.short)
1113 code('''
1114 <TH bgcolor=$color>$ref</TH>
1115</TR>
1116''')
1117 code('''
1118<TR>
1119 <TH> </TH>
1120''')
1121
1122 for event in self.events.itervalues():
1123 href = "%s_Event_%s.html" % (self.ident, event.ident)
1124 ref = self.frameRef(href, "Status", href, "1", event.short)
1125 code('<TH bgcolor=white>$ref</TH>')
1126 code('''
1127</TR>
1128</TABLE>
1129</BODY></HTML>
1130''')
1131
1132
1133 if active_state:
1134 name = "%s_table_%s.html" % (self.ident, active_state.ident)
1135 else:
1136 name = "%s_table.html" % self.ident
1137 code.write(path, name)
1138
1139__all__ = [ "StateMachine" ]