StateMachine.py (6969:1ab268977bbb) StateMachine.py (6999:f226c098c393)
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
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
28from m5.util import 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):
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()
153 code = self.symtab.codeFormatter()
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'''
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()
183 code = self.symtab.codeFormatter()
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;
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
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;
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()
304 code = self.symtab.codeFormatter()
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 m_is_blocking = false;
353''')
354 code.indent()
355
356 #
357 # After initializing the universal machine parameters, initialize the
358 # this machines config parameters. Also detemine if these configuration
359 # params include a sequencer. This information will be used later for
360 # contecting the sequencer back to the L1 cache controller.
361 #
362 contains_sequencer = False
363 for param in self.config_parameters:
364 if param.name == "sequencer" or param.name == "dma_sequencer":
365 contains_sequencer = True
366 if param.pointer:
367 code('m_${{param.name}}_ptr = p->${{param.name}};')
368 else:
369 code('m_${{param.name}} = p->${{param.name}};')
370
371 #
372 # For the l1 cache controller, add the special atomic support which
373 # includes passing the sequencer a pointer to the controller.
374 #
375 if self.ident == "L1Cache":
376 if not contains_sequencer:
377 self.error("The L1Cache controller must include the sequencer " \
378 "configuration parameter")
379
380 code('''
381m_sequencer_ptr->setController(this);
382''')
383 #
384 # For the DMA controller, pass the sequencer a pointer to the
385 # controller.
386 #
387 if self.ident == "DMA":
388 if not contains_sequencer:
389 self.error("The DMA controller must include the sequencer " \
390 "configuration parameter")
391
392 code('''
393m_dma_sequencer_ptr->setController(this);
394''')
395
396 code('m_num_controllers++;')
397 for var in self.objects:
398 if var.ident.find("mandatoryQueue") >= 0:
399 code('m_${{var.c_ident}}_ptr = new ${{var.type.c_ident}}();')
400
401 code.dedent()
402 code('''
403}
404
405void $c_ident::init()
406{
407 m_machineID.type = MachineType_${ident};
408 m_machineID.num = m_version;
409
410 // Objects
411 s_profiler.setVersion(m_version);
412''')
413
414 code.indent()
415 for var in self.objects:
416 vtype = var.type
417 vid = "m_%s_ptr" % var.c_ident
418 if "network" not in var:
419 # Not a network port object
420 if "primitive" in vtype:
421 code('$vid = new ${{vtype.c_ident}};')
422 if "default" in var:
423 code('(*$vid) = ${{var["default"]}};')
424 else:
425 # Normal Object
426 # added by SS
427 if "factory" in var:
428 code('$vid = ${{var["factory"]}};')
429 elif var.ident.find("mandatoryQueue") < 0:
430 th = var.get("template_hack", "")
431 expr = "%s = new %s%s" % (vid, vtype.c_ident, th)
432
433 args = ""
434 if "non_obj" not in vtype and not vtype.isEnumeration:
435 if expr.find("TBETable") >= 0:
436 args = "m_number_of_TBEs"
437 else:
438 args = var.get("constructor_hack", "")
439 args = "(%s)" % args
440
441 code('$expr$args;')
442 else:
443 code(';')
444
445 code('assert($vid != NULL);')
446
447 if "default" in var:
448 code('(*$vid) = ${{var["default"]}}; // Object default')
449 elif "default" in vtype:
450 code('(*$vid) = ${{vtype["default"]}}; // Type ${{vtype.ident}} default')
451
452 # Set ordering
453 if "ordered" in var and "trigger_queue" not in var:
454 # A buffer
455 code('$vid->setOrdering(${{var["ordered"]}});')
456
457 # Set randomization
458 if "random" in var:
459 # A buffer
460 code('$vid->setRandomization(${{var["random"]}});')
461
462 # Set Priority
463 if vtype.isBuffer and \
464 "rank" in var and "trigger_queue" not in var:
465 code('$vid->setPriority(${{var["rank"]}});')
466 else:
467 # Network port object
468 network = var["network"]
469 ordered = var["ordered"]
470 vnet = var["virtual_network"]
471
472 assert var.machine is not None
473 code('''
474$vid = m_net_ptr->get${network}NetQueue(m_version+MachineType_base_number(string_to_MachineType("${{var.machine.ident}}")), $ordered, $vnet);
475''')
476
477 code('assert($vid != NULL);')
478
479 # Set ordering
480 if "ordered" in var:
481 # A buffer
482 code('$vid->setOrdering(${{var["ordered"]}});')
483
484 # Set randomization
485 if "random" in var:
486 # A buffer
487 code('$vid->setRandomization(${{var["random"]}})')
488
489 # Set Priority
490 if "rank" in var:
491 code('$vid->setPriority(${{var["rank"]}})')
492
493 # Set buffer size
494 if vtype.isBuffer:
495 code('''
496if (m_buffer_size > 0) {
497 $vid->setSize(m_buffer_size);
498}
499''')
500
501 # set description (may be overriden later by port def)
502 code('$vid->setDescription("[Version " + int_to_string(m_version) + ", ${ident}, name=${{var.c_ident}}]");')
503
504 # Set the queue consumers
505 code.insert_newline()
506 for port in self.in_ports:
507 code('${{port.code}}.setConsumer(this);')
508
509 # Set the queue descriptions
510 code.insert_newline()
511 for port in self.in_ports:
512 code('${{port.code}}.setDescription("[Version " + int_to_string(m_version) + ", $ident, $port]");')
513
514 # Initialize the transition profiling
515 code.insert_newline()
516 for trans in self.transitions:
517 # Figure out if we stall
518 stall = False
519 for action in trans.actions:
520 if action.ident == "z_stall":
521 stall = True
522
523 # Only possible if it is not a 'z' case
524 if not stall:
525 state = "%s_State_%s" % (self.ident, trans.state.ident)
526 event = "%s_Event_%s" % (self.ident, trans.event.ident)
527 code('s_profiler.possibleTransition($state, $event);')
528
529 # added by SS to initialize recycle_latency of message buffers
530 for buf in self.message_buffer_names:
531 code("$buf->setRecycleLatency(m_recycle_latency);")
532
533 code.dedent()
534 code('}')
535
536 has_mandatory_q = False
537 for port in self.in_ports:
538 if port.code.find("mandatoryQueue_ptr") >= 0:
539 has_mandatory_q = True
540
541 if has_mandatory_q:
542 mq_ident = "m_%s_mandatoryQueue_ptr" % self.ident
543 else:
544 mq_ident = "NULL"
545
546 code('''
547int $c_ident::getNumControllers() {
548 return m_num_controllers;
549}
550
551MessageBuffer* $c_ident::getMandatoryQueue() const {
552 return $mq_ident;
553}
554
555const int & $c_ident::getVersion() const{
556 return m_version;
557}
558
559const string $c_ident::toString() const{
560 return "$c_ident";
561}
562
563const string $c_ident::getName() const{
564 return m_name;
565}
566const MachineType $c_ident::getMachineType() const{
567 return MachineType_${ident};
568}
569
570void $c_ident::blockOnQueue(Address addr, MessageBuffer* port) {
571 m_is_blocking = true;
572 m_block_map[addr] = port;
573}
574void $c_ident::unblock(Address addr) {
575 m_block_map.erase(addr);
576 if (m_block_map.size() == 0) {
577 m_is_blocking = false;
578 }
579}
580
581void $c_ident::print(ostream& out) const { out << "[$c_ident " << m_version << "]"; }
582
583void $c_ident::printConfig(ostream& out) const {
584 out << "$c_ident config: " << m_name << endl;
585 out << " version: " << m_version << endl;
586 for (map<string, string>::const_iterator it = m_cfg.begin(); it != m_cfg.end(); it++) {
587 out << " " << (*it).first << ": " << (*it).second << endl;
588 }
589}
590
591void $c_ident::printStats(ostream& out) const {
592''')
593 #
594 # Cache and Memory Controllers have specific profilers associated with
595 # them. Print out these stats before dumping state transition stats.
596 #
597 for param in self.config_parameters:
598 if param.type_ast.type.ident == "CacheMemory" or \
599 param.type_ast.type.ident == "MemoryControl":
600 assert(param.pointer)
601 code(' m_${{param.ident}}_ptr->printStats(out);')
602
603 code('''
604 s_profiler.dumpStats(out);
605}
606
607void $c_ident::clearStats() {
608''')
609 #
610 # Cache and Memory Controllers have specific profilers associated with
611 # them. These stats must be cleared too.
612 #
613 for param in self.config_parameters:
614 if param.type_ast.type.ident == "CacheMemory" or \
615 param.type_ast.type.ident == "MemoryControl":
616 assert(param.pointer)
617 code(' m_${{param.ident}}_ptr->clearStats();')
618
619 code('''
620 s_profiler.clearStats();
621}
622
623// Actions
624''')
625
626 for action in self.actions.itervalues():
627 if "c_code" not in action:
628 continue
629
630 code('''
631/** \\brief ${{action.desc}} */
632void $c_ident::${{action.ident}}(const Address& addr)
633{
634 DEBUG_MSG(GENERATED_COMP, HighPrio, "executing");
635 ${{action["c_code"]}}
636}
637
638''')
639 code.write(path, "%s.cc" % c_ident)
640
641 def printCWakeup(self, path):
642 '''Output the wakeup loop for the events'''
643
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 m_is_blocking = false;
353''')
354 code.indent()
355
356 #
357 # After initializing the universal machine parameters, initialize the
358 # this machines config parameters. Also detemine if these configuration
359 # params include a sequencer. This information will be used later for
360 # contecting the sequencer back to the L1 cache controller.
361 #
362 contains_sequencer = False
363 for param in self.config_parameters:
364 if param.name == "sequencer" or param.name == "dma_sequencer":
365 contains_sequencer = True
366 if param.pointer:
367 code('m_${{param.name}}_ptr = p->${{param.name}};')
368 else:
369 code('m_${{param.name}} = p->${{param.name}};')
370
371 #
372 # For the l1 cache controller, add the special atomic support which
373 # includes passing the sequencer a pointer to the controller.
374 #
375 if self.ident == "L1Cache":
376 if not contains_sequencer:
377 self.error("The L1Cache controller must include the sequencer " \
378 "configuration parameter")
379
380 code('''
381m_sequencer_ptr->setController(this);
382''')
383 #
384 # For the DMA controller, pass the sequencer a pointer to the
385 # controller.
386 #
387 if self.ident == "DMA":
388 if not contains_sequencer:
389 self.error("The DMA controller must include the sequencer " \
390 "configuration parameter")
391
392 code('''
393m_dma_sequencer_ptr->setController(this);
394''')
395
396 code('m_num_controllers++;')
397 for var in self.objects:
398 if var.ident.find("mandatoryQueue") >= 0:
399 code('m_${{var.c_ident}}_ptr = new ${{var.type.c_ident}}();')
400
401 code.dedent()
402 code('''
403}
404
405void $c_ident::init()
406{
407 m_machineID.type = MachineType_${ident};
408 m_machineID.num = m_version;
409
410 // Objects
411 s_profiler.setVersion(m_version);
412''')
413
414 code.indent()
415 for var in self.objects:
416 vtype = var.type
417 vid = "m_%s_ptr" % var.c_ident
418 if "network" not in var:
419 # Not a network port object
420 if "primitive" in vtype:
421 code('$vid = new ${{vtype.c_ident}};')
422 if "default" in var:
423 code('(*$vid) = ${{var["default"]}};')
424 else:
425 # Normal Object
426 # added by SS
427 if "factory" in var:
428 code('$vid = ${{var["factory"]}};')
429 elif var.ident.find("mandatoryQueue") < 0:
430 th = var.get("template_hack", "")
431 expr = "%s = new %s%s" % (vid, vtype.c_ident, th)
432
433 args = ""
434 if "non_obj" not in vtype and not vtype.isEnumeration:
435 if expr.find("TBETable") >= 0:
436 args = "m_number_of_TBEs"
437 else:
438 args = var.get("constructor_hack", "")
439 args = "(%s)" % args
440
441 code('$expr$args;')
442 else:
443 code(';')
444
445 code('assert($vid != NULL);')
446
447 if "default" in var:
448 code('(*$vid) = ${{var["default"]}}; // Object default')
449 elif "default" in vtype:
450 code('(*$vid) = ${{vtype["default"]}}; // Type ${{vtype.ident}} default')
451
452 # Set ordering
453 if "ordered" in var and "trigger_queue" not in var:
454 # A buffer
455 code('$vid->setOrdering(${{var["ordered"]}});')
456
457 # Set randomization
458 if "random" in var:
459 # A buffer
460 code('$vid->setRandomization(${{var["random"]}});')
461
462 # Set Priority
463 if vtype.isBuffer and \
464 "rank" in var and "trigger_queue" not in var:
465 code('$vid->setPriority(${{var["rank"]}});')
466 else:
467 # Network port object
468 network = var["network"]
469 ordered = var["ordered"]
470 vnet = var["virtual_network"]
471
472 assert var.machine is not None
473 code('''
474$vid = m_net_ptr->get${network}NetQueue(m_version+MachineType_base_number(string_to_MachineType("${{var.machine.ident}}")), $ordered, $vnet);
475''')
476
477 code('assert($vid != NULL);')
478
479 # Set ordering
480 if "ordered" in var:
481 # A buffer
482 code('$vid->setOrdering(${{var["ordered"]}});')
483
484 # Set randomization
485 if "random" in var:
486 # A buffer
487 code('$vid->setRandomization(${{var["random"]}})')
488
489 # Set Priority
490 if "rank" in var:
491 code('$vid->setPriority(${{var["rank"]}})')
492
493 # Set buffer size
494 if vtype.isBuffer:
495 code('''
496if (m_buffer_size > 0) {
497 $vid->setSize(m_buffer_size);
498}
499''')
500
501 # set description (may be overriden later by port def)
502 code('$vid->setDescription("[Version " + int_to_string(m_version) + ", ${ident}, name=${{var.c_ident}}]");')
503
504 # Set the queue consumers
505 code.insert_newline()
506 for port in self.in_ports:
507 code('${{port.code}}.setConsumer(this);')
508
509 # Set the queue descriptions
510 code.insert_newline()
511 for port in self.in_ports:
512 code('${{port.code}}.setDescription("[Version " + int_to_string(m_version) + ", $ident, $port]");')
513
514 # Initialize the transition profiling
515 code.insert_newline()
516 for trans in self.transitions:
517 # Figure out if we stall
518 stall = False
519 for action in trans.actions:
520 if action.ident == "z_stall":
521 stall = True
522
523 # Only possible if it is not a 'z' case
524 if not stall:
525 state = "%s_State_%s" % (self.ident, trans.state.ident)
526 event = "%s_Event_%s" % (self.ident, trans.event.ident)
527 code('s_profiler.possibleTransition($state, $event);')
528
529 # added by SS to initialize recycle_latency of message buffers
530 for buf in self.message_buffer_names:
531 code("$buf->setRecycleLatency(m_recycle_latency);")
532
533 code.dedent()
534 code('}')
535
536 has_mandatory_q = False
537 for port in self.in_ports:
538 if port.code.find("mandatoryQueue_ptr") >= 0:
539 has_mandatory_q = True
540
541 if has_mandatory_q:
542 mq_ident = "m_%s_mandatoryQueue_ptr" % self.ident
543 else:
544 mq_ident = "NULL"
545
546 code('''
547int $c_ident::getNumControllers() {
548 return m_num_controllers;
549}
550
551MessageBuffer* $c_ident::getMandatoryQueue() const {
552 return $mq_ident;
553}
554
555const int & $c_ident::getVersion() const{
556 return m_version;
557}
558
559const string $c_ident::toString() const{
560 return "$c_ident";
561}
562
563const string $c_ident::getName() const{
564 return m_name;
565}
566const MachineType $c_ident::getMachineType() const{
567 return MachineType_${ident};
568}
569
570void $c_ident::blockOnQueue(Address addr, MessageBuffer* port) {
571 m_is_blocking = true;
572 m_block_map[addr] = port;
573}
574void $c_ident::unblock(Address addr) {
575 m_block_map.erase(addr);
576 if (m_block_map.size() == 0) {
577 m_is_blocking = false;
578 }
579}
580
581void $c_ident::print(ostream& out) const { out << "[$c_ident " << m_version << "]"; }
582
583void $c_ident::printConfig(ostream& out) const {
584 out << "$c_ident config: " << m_name << endl;
585 out << " version: " << m_version << endl;
586 for (map<string, string>::const_iterator it = m_cfg.begin(); it != m_cfg.end(); it++) {
587 out << " " << (*it).first << ": " << (*it).second << endl;
588 }
589}
590
591void $c_ident::printStats(ostream& out) const {
592''')
593 #
594 # Cache and Memory Controllers have specific profilers associated with
595 # them. Print out these stats before dumping state transition stats.
596 #
597 for param in self.config_parameters:
598 if param.type_ast.type.ident == "CacheMemory" or \
599 param.type_ast.type.ident == "MemoryControl":
600 assert(param.pointer)
601 code(' m_${{param.ident}}_ptr->printStats(out);')
602
603 code('''
604 s_profiler.dumpStats(out);
605}
606
607void $c_ident::clearStats() {
608''')
609 #
610 # Cache and Memory Controllers have specific profilers associated with
611 # them. These stats must be cleared too.
612 #
613 for param in self.config_parameters:
614 if param.type_ast.type.ident == "CacheMemory" or \
615 param.type_ast.type.ident == "MemoryControl":
616 assert(param.pointer)
617 code(' m_${{param.ident}}_ptr->clearStats();')
618
619 code('''
620 s_profiler.clearStats();
621}
622
623// Actions
624''')
625
626 for action in self.actions.itervalues():
627 if "c_code" not in action:
628 continue
629
630 code('''
631/** \\brief ${{action.desc}} */
632void $c_ident::${{action.ident}}(const Address& addr)
633{
634 DEBUG_MSG(GENERATED_COMP, HighPrio, "executing");
635 ${{action["c_code"]}}
636}
637
638''')
639 code.write(path, "%s.cc" % c_ident)
640
641 def printCWakeup(self, path):
642 '''Output the wakeup loop for the events'''
643
644 code = code_formatter()
644 code = self.symtab.codeFormatter()
645 ident = self.ident
646
647 code('''
648// Auto generated C++ code started by $__file__:$__line__
649// ${ident}: ${{self.short}}
650
651#include "mem/ruby/common/Global.hh"
652#include "mem/ruby/slicc_interface/RubySlicc_includes.hh"
653#include "mem/protocol/${ident}_Controller.hh"
654#include "mem/protocol/${ident}_State.hh"
655#include "mem/protocol/${ident}_Event.hh"
656#include "mem/protocol/Types.hh"
657#include "mem/ruby/system/System.hh"
658
659void ${ident}_Controller::wakeup()
660{
661
662 int counter = 0;
663 while (true) {
664 // Some cases will put us into an infinite loop without this limit
665 assert(counter <= m_transitions_per_cycle);
666 if (counter == m_transitions_per_cycle) {
667 g_system_ptr->getProfiler()->controllerBusy(m_machineID); // Count how often we\'re fully utilized
668 g_eventQueue_ptr->scheduleEvent(this, 1); // Wakeup in another cycle and try again
669 break;
670 }
671''')
672
673 code.indent()
674 code.indent()
675
676 # InPorts
677 #
678 for port in self.in_ports:
679 code.indent()
680 code('// ${ident}InPort $port')
681 code('${{port["c_code_in_port"]}}')
682 code.dedent()
683
684 code('')
685
686 code.dedent()
687 code.dedent()
688 code('''
689 break; // If we got this far, we have nothing left todo
690 }
691}
692''')
693
694 code.write(path, "%s_Wakeup.cc" % self.ident)
695
696 def printCSwitch(self, path):
697 '''Output switch statement for transition table'''
698
645 ident = self.ident
646
647 code('''
648// Auto generated C++ code started by $__file__:$__line__
649// ${ident}: ${{self.short}}
650
651#include "mem/ruby/common/Global.hh"
652#include "mem/ruby/slicc_interface/RubySlicc_includes.hh"
653#include "mem/protocol/${ident}_Controller.hh"
654#include "mem/protocol/${ident}_State.hh"
655#include "mem/protocol/${ident}_Event.hh"
656#include "mem/protocol/Types.hh"
657#include "mem/ruby/system/System.hh"
658
659void ${ident}_Controller::wakeup()
660{
661
662 int counter = 0;
663 while (true) {
664 // Some cases will put us into an infinite loop without this limit
665 assert(counter <= m_transitions_per_cycle);
666 if (counter == m_transitions_per_cycle) {
667 g_system_ptr->getProfiler()->controllerBusy(m_machineID); // Count how often we\'re fully utilized
668 g_eventQueue_ptr->scheduleEvent(this, 1); // Wakeup in another cycle and try again
669 break;
670 }
671''')
672
673 code.indent()
674 code.indent()
675
676 # InPorts
677 #
678 for port in self.in_ports:
679 code.indent()
680 code('// ${ident}InPort $port')
681 code('${{port["c_code_in_port"]}}')
682 code.dedent()
683
684 code('')
685
686 code.dedent()
687 code.dedent()
688 code('''
689 break; // If we got this far, we have nothing left todo
690 }
691}
692''')
693
694 code.write(path, "%s_Wakeup.cc" % self.ident)
695
696 def printCSwitch(self, path):
697 '''Output switch statement for transition table'''
698
699 code = code_formatter()
699 code = self.symtab.codeFormatter()
700 ident = self.ident
701
702 code('''
703// Auto generated C++ code started by $__file__:$__line__
704// ${ident}: ${{self.short}}
705
706#include "mem/ruby/common/Global.hh"
707#include "mem/protocol/${ident}_Controller.hh"
708#include "mem/protocol/${ident}_State.hh"
709#include "mem/protocol/${ident}_Event.hh"
710#include "mem/protocol/Types.hh"
711#include "mem/ruby/system/System.hh"
712
713#define HASH_FUN(state, event) ((int(state)*${ident}_Event_NUM)+int(event))
714
715#define GET_TRANSITION_COMMENT() (${ident}_transitionComment.str())
716#define CLEAR_TRANSITION_COMMENT() (${ident}_transitionComment.str(""))
717
718TransitionResult ${ident}_Controller::doTransition(${ident}_Event event, ${ident}_State state, const Address& addr
719)
720{
721 ${ident}_State next_state = state;
722
723 DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
724 DEBUG_MSG(GENERATED_COMP, MedPrio, *this);
725 DEBUG_EXPR(GENERATED_COMP, MedPrio, g_eventQueue_ptr->getTime());
726 DEBUG_EXPR(GENERATED_COMP, MedPrio,state);
727 DEBUG_EXPR(GENERATED_COMP, MedPrio,event);
728 DEBUG_EXPR(GENERATED_COMP, MedPrio,addr);
729
730 TransitionResult result = doTransitionWorker(event, state, next_state, addr);
731
732 if (result == TransitionResult_Valid) {
733 DEBUG_EXPR(GENERATED_COMP, MedPrio, next_state);
734 DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
735 s_profiler.countTransition(state, event);
736 if (Debug::getProtocolTrace()) {
737 g_system_ptr->getProfiler()->profileTransition("${ident}", m_version, addr,
738 ${ident}_State_to_string(state),
739 ${ident}_Event_to_string(event),
740 ${ident}_State_to_string(next_state), GET_TRANSITION_COMMENT());
741 }
742 CLEAR_TRANSITION_COMMENT();
743 ${ident}_setState(addr, next_state);
744
745 } else if (result == TransitionResult_ResourceStall) {
746 if (Debug::getProtocolTrace()) {
747 g_system_ptr->getProfiler()->profileTransition("${ident}", m_version, addr,
748 ${ident}_State_to_string(state),
749 ${ident}_Event_to_string(event),
750 ${ident}_State_to_string(next_state),
751 "Resource Stall");
752 }
753 } else if (result == TransitionResult_ProtocolStall) {
754 DEBUG_MSG(GENERATED_COMP, HighPrio, "stalling");
755 DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
756 if (Debug::getProtocolTrace()) {
757 g_system_ptr->getProfiler()->profileTransition("${ident}", m_version, addr,
758 ${ident}_State_to_string(state),
759 ${ident}_Event_to_string(event),
760 ${ident}_State_to_string(next_state),
761 "Protocol Stall");
762 }
763 }
764
765 return result;
766}
767
768TransitionResult ${ident}_Controller::doTransitionWorker(${ident}_Event event, ${ident}_State state, ${ident}_State& next_state, const Address& addr
769)
770{
771 switch(HASH_FUN(state, event)) {
772''')
773
774 # This map will allow suppress generating duplicate code
775 cases = orderdict()
776
777 for trans in self.transitions:
778 case_string = "%s_State_%s, %s_Event_%s" % \
779 (self.ident, trans.state.ident, self.ident, trans.event.ident)
780
700 ident = self.ident
701
702 code('''
703// Auto generated C++ code started by $__file__:$__line__
704// ${ident}: ${{self.short}}
705
706#include "mem/ruby/common/Global.hh"
707#include "mem/protocol/${ident}_Controller.hh"
708#include "mem/protocol/${ident}_State.hh"
709#include "mem/protocol/${ident}_Event.hh"
710#include "mem/protocol/Types.hh"
711#include "mem/ruby/system/System.hh"
712
713#define HASH_FUN(state, event) ((int(state)*${ident}_Event_NUM)+int(event))
714
715#define GET_TRANSITION_COMMENT() (${ident}_transitionComment.str())
716#define CLEAR_TRANSITION_COMMENT() (${ident}_transitionComment.str(""))
717
718TransitionResult ${ident}_Controller::doTransition(${ident}_Event event, ${ident}_State state, const Address& addr
719)
720{
721 ${ident}_State next_state = state;
722
723 DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
724 DEBUG_MSG(GENERATED_COMP, MedPrio, *this);
725 DEBUG_EXPR(GENERATED_COMP, MedPrio, g_eventQueue_ptr->getTime());
726 DEBUG_EXPR(GENERATED_COMP, MedPrio,state);
727 DEBUG_EXPR(GENERATED_COMP, MedPrio,event);
728 DEBUG_EXPR(GENERATED_COMP, MedPrio,addr);
729
730 TransitionResult result = doTransitionWorker(event, state, next_state, addr);
731
732 if (result == TransitionResult_Valid) {
733 DEBUG_EXPR(GENERATED_COMP, MedPrio, next_state);
734 DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
735 s_profiler.countTransition(state, event);
736 if (Debug::getProtocolTrace()) {
737 g_system_ptr->getProfiler()->profileTransition("${ident}", m_version, addr,
738 ${ident}_State_to_string(state),
739 ${ident}_Event_to_string(event),
740 ${ident}_State_to_string(next_state), GET_TRANSITION_COMMENT());
741 }
742 CLEAR_TRANSITION_COMMENT();
743 ${ident}_setState(addr, next_state);
744
745 } else if (result == TransitionResult_ResourceStall) {
746 if (Debug::getProtocolTrace()) {
747 g_system_ptr->getProfiler()->profileTransition("${ident}", m_version, addr,
748 ${ident}_State_to_string(state),
749 ${ident}_Event_to_string(event),
750 ${ident}_State_to_string(next_state),
751 "Resource Stall");
752 }
753 } else if (result == TransitionResult_ProtocolStall) {
754 DEBUG_MSG(GENERATED_COMP, HighPrio, "stalling");
755 DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
756 if (Debug::getProtocolTrace()) {
757 g_system_ptr->getProfiler()->profileTransition("${ident}", m_version, addr,
758 ${ident}_State_to_string(state),
759 ${ident}_Event_to_string(event),
760 ${ident}_State_to_string(next_state),
761 "Protocol Stall");
762 }
763 }
764
765 return result;
766}
767
768TransitionResult ${ident}_Controller::doTransitionWorker(${ident}_Event event, ${ident}_State state, ${ident}_State& next_state, const Address& addr
769)
770{
771 switch(HASH_FUN(state, event)) {
772''')
773
774 # This map will allow suppress generating duplicate code
775 cases = orderdict()
776
777 for trans in self.transitions:
778 case_string = "%s_State_%s, %s_Event_%s" % \
779 (self.ident, trans.state.ident, self.ident, trans.event.ident)
780
781 case = code_formatter()
781 case = self.symtab.codeFormatter()
782 # Only set next_state if it changes
783 if trans.state != trans.nextState:
784 ns_ident = trans.nextState.ident
785 case('next_state = ${ident}_State_${ns_ident};')
786
787 actions = trans.actions
788
789 # Check for resources
790 case_sorter = []
791 res = trans.resources
792 for key,val in res.iteritems():
793 if key.type.ident != "DNUCAStopTable":
794 val = '''
795if (!%s.areNSlotsAvailable(%s)) {
796 return TransitionResult_ResourceStall;
797}
798''' % (key.code, val)
799 case_sorter.append(val)
800
801
802 # Emit the code sequences in a sorted order. This makes the
803 # output deterministic (without this the output order can vary
804 # since Map's keys() on a vector of pointers is not deterministic
805 for c in sorted(case_sorter):
806 case("$c")
807
808 # Figure out if we stall
809 stall = False
810 for action in actions:
811 if action.ident == "z_stall":
812 stall = True
813 break
814
815 if stall:
816 case('return TransitionResult_ProtocolStall;')
817 else:
818 for action in actions:
819 case('${{action.ident}}(addr);')
820 case('return TransitionResult_Valid;')
821
822 case = str(case)
823
824 # Look to see if this transition code is unique.
825 if case not in cases:
826 cases[case] = []
827
828 cases[case].append(case_string)
829
830 # Walk through all of the unique code blocks and spit out the
831 # corresponding case statement elements
832 for case,transitions in cases.iteritems():
833 # Iterative over all the multiple transitions that share
834 # the same code
835 for trans in transitions:
836 code(' case HASH_FUN($trans):')
837 code(' {')
838 code(' $case')
839 code(' }')
840
841 code('''
842 default:
843 WARN_EXPR(m_version);
844 WARN_EXPR(g_eventQueue_ptr->getTime());
845 WARN_EXPR(addr);
846 WARN_EXPR(event);
847 WARN_EXPR(state);
848 ERROR_MSG(\"Invalid transition\");
849 }
850 return TransitionResult_Valid;
851}
852''')
853 code.write(path, "%s_Transitions.cc" % self.ident)
854
855 def printProfilerHH(self, path):
782 # Only set next_state if it changes
783 if trans.state != trans.nextState:
784 ns_ident = trans.nextState.ident
785 case('next_state = ${ident}_State_${ns_ident};')
786
787 actions = trans.actions
788
789 # Check for resources
790 case_sorter = []
791 res = trans.resources
792 for key,val in res.iteritems():
793 if key.type.ident != "DNUCAStopTable":
794 val = '''
795if (!%s.areNSlotsAvailable(%s)) {
796 return TransitionResult_ResourceStall;
797}
798''' % (key.code, val)
799 case_sorter.append(val)
800
801
802 # Emit the code sequences in a sorted order. This makes the
803 # output deterministic (without this the output order can vary
804 # since Map's keys() on a vector of pointers is not deterministic
805 for c in sorted(case_sorter):
806 case("$c")
807
808 # Figure out if we stall
809 stall = False
810 for action in actions:
811 if action.ident == "z_stall":
812 stall = True
813 break
814
815 if stall:
816 case('return TransitionResult_ProtocolStall;')
817 else:
818 for action in actions:
819 case('${{action.ident}}(addr);')
820 case('return TransitionResult_Valid;')
821
822 case = str(case)
823
824 # Look to see if this transition code is unique.
825 if case not in cases:
826 cases[case] = []
827
828 cases[case].append(case_string)
829
830 # Walk through all of the unique code blocks and spit out the
831 # corresponding case statement elements
832 for case,transitions in cases.iteritems():
833 # Iterative over all the multiple transitions that share
834 # the same code
835 for trans in transitions:
836 code(' case HASH_FUN($trans):')
837 code(' {')
838 code(' $case')
839 code(' }')
840
841 code('''
842 default:
843 WARN_EXPR(m_version);
844 WARN_EXPR(g_eventQueue_ptr->getTime());
845 WARN_EXPR(addr);
846 WARN_EXPR(event);
847 WARN_EXPR(state);
848 ERROR_MSG(\"Invalid transition\");
849 }
850 return TransitionResult_Valid;
851}
852''')
853 code.write(path, "%s_Transitions.cc" % self.ident)
854
855 def printProfilerHH(self, path):
856 code = code_formatter()
856 code = self.symtab.codeFormatter()
857 ident = self.ident
858
859 code('''
860// Auto generated C++ code started by $__file__:$__line__
861// ${ident}: ${{self.short}}
862
863#ifndef ${ident}_PROFILER_H
864#define ${ident}_PROFILER_H
865
866#include "mem/ruby/common/Global.hh"
867#include "mem/protocol/${ident}_State.hh"
868#include "mem/protocol/${ident}_Event.hh"
869
870class ${ident}_Profiler {
871 public:
872 ${ident}_Profiler();
873 void setVersion(int version);
874 void countTransition(${ident}_State state, ${ident}_Event event);
875 void possibleTransition(${ident}_State state, ${ident}_Event event);
876 void dumpStats(ostream& out) const;
877 void clearStats();
878
879 private:
880 int m_counters[${ident}_State_NUM][${ident}_Event_NUM];
881 int m_event_counters[${ident}_Event_NUM];
882 bool m_possible[${ident}_State_NUM][${ident}_Event_NUM];
883 int m_version;
884};
885
886#endif // ${ident}_PROFILER_H
887''')
888 code.write(path, "%s_Profiler.hh" % self.ident)
889
890 def printProfilerCC(self, path):
857 ident = self.ident
858
859 code('''
860// Auto generated C++ code started by $__file__:$__line__
861// ${ident}: ${{self.short}}
862
863#ifndef ${ident}_PROFILER_H
864#define ${ident}_PROFILER_H
865
866#include "mem/ruby/common/Global.hh"
867#include "mem/protocol/${ident}_State.hh"
868#include "mem/protocol/${ident}_Event.hh"
869
870class ${ident}_Profiler {
871 public:
872 ${ident}_Profiler();
873 void setVersion(int version);
874 void countTransition(${ident}_State state, ${ident}_Event event);
875 void possibleTransition(${ident}_State state, ${ident}_Event event);
876 void dumpStats(ostream& out) const;
877 void clearStats();
878
879 private:
880 int m_counters[${ident}_State_NUM][${ident}_Event_NUM];
881 int m_event_counters[${ident}_Event_NUM];
882 bool m_possible[${ident}_State_NUM][${ident}_Event_NUM];
883 int m_version;
884};
885
886#endif // ${ident}_PROFILER_H
887''')
888 code.write(path, "%s_Profiler.hh" % self.ident)
889
890 def printProfilerCC(self, path):
891 code = code_formatter()
891 code = self.symtab.codeFormatter()
892 ident = self.ident
893
894 code('''
895// Auto generated C++ code started by $__file__:$__line__
896// ${ident}: ${{self.short}}
897
898#include "mem/protocol/${ident}_Profiler.hh"
899
900${ident}_Profiler::${ident}_Profiler()
901{
902 for (int state = 0; state < ${ident}_State_NUM; state++) {
903 for (int event = 0; event < ${ident}_Event_NUM; event++) {
904 m_possible[state][event] = false;
905 m_counters[state][event] = 0;
906 }
907 }
908 for (int event = 0; event < ${ident}_Event_NUM; event++) {
909 m_event_counters[event] = 0;
910 }
911}
912void ${ident}_Profiler::setVersion(int version)
913{
914 m_version = version;
915}
916void ${ident}_Profiler::clearStats()
917{
918 for (int state = 0; state < ${ident}_State_NUM; state++) {
919 for (int event = 0; event < ${ident}_Event_NUM; event++) {
920 m_counters[state][event] = 0;
921 }
922 }
923
924 for (int event = 0; event < ${ident}_Event_NUM; event++) {
925 m_event_counters[event] = 0;
926 }
927}
928void ${ident}_Profiler::countTransition(${ident}_State state, ${ident}_Event event)
929{
930 assert(m_possible[state][event]);
931 m_counters[state][event]++;
932 m_event_counters[event]++;
933}
934void ${ident}_Profiler::possibleTransition(${ident}_State state, ${ident}_Event event)
935{
936 m_possible[state][event] = true;
937}
938void ${ident}_Profiler::dumpStats(ostream& out) const
939{
940 out << " --- ${ident} " << m_version << " ---" << endl;
941 out << " - Event Counts -" << endl;
942 for (int event = 0; event < ${ident}_Event_NUM; event++) {
943 int count = m_event_counters[event];
944 out << (${ident}_Event) event << " " << count << endl;
945 }
946 out << endl;
947 out << " - Transitions -" << endl;
948 for (int state = 0; state < ${ident}_State_NUM; state++) {
949 for (int event = 0; event < ${ident}_Event_NUM; event++) {
950 if (m_possible[state][event]) {
951 int count = m_counters[state][event];
952 out << (${ident}_State) state << " " << (${ident}_Event) event << " " << count;
953 if (count == 0) {
954 out << " <-- ";
955 }
956 out << endl;
957 }
958 }
959 out << endl;
960 }
961}
962''')
963 code.write(path, "%s_Profiler.cc" % self.ident)
964
965 # **************************
966 # ******* HTML Files *******
967 # **************************
968 def frameRef(self, click_href, click_target, over_href, over_target_num,
969 text):
892 ident = self.ident
893
894 code('''
895// Auto generated C++ code started by $__file__:$__line__
896// ${ident}: ${{self.short}}
897
898#include "mem/protocol/${ident}_Profiler.hh"
899
900${ident}_Profiler::${ident}_Profiler()
901{
902 for (int state = 0; state < ${ident}_State_NUM; state++) {
903 for (int event = 0; event < ${ident}_Event_NUM; event++) {
904 m_possible[state][event] = false;
905 m_counters[state][event] = 0;
906 }
907 }
908 for (int event = 0; event < ${ident}_Event_NUM; event++) {
909 m_event_counters[event] = 0;
910 }
911}
912void ${ident}_Profiler::setVersion(int version)
913{
914 m_version = version;
915}
916void ${ident}_Profiler::clearStats()
917{
918 for (int state = 0; state < ${ident}_State_NUM; state++) {
919 for (int event = 0; event < ${ident}_Event_NUM; event++) {
920 m_counters[state][event] = 0;
921 }
922 }
923
924 for (int event = 0; event < ${ident}_Event_NUM; event++) {
925 m_event_counters[event] = 0;
926 }
927}
928void ${ident}_Profiler::countTransition(${ident}_State state, ${ident}_Event event)
929{
930 assert(m_possible[state][event]);
931 m_counters[state][event]++;
932 m_event_counters[event]++;
933}
934void ${ident}_Profiler::possibleTransition(${ident}_State state, ${ident}_Event event)
935{
936 m_possible[state][event] = true;
937}
938void ${ident}_Profiler::dumpStats(ostream& out) const
939{
940 out << " --- ${ident} " << m_version << " ---" << endl;
941 out << " - Event Counts -" << endl;
942 for (int event = 0; event < ${ident}_Event_NUM; event++) {
943 int count = m_event_counters[event];
944 out << (${ident}_Event) event << " " << count << endl;
945 }
946 out << endl;
947 out << " - Transitions -" << endl;
948 for (int state = 0; state < ${ident}_State_NUM; state++) {
949 for (int event = 0; event < ${ident}_Event_NUM; event++) {
950 if (m_possible[state][event]) {
951 int count = m_counters[state][event];
952 out << (${ident}_State) state << " " << (${ident}_Event) event << " " << count;
953 if (count == 0) {
954 out << " <-- ";
955 }
956 out << endl;
957 }
958 }
959 out << endl;
960 }
961}
962''')
963 code.write(path, "%s_Profiler.cc" % self.ident)
964
965 # **************************
966 # ******* HTML Files *******
967 # **************************
968 def frameRef(self, click_href, click_target, over_href, over_target_num,
969 text):
970 code = code_formatter(fix_newlines=False)
970 code = self.symtab.codeFormatter(fix_newlines=False)
971 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>""")
972 return str(code)
973
974 def writeHTMLFiles(self, path):
975 # Create table with no row hilighted
976 self.printHTMLTransitions(path, None)
977
978 # Generate transition tables
979 for state in self.states.itervalues():
980 self.printHTMLTransitions(path, state)
981
982 # Generate action descriptions
983 for action in self.actions.itervalues():
984 name = "%s_action_%s.html" % (self.ident, action.ident)
985 code = html.createSymbol(action, "Action")
986 code.write(path, name)
987
988 # Generate state descriptions
989 for state in self.states.itervalues():
990 name = "%s_State_%s.html" % (self.ident, state.ident)
991 code = html.createSymbol(state, "State")
992 code.write(path, name)
993
994 # Generate event descriptions
995 for event in self.events.itervalues():
996 name = "%s_Event_%s.html" % (self.ident, event.ident)
997 code = html.createSymbol(event, "Event")
998 code.write(path, name)
999
1000 def printHTMLTransitions(self, path, active_state):
971 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>""")
972 return str(code)
973
974 def writeHTMLFiles(self, path):
975 # Create table with no row hilighted
976 self.printHTMLTransitions(path, None)
977
978 # Generate transition tables
979 for state in self.states.itervalues():
980 self.printHTMLTransitions(path, state)
981
982 # Generate action descriptions
983 for action in self.actions.itervalues():
984 name = "%s_action_%s.html" % (self.ident, action.ident)
985 code = html.createSymbol(action, "Action")
986 code.write(path, name)
987
988 # Generate state descriptions
989 for state in self.states.itervalues():
990 name = "%s_State_%s.html" % (self.ident, state.ident)
991 code = html.createSymbol(state, "State")
992 code.write(path, name)
993
994 # Generate event descriptions
995 for event in self.events.itervalues():
996 name = "%s_Event_%s.html" % (self.ident, event.ident)
997 code = html.createSymbol(event, "Event")
998 code.write(path, name)
999
1000 def printHTMLTransitions(self, path, active_state):
1001 code = code_formatter()
1001 code = self.symtab.codeFormatter()
1002
1003 code('''
1004<HTML><BODY link="blue" vlink="blue">
1005
1006<H1 align="center">${{html.formatShorthand(self.short)}}:
1007''')
1008 code.indent()
1009 for i,machine in enumerate(self.symtab.getAllType(StateMachine)):
1010 mid = machine.ident
1011 if i != 0:
1012 extra = " - "
1013 else:
1014 extra = ""
1015 if machine == self:
1016 code('$extra$mid')
1017 else:
1018 code('$extra<A target="Table" href="${mid}_table.html">$mid</A>')
1019 code.dedent()
1020
1021 code("""
1022</H1>
1023
1024<TABLE border=1>
1025<TR>
1026 <TH> </TH>
1027""")
1028
1029 for event in self.events.itervalues():
1030 href = "%s_Event_%s.html" % (self.ident, event.ident)
1031 ref = self.frameRef(href, "Status", href, "1", event.short)
1032 code('<TH bgcolor=white>$ref</TH>')
1033
1034 code('</TR>')
1035 # -- Body of table
1036 for state in self.states.itervalues():
1037 # -- Each row
1038 if state == active_state:
1039 color = "yellow"
1040 else:
1041 color = "white"
1042
1043 click = "%s_table_%s.html" % (self.ident, state.ident)
1044 over = "%s_State_%s.html" % (self.ident, state.ident)
1045 text = html.formatShorthand(state.short)
1046 ref = self.frameRef(click, "Table", over, "1", state.short)
1047 code('''
1048<TR>
1049 <TH bgcolor=$color>$ref</TH>
1050''')
1051
1052 # -- One column for each event
1053 for event in self.events.itervalues():
1054 trans = self.table.get((state,event), None)
1055 if trans is None:
1056 # This is the no transition case
1057 if state == active_state:
1058 color = "#C0C000"
1059 else:
1060 color = "lightgrey"
1061
1062 code('<TD bgcolor=$color>&nbsp;</TD>')
1063 continue
1064
1065 next = trans.nextState
1066 stall_action = False
1067
1068 # -- Get the actions
1069 for action in trans.actions:
1070 if action.ident == "z_stall" or \
1071 action.ident == "zz_recycleMandatoryQueue":
1072 stall_action = True
1073
1074 # -- Print out "actions/next-state"
1075 if stall_action:
1076 if state == active_state:
1077 color = "#C0C000"
1078 else:
1079 color = "lightgrey"
1080
1081 elif active_state and next.ident == active_state.ident:
1082 color = "aqua"
1083 elif state == active_state:
1084 color = "yellow"
1085 else:
1086 color = "white"
1087
1088 fix = code.nofix()
1089 code('<TD bgcolor=$color>')
1090 for action in trans.actions:
1091 href = "%s_action_%s.html" % (self.ident, action.ident)
1092 ref = self.frameRef(href, "Status", href, "1",
1093 action.short)
1094 code(' $ref\n')
1095 if next != state:
1096 if trans.actions:
1097 code('/')
1098 click = "%s_table_%s.html" % (self.ident, next.ident)
1099 over = "%s_State_%s.html" % (self.ident, next.ident)
1100 ref = self.frameRef(click, "Table", over, "1", next.short)
1101 code("$ref")
1102 code("</TD>\n")
1103 code.fix(fix)
1104
1105 # -- Each row
1106 if state == active_state:
1107 color = "yellow"
1108 else:
1109 color = "white"
1110
1111 click = "%s_table_%s.html" % (self.ident, state.ident)
1112 over = "%s_State_%s.html" % (self.ident, state.ident)
1113 ref = self.frameRef(click, "Table", over, "1", state.short)
1114 code('''
1115 <TH bgcolor=$color>$ref</TH>
1116</TR>
1117''')
1118 code('''
1119<TR>
1120 <TH> </TH>
1121''')
1122
1123 for event in self.events.itervalues():
1124 href = "%s_Event_%s.html" % (self.ident, event.ident)
1125 ref = self.frameRef(href, "Status", href, "1", event.short)
1126 code('<TH bgcolor=white>$ref</TH>')
1127 code('''
1128</TR>
1129</TABLE>
1130</BODY></HTML>
1131''')
1132
1133
1134 if active_state:
1135 name = "%s_table_%s.html" % (self.ident, active_state.ident)
1136 else:
1137 name = "%s_table.html" % self.ident
1138 code.write(path, name)
1139
1140__all__ = [ "StateMachine" ]
1002
1003 code('''
1004<HTML><BODY link="blue" vlink="blue">
1005
1006<H1 align="center">${{html.formatShorthand(self.short)}}:
1007''')
1008 code.indent()
1009 for i,machine in enumerate(self.symtab.getAllType(StateMachine)):
1010 mid = machine.ident
1011 if i != 0:
1012 extra = " - "
1013 else:
1014 extra = ""
1015 if machine == self:
1016 code('$extra$mid')
1017 else:
1018 code('$extra<A target="Table" href="${mid}_table.html">$mid</A>')
1019 code.dedent()
1020
1021 code("""
1022</H1>
1023
1024<TABLE border=1>
1025<TR>
1026 <TH> </TH>
1027""")
1028
1029 for event in self.events.itervalues():
1030 href = "%s_Event_%s.html" % (self.ident, event.ident)
1031 ref = self.frameRef(href, "Status", href, "1", event.short)
1032 code('<TH bgcolor=white>$ref</TH>')
1033
1034 code('</TR>')
1035 # -- Body of table
1036 for state in self.states.itervalues():
1037 # -- Each row
1038 if state == active_state:
1039 color = "yellow"
1040 else:
1041 color = "white"
1042
1043 click = "%s_table_%s.html" % (self.ident, state.ident)
1044 over = "%s_State_%s.html" % (self.ident, state.ident)
1045 text = html.formatShorthand(state.short)
1046 ref = self.frameRef(click, "Table", over, "1", state.short)
1047 code('''
1048<TR>
1049 <TH bgcolor=$color>$ref</TH>
1050''')
1051
1052 # -- One column for each event
1053 for event in self.events.itervalues():
1054 trans = self.table.get((state,event), None)
1055 if trans is None:
1056 # This is the no transition case
1057 if state == active_state:
1058 color = "#C0C000"
1059 else:
1060 color = "lightgrey"
1061
1062 code('<TD bgcolor=$color>&nbsp;</TD>')
1063 continue
1064
1065 next = trans.nextState
1066 stall_action = False
1067
1068 # -- Get the actions
1069 for action in trans.actions:
1070 if action.ident == "z_stall" or \
1071 action.ident == "zz_recycleMandatoryQueue":
1072 stall_action = True
1073
1074 # -- Print out "actions/next-state"
1075 if stall_action:
1076 if state == active_state:
1077 color = "#C0C000"
1078 else:
1079 color = "lightgrey"
1080
1081 elif active_state and next.ident == active_state.ident:
1082 color = "aqua"
1083 elif state == active_state:
1084 color = "yellow"
1085 else:
1086 color = "white"
1087
1088 fix = code.nofix()
1089 code('<TD bgcolor=$color>')
1090 for action in trans.actions:
1091 href = "%s_action_%s.html" % (self.ident, action.ident)
1092 ref = self.frameRef(href, "Status", href, "1",
1093 action.short)
1094 code(' $ref\n')
1095 if next != state:
1096 if trans.actions:
1097 code('/')
1098 click = "%s_table_%s.html" % (self.ident, next.ident)
1099 over = "%s_State_%s.html" % (self.ident, next.ident)
1100 ref = self.frameRef(click, "Table", over, "1", next.short)
1101 code("$ref")
1102 code("</TD>\n")
1103 code.fix(fix)
1104
1105 # -- Each row
1106 if state == active_state:
1107 color = "yellow"
1108 else:
1109 color = "white"
1110
1111 click = "%s_table_%s.html" % (self.ident, state.ident)
1112 over = "%s_State_%s.html" % (self.ident, state.ident)
1113 ref = self.frameRef(click, "Table", over, "1", state.short)
1114 code('''
1115 <TH bgcolor=$color>$ref</TH>
1116</TR>
1117''')
1118 code('''
1119<TR>
1120 <TH> </TH>
1121''')
1122
1123 for event in self.events.itervalues():
1124 href = "%s_Event_%s.html" % (self.ident, event.ident)
1125 ref = self.frameRef(href, "Status", href, "1", event.short)
1126 code('<TH bgcolor=white>$ref</TH>')
1127 code('''
1128</TR>
1129</TABLE>
1130</BODY></HTML>
1131''')
1132
1133
1134 if active_state:
1135 name = "%s_table_%s.html" % (self.ident, active_state.ident)
1136 else:
1137 name = "%s_table.html" % self.ident
1138 code.write(path, name)
1139
1140__all__ = [ "StateMachine" ]