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