StateMachine.py (7007:79413d1ec307) StateMachine.py (7025:9adf5b0ccc79)
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
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 = 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'''
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 $c_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_HH__
197#define __${ident}_CONTROLLER_HH__
198
199#include <iostream>
200#include <sstream>
201#include <string>
202
203#include "params/$c_ident.hh"
204
205#include "mem/ruby/common/Global.hh"
206#include "mem/ruby/common/Consumer.hh"
207#include "mem/ruby/slicc_interface/AbstractController.hh"
208#include "mem/protocol/TransitionResult.hh"
209#include "mem/protocol/Types.hh"
210#include "mem/protocol/${ident}_Profiler.hh"
211''')
212
213 seen_types = set()
214 for var in self.objects:
215 if var.type.ident not in seen_types and not var.type.isPrimitive:
216 code('#include "mem/protocol/${{var.type.c_ident}}.hh"')
217 seen_types.add(var.type.ident)
218
219 # for adding information to the protocol debug trace
220 code('''
221extern std::stringstream ${ident}_transitionComment;
222
223class $c_ident : public AbstractController
224{
225// the coherence checker needs to call isBlockExclusive() and isBlockShared()
226// making the Chip a friend class is an easy way to do this for now
227
228public:
229 typedef ${c_ident}Params Params;
230 $c_ident(const Params *p);
231 static int getNumControllers();
232 void init();
233 MessageBuffer* getMandatoryQueue() const;
234 const int & getVersion() const;
235 const std::string toString() const;
236 const std::string getName() const;
237 const MachineType getMachineType() const;
238 void initNetworkPtr(Network* net_ptr) { m_net_ptr = net_ptr; }
239 void print(std::ostream& out) const;
240 void printConfig(std::ostream& out) const;
241 void wakeup();
242 void printStats(std::ostream& out) const;
243 void clearStats();
244 void blockOnQueue(Address addr, MessageBuffer* port);
245 void unblock(Address addr);
246
247private:
248''')
249
250 code.indent()
251 # added by SS
252 for param in self.config_parameters:
253 if param.pointer:
254 code('${{param.type_ast.type}}* m_${{param.ident}}_ptr;')
255 else:
256 code('${{param.type_ast.type}} m_${{param.ident}};')
257
258 code('''
259int m_number_of_TBEs;
260
261TransitionResult doTransition(${ident}_Event event,
262 ${ident}_State state,
263 const Address& addr);
264
265TransitionResult doTransitionWorker(${ident}_Event event,
266 ${ident}_State state,
267 ${ident}_State& next_state,
268 const Address& addr);
269
270std::string m_name;
271int m_transitions_per_cycle;
272int m_buffer_size;
273int m_recycle_latency;
274map<std::string, std::string> m_cfg;
275NodeID m_version;
276Network* m_net_ptr;
277MachineID m_machineID;
278bool m_is_blocking;
279map< Address, MessageBuffer* > m_block_map;
280${ident}_Profiler s_profiler;
281static int m_num_controllers;
282
283// Internal functions
284''')
285
286 for func in self.functions:
287 proto = func.prototype
288 if proto:
289 code('$proto')
290
291 code('''
292
293// Actions
294''')
295 for action in self.actions.itervalues():
296 code('/** \\brief ${{action.desc}} */')
297 code('void ${{action.ident}}(const Address& addr);')
298
299 # the controller internal variables
300 code('''
301
302// Objects
303''')
304 for var in self.objects:
305 th = var.get("template_hack", "")
306 code('${{var.type.c_ident}}$th* m_${{var.c_ident}}_ptr;')
307
308 if var.type.ident == "MessageBuffer":
309 self.message_buffer_names.append("m_%s_ptr" % var.c_ident)
310
311 code.dedent()
312 code('};')
313 code('#endif // __${ident}_CONTROLLER_H__')
314 code.write(path, '%s.hh' % c_ident)
315
316 def printControllerCC(self, path):
317 '''Output the actions for performing the actions'''
318
319 code = self.symtab.codeFormatter()
320 ident = self.ident
321 c_ident = "%s_Controller" % self.ident
322
323 code('''
324/** \\file $c_ident.cc
325 *
326 * Auto generated C++ code started by $__file__:$__line__
327 * Created by slicc definition of Module "${{self.short}}"
328 */
329
330#include <sstream>
331#include <string>
332
333#include "mem/ruby/common/Global.hh"
334#include "mem/ruby/slicc_interface/RubySlicc_includes.hh"
335#include "mem/protocol/${ident}_Controller.hh"
336#include "mem/protocol/${ident}_State.hh"
337#include "mem/protocol/${ident}_Event.hh"
338#include "mem/protocol/Types.hh"
339#include "mem/ruby/system/System.hh"
340
341using namespace std;
342''')
343
344 # include object classes
345 seen_types = set()
346 for var in self.objects:
347 if var.type.ident not in seen_types and not var.type.isPrimitive:
348 code('#include "mem/protocol/${{var.type.c_ident}}.hh"')
349 seen_types.add(var.type.ident)
350
351 code('''
352$c_ident *
353${c_ident}Params::create()
354{
355 return new $c_ident(this);
356}
357
358int $c_ident::m_num_controllers = 0;
359
360// for adding information to the protocol debug trace
361stringstream ${ident}_transitionComment;
362#define APPEND_TRANSITION_COMMENT(str) (${ident}_transitionComment << str)
363
364/** \\brief constructor */
365$c_ident::$c_ident(const Params *p)
366 : AbstractController(p)
367{
368 m_version = p->version;
369 m_transitions_per_cycle = p->transitions_per_cycle;
370 m_buffer_size = p->buffer_size;
371 m_recycle_latency = p->recycle_latency;
372 m_number_of_TBEs = p->number_of_TBEs;
373 m_is_blocking = false;
374''')
375 code.indent()
376
377 #
378 # After initializing the universal machine parameters, initialize the
379 # this machines config parameters. Also detemine if these configuration
380 # params include a sequencer. This information will be used later for
381 # contecting the sequencer back to the L1 cache controller.
382 #
383 contains_sequencer = False
384 for param in self.config_parameters:
385 if param.name == "sequencer" or param.name == "dma_sequencer":
386 contains_sequencer = True
387 if param.pointer:
388 code('m_${{param.name}}_ptr = p->${{param.name}};')
389 else:
390 code('m_${{param.name}} = p->${{param.name}};')
391
392 #
393 # For the l1 cache controller, add the special atomic support which
394 # includes passing the sequencer a pointer to the controller.
395 #
396 if self.ident == "L1Cache":
397 if not contains_sequencer:
398 self.error("The L1Cache controller must include the sequencer " \
399 "configuration parameter")
400
401 code('''
402m_sequencer_ptr->setController(this);
403''')
404 #
405 # For the DMA controller, pass the sequencer a pointer to the
406 # controller.
407 #
408 if self.ident == "DMA":
409 if not contains_sequencer:
410 self.error("The DMA controller must include the sequencer " \
411 "configuration parameter")
412
413 code('''
414m_dma_sequencer_ptr->setController(this);
415''')
416
417 code('m_num_controllers++;')
418 for var in self.objects:
419 if var.ident.find("mandatoryQueue") >= 0:
420 code('m_${{var.c_ident}}_ptr = new ${{var.type.c_ident}}();')
421
422 code.dedent()
423 code('''
424}
425
426void
427$c_ident::init()
428{
429 MachineType machine_type;
430 int base;
431
432 m_machineID.type = MachineType_${ident};
433 m_machineID.num = m_version;
434
435 // initialize objects
436 s_profiler.setVersion(m_version);
437
438''')
439
440 code.indent()
441 for var in self.objects:
442 vtype = var.type
443 vid = "m_%s_ptr" % var.c_ident
444 if "network" not in var:
445 # Not a network port object
446 if "primitive" in vtype:
447 code('$vid = new ${{vtype.c_ident}};')
448 if "default" in var:
449 code('(*$vid) = ${{var["default"]}};')
450 else:
451 # Normal Object
452 # added by SS
453 if "factory" in var:
454 code('$vid = ${{var["factory"]}};')
455 elif var.ident.find("mandatoryQueue") < 0:
456 th = var.get("template_hack", "")
457 expr = "%s = new %s%s" % (vid, vtype.c_ident, th)
458
459 args = ""
460 if "non_obj" not in vtype and not vtype.isEnumeration:
461 if expr.find("TBETable") >= 0:
462 args = "m_number_of_TBEs"
463 else:
464 args = var.get("constructor_hack", "")
465
466 code('$expr($args);')
467
468 code('assert($vid != NULL);')
469
470 if "default" in var:
471 code('*$vid = ${{var["default"]}}; // Object default')
472 elif "default" in vtype:
473 comment = "Type %s default" % vtype.ident
474 code('*$vid = ${{vtype["default"]}}; // $comment')
475
476 # Set ordering
477 if "ordered" in var and "trigger_queue" not in var:
478 # A buffer
479 code('$vid->setOrdering(${{var["ordered"]}});')
480
481 # Set randomization
482 if "random" in var:
483 # A buffer
484 code('$vid->setRandomization(${{var["random"]}});')
485
486 # Set Priority
487 if vtype.isBuffer and \
488 "rank" in var and "trigger_queue" not in var:
489 code('$vid->setPriority(${{var["rank"]}});')
490 else:
491 # Network port object
492 network = var["network"]
493 ordered = var["ordered"]
494 vnet = var["virtual_network"]
495
496 assert var.machine is not None
497 code('''
498machine_type = string_to_MachineType("${{var.machine.ident}}");
499base = MachineType_base_number(machine_type);
500$vid = m_net_ptr->get${network}NetQueue(m_version + base, $ordered, $vnet);
501''')
502
503 code('assert($vid != NULL);')
504
505 # Set ordering
506 if "ordered" in var:
507 # A buffer
508 code('$vid->setOrdering(${{var["ordered"]}});')
509
510 # Set randomization
511 if "random" in var:
512 # A buffer
513 code('$vid->setRandomization(${{var["random"]}})')
514
515 # Set Priority
516 if "rank" in var:
517 code('$vid->setPriority(${{var["rank"]}})')
518
519 # Set buffer size
520 if vtype.isBuffer:
521 code('''
522if (m_buffer_size > 0) {
523 $vid->setSize(m_buffer_size);
524}
525''')
526
527 # set description (may be overriden later by port def)
528 code('''
529$vid->setDescription("[Version " + int_to_string(m_version) + ", ${ident}, name=${{var.c_ident}}]");
530
531''')
532
533 # Set the queue consumers
534 code.insert_newline()
535 for port in self.in_ports:
536 code('${{port.code}}.setConsumer(this);')
537
538 # Set the queue descriptions
539 code.insert_newline()
540 for port in self.in_ports:
541 code('${{port.code}}.setDescription("[Version " + int_to_string(m_version) + ", $ident, $port]");')
542
543 # Initialize the transition profiling
544 code.insert_newline()
545 for trans in self.transitions:
546 # Figure out if we stall
547 stall = False
548 for action in trans.actions:
549 if action.ident == "z_stall":
550 stall = True
551
552 # Only possible if it is not a 'z' case
553 if not stall:
554 state = "%s_State_%s" % (self.ident, trans.state.ident)
555 event = "%s_Event_%s" % (self.ident, trans.event.ident)
556 code('s_profiler.possibleTransition($state, $event);')
557
558 # added by SS to initialize recycle_latency of message buffers
559 for buf in self.message_buffer_names:
560 code("$buf->setRecycleLatency(m_recycle_latency);")
561
562 code.dedent()
563 code('}')
564
565 has_mandatory_q = False
566 for port in self.in_ports:
567 if port.code.find("mandatoryQueue_ptr") >= 0:
568 has_mandatory_q = True
569
570 if has_mandatory_q:
571 mq_ident = "m_%s_mandatoryQueue_ptr" % self.ident
572 else:
573 mq_ident = "NULL"
574
575 code('''
576int
577$c_ident::getNumControllers()
578{
579 return m_num_controllers;
580}
581
582MessageBuffer*
583$c_ident::getMandatoryQueue() const
584{
585 return $mq_ident;
586}
587
588const int &
589$c_ident::getVersion() const
590{
591 return m_version;
592}
593
594const string
595$c_ident::toString() const
596{
597 return "$c_ident";
598}
599
600const string
601$c_ident::getName() const
602{
603 return m_name;
604}
605
606const MachineType
607$c_ident::getMachineType() const
608{
609 return MachineType_${ident};
610}
611
612void
613$c_ident::blockOnQueue(Address addr, MessageBuffer* port)
614{
615 m_is_blocking = true;
616 m_block_map[addr] = port;
617}
618
619void
620$c_ident::unblock(Address addr)
621{
622 m_block_map.erase(addr);
623 if (m_block_map.size() == 0) {
624 m_is_blocking = false;
625 }
626}
627
628void
629$c_ident::print(ostream& out) const
630{
631 out << "[$c_ident " << m_version << "]";
632}
633
634void
635$c_ident::printConfig(ostream& out) const
636{
637 out << "$c_ident config: " << m_name << endl;
638 out << " version: " << m_version << endl;
639 map<string, string>::const_iterator it;
640 for (it = m_cfg.begin(); it != m_cfg.end(); it++)
641 out << " " << it->first << ": " << it->second << endl;
642}
643
644void
645$c_ident::printStats(ostream& out) const
646{
647''')
648 #
649 # Cache and Memory Controllers have specific profilers associated with
650 # them. Print out these stats before dumping state transition stats.
651 #
652 for param in self.config_parameters:
653 if param.type_ast.type.ident == "CacheMemory" or \
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
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 = 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'''
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 $c_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_HH__
197#define __${ident}_CONTROLLER_HH__
198
199#include <iostream>
200#include <sstream>
201#include <string>
202
203#include "params/$c_ident.hh"
204
205#include "mem/ruby/common/Global.hh"
206#include "mem/ruby/common/Consumer.hh"
207#include "mem/ruby/slicc_interface/AbstractController.hh"
208#include "mem/protocol/TransitionResult.hh"
209#include "mem/protocol/Types.hh"
210#include "mem/protocol/${ident}_Profiler.hh"
211''')
212
213 seen_types = set()
214 for var in self.objects:
215 if var.type.ident not in seen_types and not var.type.isPrimitive:
216 code('#include "mem/protocol/${{var.type.c_ident}}.hh"')
217 seen_types.add(var.type.ident)
218
219 # for adding information to the protocol debug trace
220 code('''
221extern std::stringstream ${ident}_transitionComment;
222
223class $c_ident : public AbstractController
224{
225// the coherence checker needs to call isBlockExclusive() and isBlockShared()
226// making the Chip a friend class is an easy way to do this for now
227
228public:
229 typedef ${c_ident}Params Params;
230 $c_ident(const Params *p);
231 static int getNumControllers();
232 void init();
233 MessageBuffer* getMandatoryQueue() const;
234 const int & getVersion() const;
235 const std::string toString() const;
236 const std::string getName() const;
237 const MachineType getMachineType() const;
238 void initNetworkPtr(Network* net_ptr) { m_net_ptr = net_ptr; }
239 void print(std::ostream& out) const;
240 void printConfig(std::ostream& out) const;
241 void wakeup();
242 void printStats(std::ostream& out) const;
243 void clearStats();
244 void blockOnQueue(Address addr, MessageBuffer* port);
245 void unblock(Address addr);
246
247private:
248''')
249
250 code.indent()
251 # added by SS
252 for param in self.config_parameters:
253 if param.pointer:
254 code('${{param.type_ast.type}}* m_${{param.ident}}_ptr;')
255 else:
256 code('${{param.type_ast.type}} m_${{param.ident}};')
257
258 code('''
259int m_number_of_TBEs;
260
261TransitionResult doTransition(${ident}_Event event,
262 ${ident}_State state,
263 const Address& addr);
264
265TransitionResult doTransitionWorker(${ident}_Event event,
266 ${ident}_State state,
267 ${ident}_State& next_state,
268 const Address& addr);
269
270std::string m_name;
271int m_transitions_per_cycle;
272int m_buffer_size;
273int m_recycle_latency;
274map<std::string, std::string> m_cfg;
275NodeID m_version;
276Network* m_net_ptr;
277MachineID m_machineID;
278bool m_is_blocking;
279map< Address, MessageBuffer* > m_block_map;
280${ident}_Profiler s_profiler;
281static int m_num_controllers;
282
283// Internal functions
284''')
285
286 for func in self.functions:
287 proto = func.prototype
288 if proto:
289 code('$proto')
290
291 code('''
292
293// Actions
294''')
295 for action in self.actions.itervalues():
296 code('/** \\brief ${{action.desc}} */')
297 code('void ${{action.ident}}(const Address& addr);')
298
299 # the controller internal variables
300 code('''
301
302// Objects
303''')
304 for var in self.objects:
305 th = var.get("template_hack", "")
306 code('${{var.type.c_ident}}$th* m_${{var.c_ident}}_ptr;')
307
308 if var.type.ident == "MessageBuffer":
309 self.message_buffer_names.append("m_%s_ptr" % var.c_ident)
310
311 code.dedent()
312 code('};')
313 code('#endif // __${ident}_CONTROLLER_H__')
314 code.write(path, '%s.hh' % c_ident)
315
316 def printControllerCC(self, path):
317 '''Output the actions for performing the actions'''
318
319 code = self.symtab.codeFormatter()
320 ident = self.ident
321 c_ident = "%s_Controller" % self.ident
322
323 code('''
324/** \\file $c_ident.cc
325 *
326 * Auto generated C++ code started by $__file__:$__line__
327 * Created by slicc definition of Module "${{self.short}}"
328 */
329
330#include <sstream>
331#include <string>
332
333#include "mem/ruby/common/Global.hh"
334#include "mem/ruby/slicc_interface/RubySlicc_includes.hh"
335#include "mem/protocol/${ident}_Controller.hh"
336#include "mem/protocol/${ident}_State.hh"
337#include "mem/protocol/${ident}_Event.hh"
338#include "mem/protocol/Types.hh"
339#include "mem/ruby/system/System.hh"
340
341using namespace std;
342''')
343
344 # include object classes
345 seen_types = set()
346 for var in self.objects:
347 if var.type.ident not in seen_types and not var.type.isPrimitive:
348 code('#include "mem/protocol/${{var.type.c_ident}}.hh"')
349 seen_types.add(var.type.ident)
350
351 code('''
352$c_ident *
353${c_ident}Params::create()
354{
355 return new $c_ident(this);
356}
357
358int $c_ident::m_num_controllers = 0;
359
360// for adding information to the protocol debug trace
361stringstream ${ident}_transitionComment;
362#define APPEND_TRANSITION_COMMENT(str) (${ident}_transitionComment << str)
363
364/** \\brief constructor */
365$c_ident::$c_ident(const Params *p)
366 : AbstractController(p)
367{
368 m_version = p->version;
369 m_transitions_per_cycle = p->transitions_per_cycle;
370 m_buffer_size = p->buffer_size;
371 m_recycle_latency = p->recycle_latency;
372 m_number_of_TBEs = p->number_of_TBEs;
373 m_is_blocking = false;
374''')
375 code.indent()
376
377 #
378 # After initializing the universal machine parameters, initialize the
379 # this machines config parameters. Also detemine if these configuration
380 # params include a sequencer. This information will be used later for
381 # contecting the sequencer back to the L1 cache controller.
382 #
383 contains_sequencer = False
384 for param in self.config_parameters:
385 if param.name == "sequencer" or param.name == "dma_sequencer":
386 contains_sequencer = True
387 if param.pointer:
388 code('m_${{param.name}}_ptr = p->${{param.name}};')
389 else:
390 code('m_${{param.name}} = p->${{param.name}};')
391
392 #
393 # For the l1 cache controller, add the special atomic support which
394 # includes passing the sequencer a pointer to the controller.
395 #
396 if self.ident == "L1Cache":
397 if not contains_sequencer:
398 self.error("The L1Cache controller must include the sequencer " \
399 "configuration parameter")
400
401 code('''
402m_sequencer_ptr->setController(this);
403''')
404 #
405 # For the DMA controller, pass the sequencer a pointer to the
406 # controller.
407 #
408 if self.ident == "DMA":
409 if not contains_sequencer:
410 self.error("The DMA controller must include the sequencer " \
411 "configuration parameter")
412
413 code('''
414m_dma_sequencer_ptr->setController(this);
415''')
416
417 code('m_num_controllers++;')
418 for var in self.objects:
419 if var.ident.find("mandatoryQueue") >= 0:
420 code('m_${{var.c_ident}}_ptr = new ${{var.type.c_ident}}();')
421
422 code.dedent()
423 code('''
424}
425
426void
427$c_ident::init()
428{
429 MachineType machine_type;
430 int base;
431
432 m_machineID.type = MachineType_${ident};
433 m_machineID.num = m_version;
434
435 // initialize objects
436 s_profiler.setVersion(m_version);
437
438''')
439
440 code.indent()
441 for var in self.objects:
442 vtype = var.type
443 vid = "m_%s_ptr" % var.c_ident
444 if "network" not in var:
445 # Not a network port object
446 if "primitive" in vtype:
447 code('$vid = new ${{vtype.c_ident}};')
448 if "default" in var:
449 code('(*$vid) = ${{var["default"]}};')
450 else:
451 # Normal Object
452 # added by SS
453 if "factory" in var:
454 code('$vid = ${{var["factory"]}};')
455 elif var.ident.find("mandatoryQueue") < 0:
456 th = var.get("template_hack", "")
457 expr = "%s = new %s%s" % (vid, vtype.c_ident, th)
458
459 args = ""
460 if "non_obj" not in vtype and not vtype.isEnumeration:
461 if expr.find("TBETable") >= 0:
462 args = "m_number_of_TBEs"
463 else:
464 args = var.get("constructor_hack", "")
465
466 code('$expr($args);')
467
468 code('assert($vid != NULL);')
469
470 if "default" in var:
471 code('*$vid = ${{var["default"]}}; // Object default')
472 elif "default" in vtype:
473 comment = "Type %s default" % vtype.ident
474 code('*$vid = ${{vtype["default"]}}; // $comment')
475
476 # Set ordering
477 if "ordered" in var and "trigger_queue" not in var:
478 # A buffer
479 code('$vid->setOrdering(${{var["ordered"]}});')
480
481 # Set randomization
482 if "random" in var:
483 # A buffer
484 code('$vid->setRandomization(${{var["random"]}});')
485
486 # Set Priority
487 if vtype.isBuffer and \
488 "rank" in var and "trigger_queue" not in var:
489 code('$vid->setPriority(${{var["rank"]}});')
490 else:
491 # Network port object
492 network = var["network"]
493 ordered = var["ordered"]
494 vnet = var["virtual_network"]
495
496 assert var.machine is not None
497 code('''
498machine_type = string_to_MachineType("${{var.machine.ident}}");
499base = MachineType_base_number(machine_type);
500$vid = m_net_ptr->get${network}NetQueue(m_version + base, $ordered, $vnet);
501''')
502
503 code('assert($vid != NULL);')
504
505 # Set ordering
506 if "ordered" in var:
507 # A buffer
508 code('$vid->setOrdering(${{var["ordered"]}});')
509
510 # Set randomization
511 if "random" in var:
512 # A buffer
513 code('$vid->setRandomization(${{var["random"]}})')
514
515 # Set Priority
516 if "rank" in var:
517 code('$vid->setPriority(${{var["rank"]}})')
518
519 # Set buffer size
520 if vtype.isBuffer:
521 code('''
522if (m_buffer_size > 0) {
523 $vid->setSize(m_buffer_size);
524}
525''')
526
527 # set description (may be overriden later by port def)
528 code('''
529$vid->setDescription("[Version " + int_to_string(m_version) + ", ${ident}, name=${{var.c_ident}}]");
530
531''')
532
533 # Set the queue consumers
534 code.insert_newline()
535 for port in self.in_ports:
536 code('${{port.code}}.setConsumer(this);')
537
538 # Set the queue descriptions
539 code.insert_newline()
540 for port in self.in_ports:
541 code('${{port.code}}.setDescription("[Version " + int_to_string(m_version) + ", $ident, $port]");')
542
543 # Initialize the transition profiling
544 code.insert_newline()
545 for trans in self.transitions:
546 # Figure out if we stall
547 stall = False
548 for action in trans.actions:
549 if action.ident == "z_stall":
550 stall = True
551
552 # Only possible if it is not a 'z' case
553 if not stall:
554 state = "%s_State_%s" % (self.ident, trans.state.ident)
555 event = "%s_Event_%s" % (self.ident, trans.event.ident)
556 code('s_profiler.possibleTransition($state, $event);')
557
558 # added by SS to initialize recycle_latency of message buffers
559 for buf in self.message_buffer_names:
560 code("$buf->setRecycleLatency(m_recycle_latency);")
561
562 code.dedent()
563 code('}')
564
565 has_mandatory_q = False
566 for port in self.in_ports:
567 if port.code.find("mandatoryQueue_ptr") >= 0:
568 has_mandatory_q = True
569
570 if has_mandatory_q:
571 mq_ident = "m_%s_mandatoryQueue_ptr" % self.ident
572 else:
573 mq_ident = "NULL"
574
575 code('''
576int
577$c_ident::getNumControllers()
578{
579 return m_num_controllers;
580}
581
582MessageBuffer*
583$c_ident::getMandatoryQueue() const
584{
585 return $mq_ident;
586}
587
588const int &
589$c_ident::getVersion() const
590{
591 return m_version;
592}
593
594const string
595$c_ident::toString() const
596{
597 return "$c_ident";
598}
599
600const string
601$c_ident::getName() const
602{
603 return m_name;
604}
605
606const MachineType
607$c_ident::getMachineType() const
608{
609 return MachineType_${ident};
610}
611
612void
613$c_ident::blockOnQueue(Address addr, MessageBuffer* port)
614{
615 m_is_blocking = true;
616 m_block_map[addr] = port;
617}
618
619void
620$c_ident::unblock(Address addr)
621{
622 m_block_map.erase(addr);
623 if (m_block_map.size() == 0) {
624 m_is_blocking = false;
625 }
626}
627
628void
629$c_ident::print(ostream& out) const
630{
631 out << "[$c_ident " << m_version << "]";
632}
633
634void
635$c_ident::printConfig(ostream& out) const
636{
637 out << "$c_ident config: " << m_name << endl;
638 out << " version: " << m_version << endl;
639 map<string, string>::const_iterator it;
640 for (it = m_cfg.begin(); it != m_cfg.end(); it++)
641 out << " " << it->first << ": " << it->second << endl;
642}
643
644void
645$c_ident::printStats(ostream& out) const
646{
647''')
648 #
649 # Cache and Memory Controllers have specific profilers associated with
650 # them. Print out these stats before dumping state transition stats.
651 #
652 for param in self.config_parameters:
653 if param.type_ast.type.ident == "CacheMemory" or \
654 param.type_ast.type.ident == "DirectoryMemory" or \
654 param.type_ast.type.ident == "MemoryControl":
655 assert(param.pointer)
656 code(' m_${{param.ident}}_ptr->printStats(out);')
657
658 code('''
659 s_profiler.dumpStats(out);
660}
661
662void $c_ident::clearStats() {
663''')
664 #
665 # Cache and Memory Controllers have specific profilers associated with
666 # them. These stats must be cleared too.
667 #
668 for param in self.config_parameters:
669 if param.type_ast.type.ident == "CacheMemory" or \
670 param.type_ast.type.ident == "MemoryControl":
671 assert(param.pointer)
672 code(' m_${{param.ident}}_ptr->clearStats();')
673
674 code('''
675 s_profiler.clearStats();
676}
677
678// Actions
679''')
680
681 for action in self.actions.itervalues():
682 if "c_code" not in action:
683 continue
684
685 code('''
686/** \\brief ${{action.desc}} */
687void
688$c_ident::${{action.ident}}(const Address& addr)
689{
690 DEBUG_MSG(GENERATED_COMP, HighPrio, "executing");
691 ${{action["c_code"]}}
692}
693
694''')
695 code.write(path, "%s.cc" % c_ident)
696
697 def printCWakeup(self, path):
698 '''Output the wakeup loop for the events'''
699
700 code = self.symtab.codeFormatter()
701 ident = self.ident
702
703 code('''
704// Auto generated C++ code started by $__file__:$__line__
705// ${ident}: ${{self.short}}
706
707#include "base/misc.hh"
708#include "mem/ruby/common/Global.hh"
709#include "mem/ruby/slicc_interface/RubySlicc_includes.hh"
710#include "mem/protocol/${ident}_Controller.hh"
711#include "mem/protocol/${ident}_State.hh"
712#include "mem/protocol/${ident}_Event.hh"
713#include "mem/protocol/Types.hh"
714#include "mem/ruby/system/System.hh"
715
716void
717${ident}_Controller::wakeup()
718{
719 // DEBUG_EXPR(GENERATED_COMP, MedPrio, *this);
720 // DEBUG_EXPR(GENERATED_COMP, MedPrio, g_eventQueue_ptr->getTime());
721
722 int counter = 0;
723 while (true) {
724 // Some cases will put us into an infinite loop without this limit
725 assert(counter <= m_transitions_per_cycle);
726 if (counter == m_transitions_per_cycle) {
727 // Count how often we are fully utilized
728 g_system_ptr->getProfiler()->controllerBusy(m_machineID);
729
730 // Wakeup in another cycle and try again
731 g_eventQueue_ptr->scheduleEvent(this, 1);
732 break;
733 }
734''')
735
736 code.indent()
737 code.indent()
738
739 # InPorts
740 #
741 for port in self.in_ports:
742 code.indent()
743 code('// ${ident}InPort $port')
744 code('${{port["c_code_in_port"]}}')
745 code.dedent()
746
747 code('')
748
749 code.dedent()
750 code.dedent()
751 code('''
752 break; // If we got this far, we have nothing left todo
753 }
754 // g_eventQueue_ptr->scheduleEvent(this, 1);
755 // DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
756}
757''')
758
759 code.write(path, "%s_Wakeup.cc" % self.ident)
760
761 def printCSwitch(self, path):
762 '''Output switch statement for transition table'''
763
764 code = self.symtab.codeFormatter()
765 ident = self.ident
766
767 code('''
768// Auto generated C++ code started by $__file__:$__line__
769// ${ident}: ${{self.short}}
770
771#include "mem/ruby/common/Global.hh"
772#include "mem/protocol/${ident}_Controller.hh"
773#include "mem/protocol/${ident}_State.hh"
774#include "mem/protocol/${ident}_Event.hh"
775#include "mem/protocol/Types.hh"
776#include "mem/ruby/system/System.hh"
777
778#define HASH_FUN(state, event) ((int(state)*${ident}_Event_NUM)+int(event))
779
780#define GET_TRANSITION_COMMENT() (${ident}_transitionComment.str())
781#define CLEAR_TRANSITION_COMMENT() (${ident}_transitionComment.str(""))
782
783TransitionResult
784${ident}_Controller::doTransition(${ident}_Event event,
785 ${ident}_State state,
786 const Address &addr)
787{
788 ${ident}_State next_state = state;
789
790 DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
791 DEBUG_MSG(GENERATED_COMP, MedPrio, *this);
792 DEBUG_EXPR(GENERATED_COMP, MedPrio, g_eventQueue_ptr->getTime());
793 DEBUG_EXPR(GENERATED_COMP, MedPrio,state);
794 DEBUG_EXPR(GENERATED_COMP, MedPrio,event);
795 DEBUG_EXPR(GENERATED_COMP, MedPrio,addr);
796
797 TransitionResult result =
798 doTransitionWorker(event, state, next_state, addr);
799
800 if (result == TransitionResult_Valid) {
801 DEBUG_EXPR(GENERATED_COMP, MedPrio, next_state);
802 DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
803 s_profiler.countTransition(state, event);
804 if (Debug::getProtocolTrace()) {
805 g_system_ptr->getProfiler()->profileTransition("${ident}",
806 m_version, addr,
807 ${ident}_State_to_string(state),
808 ${ident}_Event_to_string(event),
809 ${ident}_State_to_string(next_state),
810 GET_TRANSITION_COMMENT());
811 }
812 CLEAR_TRANSITION_COMMENT();
813 ${ident}_setState(addr, next_state);
814
815 } else if (result == TransitionResult_ResourceStall) {
816 if (Debug::getProtocolTrace()) {
817 g_system_ptr->getProfiler()->profileTransition("${ident}",
818 m_version, addr,
819 ${ident}_State_to_string(state),
820 ${ident}_Event_to_string(event),
821 ${ident}_State_to_string(next_state),
822 "Resource Stall");
823 }
824 } else if (result == TransitionResult_ProtocolStall) {
825 DEBUG_MSG(GENERATED_COMP, HighPrio, "stalling");
826 DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
827 if (Debug::getProtocolTrace()) {
828 g_system_ptr->getProfiler()->profileTransition("${ident}",
829 m_version, addr,
830 ${ident}_State_to_string(state),
831 ${ident}_Event_to_string(event),
832 ${ident}_State_to_string(next_state),
833 "Protocol Stall");
834 }
835 }
836
837 return result;
838}
839
840TransitionResult
841${ident}_Controller::doTransitionWorker(${ident}_Event event,
842 ${ident}_State state,
843 ${ident}_State& next_state,
844 const Address& addr)
845{
846 switch(HASH_FUN(state, event)) {
847''')
848
849 # This map will allow suppress generating duplicate code
850 cases = orderdict()
851
852 for trans in self.transitions:
853 case_string = "%s_State_%s, %s_Event_%s" % \
854 (self.ident, trans.state.ident, self.ident, trans.event.ident)
855
856 case = self.symtab.codeFormatter()
857 # Only set next_state if it changes
858 if trans.state != trans.nextState:
859 ns_ident = trans.nextState.ident
860 case('next_state = ${ident}_State_${ns_ident};')
861
862 actions = trans.actions
863
864 # Check for resources
865 case_sorter = []
866 res = trans.resources
867 for key,val in res.iteritems():
868 if key.type.ident != "DNUCAStopTable":
869 val = '''
870if (!%s.areNSlotsAvailable(%s))
871 return TransitionResult_ResourceStall;
872''' % (key.code, val)
873 case_sorter.append(val)
874
875
876 # Emit the code sequences in a sorted order. This makes the
877 # output deterministic (without this the output order can vary
878 # since Map's keys() on a vector of pointers is not deterministic
879 for c in sorted(case_sorter):
880 case("$c")
881
882 # Figure out if we stall
883 stall = False
884 for action in actions:
885 if action.ident == "z_stall":
886 stall = True
887 break
888
889 if stall:
890 case('return TransitionResult_ProtocolStall;')
891 else:
892 for action in actions:
893 case('${{action.ident}}(addr);')
894 case('return TransitionResult_Valid;')
895
896 case = str(case)
897
898 # Look to see if this transition code is unique.
899 if case not in cases:
900 cases[case] = []
901
902 cases[case].append(case_string)
903
904 # Walk through all of the unique code blocks and spit out the
905 # corresponding case statement elements
906 for case,transitions in cases.iteritems():
907 # Iterative over all the multiple transitions that share
908 # the same code
909 for trans in transitions:
910 code(' case HASH_FUN($trans):')
911 code(' $case')
912
913 code('''
914 default:
915 WARN_EXPR(m_version);
916 WARN_EXPR(g_eventQueue_ptr->getTime());
917 WARN_EXPR(addr);
918 WARN_EXPR(event);
919 WARN_EXPR(state);
920 ERROR_MSG(\"Invalid transition\");
921 }
922 return TransitionResult_Valid;
923}
924''')
925 code.write(path, "%s_Transitions.cc" % self.ident)
926
927 def printProfilerHH(self, path):
928 code = self.symtab.codeFormatter()
929 ident = self.ident
930
931 code('''
932// Auto generated C++ code started by $__file__:$__line__
933// ${ident}: ${{self.short}}
934
935#ifndef __${ident}_PROFILER_HH_
936#define __${ident}_PROFILER_HH_
937
938#include <iostream>
939
940#include "mem/ruby/common/Global.hh"
941#include "mem/protocol/${ident}_State.hh"
942#include "mem/protocol/${ident}_Event.hh"
943
944class ${ident}_Profiler
945{
946 public:
947 ${ident}_Profiler();
948 void setVersion(int version);
949 void countTransition(${ident}_State state, ${ident}_Event event);
950 void possibleTransition(${ident}_State state, ${ident}_Event event);
951 void dumpStats(std::ostream& out) const;
952 void clearStats();
953
954 private:
955 int m_counters[${ident}_State_NUM][${ident}_Event_NUM];
956 int m_event_counters[${ident}_Event_NUM];
957 bool m_possible[${ident}_State_NUM][${ident}_Event_NUM];
958 int m_version;
959};
960
961#endif // __${ident}_PROFILER_HH__
962''')
963 code.write(path, "%s_Profiler.hh" % self.ident)
964
965 def printProfilerCC(self, path):
966 code = self.symtab.codeFormatter()
967 ident = self.ident
968
969 code('''
970// Auto generated C++ code started by $__file__:$__line__
971// ${ident}: ${{self.short}}
972
973#include "mem/protocol/${ident}_Profiler.hh"
974
975${ident}_Profiler::${ident}_Profiler()
976{
977 for (int state = 0; state < ${ident}_State_NUM; state++) {
978 for (int event = 0; event < ${ident}_Event_NUM; event++) {
979 m_possible[state][event] = false;
980 m_counters[state][event] = 0;
981 }
982 }
983 for (int event = 0; event < ${ident}_Event_NUM; event++) {
984 m_event_counters[event] = 0;
985 }
986}
987
988void
989${ident}_Profiler::setVersion(int version)
990{
991 m_version = version;
992}
993
994void
995${ident}_Profiler::clearStats()
996{
997 for (int state = 0; state < ${ident}_State_NUM; state++) {
998 for (int event = 0; event < ${ident}_Event_NUM; event++) {
999 m_counters[state][event] = 0;
1000 }
1001 }
1002
1003 for (int event = 0; event < ${ident}_Event_NUM; event++) {
1004 m_event_counters[event] = 0;
1005 }
1006}
1007void
1008${ident}_Profiler::countTransition(${ident}_State state, ${ident}_Event event)
1009{
1010 assert(m_possible[state][event]);
1011 m_counters[state][event]++;
1012 m_event_counters[event]++;
1013}
1014void
1015${ident}_Profiler::possibleTransition(${ident}_State state,
1016 ${ident}_Event event)
1017{
1018 m_possible[state][event] = true;
1019}
1020
1021void
1022${ident}_Profiler::dumpStats(std::ostream& out) const
1023{
1024 using namespace std;
1025
1026 out << " --- ${ident} " << m_version << " ---" << endl;
1027 out << " - Event Counts -" << endl;
1028 for (int event = 0; event < ${ident}_Event_NUM; event++) {
1029 int count = m_event_counters[event];
1030 out << (${ident}_Event) event << " " << count << endl;
1031 }
1032 out << endl;
1033 out << " - Transitions -" << endl;
1034 for (int state = 0; state < ${ident}_State_NUM; state++) {
1035 for (int event = 0; event < ${ident}_Event_NUM; event++) {
1036 if (m_possible[state][event]) {
1037 int count = m_counters[state][event];
1038 out << (${ident}_State) state << " "
1039 << (${ident}_Event) event << " " << count;
1040 if (count == 0) {
1041 out << " <-- ";
1042 }
1043 out << endl;
1044 }
1045 }
1046 out << endl;
1047 }
1048}
1049''')
1050 code.write(path, "%s_Profiler.cc" % self.ident)
1051
1052 # **************************
1053 # ******* HTML Files *******
1054 # **************************
1055 def frameRef(self, click_href, click_target, over_href, over_num, text):
1056 code = self.symtab.codeFormatter(fix_newlines=False)
1057 code("""<A href=\"$click_href\" target=\"$click_target\" onmouseover=\"
1058 if (parent.frames[$over_num].location != parent.location + '$over_href') {
1059 parent.frames[$over_num].location='$over_href'
1060 }\">
1061 ${{html.formatShorthand(text)}}
1062 </A>""")
1063 return str(code)
1064
1065 def writeHTMLFiles(self, path):
1066 # Create table with no row hilighted
1067 self.printHTMLTransitions(path, None)
1068
1069 # Generate transition tables
1070 for state in self.states.itervalues():
1071 self.printHTMLTransitions(path, state)
1072
1073 # Generate action descriptions
1074 for action in self.actions.itervalues():
1075 name = "%s_action_%s.html" % (self.ident, action.ident)
1076 code = html.createSymbol(action, "Action")
1077 code.write(path, name)
1078
1079 # Generate state descriptions
1080 for state in self.states.itervalues():
1081 name = "%s_State_%s.html" % (self.ident, state.ident)
1082 code = html.createSymbol(state, "State")
1083 code.write(path, name)
1084
1085 # Generate event descriptions
1086 for event in self.events.itervalues():
1087 name = "%s_Event_%s.html" % (self.ident, event.ident)
1088 code = html.createSymbol(event, "Event")
1089 code.write(path, name)
1090
1091 def printHTMLTransitions(self, path, active_state):
1092 code = self.symtab.codeFormatter()
1093
1094 code('''
1095<HTML>
1096<BODY link="blue" vlink="blue">
1097
1098<H1 align="center">${{html.formatShorthand(self.short)}}:
1099''')
1100 code.indent()
1101 for i,machine in enumerate(self.symtab.getAllType(StateMachine)):
1102 mid = machine.ident
1103 if i != 0:
1104 extra = " - "
1105 else:
1106 extra = ""
1107 if machine == self:
1108 code('$extra$mid')
1109 else:
1110 code('$extra<A target="Table" href="${mid}_table.html">$mid</A>')
1111 code.dedent()
1112
1113 code("""
1114</H1>
1115
1116<TABLE border=1>
1117<TR>
1118 <TH> </TH>
1119""")
1120
1121 for event in self.events.itervalues():
1122 href = "%s_Event_%s.html" % (self.ident, event.ident)
1123 ref = self.frameRef(href, "Status", href, "1", event.short)
1124 code('<TH bgcolor=white>$ref</TH>')
1125
1126 code('</TR>')
1127 # -- Body of table
1128 for state in self.states.itervalues():
1129 # -- Each row
1130 if state == active_state:
1131 color = "yellow"
1132 else:
1133 color = "white"
1134
1135 click = "%s_table_%s.html" % (self.ident, state.ident)
1136 over = "%s_State_%s.html" % (self.ident, state.ident)
1137 text = html.formatShorthand(state.short)
1138 ref = self.frameRef(click, "Table", over, "1", state.short)
1139 code('''
1140<TR>
1141 <TH bgcolor=$color>$ref</TH>
1142''')
1143
1144 # -- One column for each event
1145 for event in self.events.itervalues():
1146 trans = self.table.get((state,event), None)
1147 if trans is None:
1148 # This is the no transition case
1149 if state == active_state:
1150 color = "#C0C000"
1151 else:
1152 color = "lightgrey"
1153
1154 code('<TD bgcolor=$color>&nbsp;</TD>')
1155 continue
1156
1157 next = trans.nextState
1158 stall_action = False
1159
1160 # -- Get the actions
1161 for action in trans.actions:
1162 if action.ident == "z_stall" or \
1163 action.ident == "zz_recycleMandatoryQueue":
1164 stall_action = True
1165
1166 # -- Print out "actions/next-state"
1167 if stall_action:
1168 if state == active_state:
1169 color = "#C0C000"
1170 else:
1171 color = "lightgrey"
1172
1173 elif active_state and next.ident == active_state.ident:
1174 color = "aqua"
1175 elif state == active_state:
1176 color = "yellow"
1177 else:
1178 color = "white"
1179
1180 code('<TD bgcolor=$color>')
1181 for action in trans.actions:
1182 href = "%s_action_%s.html" % (self.ident, action.ident)
1183 ref = self.frameRef(href, "Status", href, "1",
1184 action.short)
1185 code(' $ref')
1186 if next != state:
1187 if trans.actions:
1188 code('/')
1189 click = "%s_table_%s.html" % (self.ident, next.ident)
1190 over = "%s_State_%s.html" % (self.ident, next.ident)
1191 ref = self.frameRef(click, "Table", over, "1", next.short)
1192 code("$ref")
1193 code("</TD>")
1194
1195 # -- Each row
1196 if state == active_state:
1197 color = "yellow"
1198 else:
1199 color = "white"
1200
1201 click = "%s_table_%s.html" % (self.ident, state.ident)
1202 over = "%s_State_%s.html" % (self.ident, state.ident)
1203 ref = self.frameRef(click, "Table", over, "1", state.short)
1204 code('''
1205 <TH bgcolor=$color>$ref</TH>
1206</TR>
1207''')
1208 code('''
1209<!- Column footer->
1210<TR>
1211 <TH> </TH>
1212''')
1213
1214 for event in self.events.itervalues():
1215 href = "%s_Event_%s.html" % (self.ident, event.ident)
1216 ref = self.frameRef(href, "Status", href, "1", event.short)
1217 code('<TH bgcolor=white>$ref</TH>')
1218 code('''
1219</TR>
1220</TABLE>
1221</BODY></HTML>
1222''')
1223
1224
1225 if active_state:
1226 name = "%s_table_%s.html" % (self.ident, active_state.ident)
1227 else:
1228 name = "%s_table.html" % self.ident
1229 code.write(path, name)
1230
1231__all__ = [ "StateMachine" ]
655 param.type_ast.type.ident == "MemoryControl":
656 assert(param.pointer)
657 code(' m_${{param.ident}}_ptr->printStats(out);')
658
659 code('''
660 s_profiler.dumpStats(out);
661}
662
663void $c_ident::clearStats() {
664''')
665 #
666 # Cache and Memory Controllers have specific profilers associated with
667 # them. These stats must be cleared too.
668 #
669 for param in self.config_parameters:
670 if param.type_ast.type.ident == "CacheMemory" or \
671 param.type_ast.type.ident == "MemoryControl":
672 assert(param.pointer)
673 code(' m_${{param.ident}}_ptr->clearStats();')
674
675 code('''
676 s_profiler.clearStats();
677}
678
679// Actions
680''')
681
682 for action in self.actions.itervalues():
683 if "c_code" not in action:
684 continue
685
686 code('''
687/** \\brief ${{action.desc}} */
688void
689$c_ident::${{action.ident}}(const Address& addr)
690{
691 DEBUG_MSG(GENERATED_COMP, HighPrio, "executing");
692 ${{action["c_code"]}}
693}
694
695''')
696 code.write(path, "%s.cc" % c_ident)
697
698 def printCWakeup(self, path):
699 '''Output the wakeup loop for the events'''
700
701 code = self.symtab.codeFormatter()
702 ident = self.ident
703
704 code('''
705// Auto generated C++ code started by $__file__:$__line__
706// ${ident}: ${{self.short}}
707
708#include "base/misc.hh"
709#include "mem/ruby/common/Global.hh"
710#include "mem/ruby/slicc_interface/RubySlicc_includes.hh"
711#include "mem/protocol/${ident}_Controller.hh"
712#include "mem/protocol/${ident}_State.hh"
713#include "mem/protocol/${ident}_Event.hh"
714#include "mem/protocol/Types.hh"
715#include "mem/ruby/system/System.hh"
716
717void
718${ident}_Controller::wakeup()
719{
720 // DEBUG_EXPR(GENERATED_COMP, MedPrio, *this);
721 // DEBUG_EXPR(GENERATED_COMP, MedPrio, g_eventQueue_ptr->getTime());
722
723 int counter = 0;
724 while (true) {
725 // Some cases will put us into an infinite loop without this limit
726 assert(counter <= m_transitions_per_cycle);
727 if (counter == m_transitions_per_cycle) {
728 // Count how often we are fully utilized
729 g_system_ptr->getProfiler()->controllerBusy(m_machineID);
730
731 // Wakeup in another cycle and try again
732 g_eventQueue_ptr->scheduleEvent(this, 1);
733 break;
734 }
735''')
736
737 code.indent()
738 code.indent()
739
740 # InPorts
741 #
742 for port in self.in_ports:
743 code.indent()
744 code('// ${ident}InPort $port')
745 code('${{port["c_code_in_port"]}}')
746 code.dedent()
747
748 code('')
749
750 code.dedent()
751 code.dedent()
752 code('''
753 break; // If we got this far, we have nothing left todo
754 }
755 // g_eventQueue_ptr->scheduleEvent(this, 1);
756 // DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
757}
758''')
759
760 code.write(path, "%s_Wakeup.cc" % self.ident)
761
762 def printCSwitch(self, path):
763 '''Output switch statement for transition table'''
764
765 code = self.symtab.codeFormatter()
766 ident = self.ident
767
768 code('''
769// Auto generated C++ code started by $__file__:$__line__
770// ${ident}: ${{self.short}}
771
772#include "mem/ruby/common/Global.hh"
773#include "mem/protocol/${ident}_Controller.hh"
774#include "mem/protocol/${ident}_State.hh"
775#include "mem/protocol/${ident}_Event.hh"
776#include "mem/protocol/Types.hh"
777#include "mem/ruby/system/System.hh"
778
779#define HASH_FUN(state, event) ((int(state)*${ident}_Event_NUM)+int(event))
780
781#define GET_TRANSITION_COMMENT() (${ident}_transitionComment.str())
782#define CLEAR_TRANSITION_COMMENT() (${ident}_transitionComment.str(""))
783
784TransitionResult
785${ident}_Controller::doTransition(${ident}_Event event,
786 ${ident}_State state,
787 const Address &addr)
788{
789 ${ident}_State next_state = state;
790
791 DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
792 DEBUG_MSG(GENERATED_COMP, MedPrio, *this);
793 DEBUG_EXPR(GENERATED_COMP, MedPrio, g_eventQueue_ptr->getTime());
794 DEBUG_EXPR(GENERATED_COMP, MedPrio,state);
795 DEBUG_EXPR(GENERATED_COMP, MedPrio,event);
796 DEBUG_EXPR(GENERATED_COMP, MedPrio,addr);
797
798 TransitionResult result =
799 doTransitionWorker(event, state, next_state, addr);
800
801 if (result == TransitionResult_Valid) {
802 DEBUG_EXPR(GENERATED_COMP, MedPrio, next_state);
803 DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
804 s_profiler.countTransition(state, event);
805 if (Debug::getProtocolTrace()) {
806 g_system_ptr->getProfiler()->profileTransition("${ident}",
807 m_version, addr,
808 ${ident}_State_to_string(state),
809 ${ident}_Event_to_string(event),
810 ${ident}_State_to_string(next_state),
811 GET_TRANSITION_COMMENT());
812 }
813 CLEAR_TRANSITION_COMMENT();
814 ${ident}_setState(addr, next_state);
815
816 } else if (result == TransitionResult_ResourceStall) {
817 if (Debug::getProtocolTrace()) {
818 g_system_ptr->getProfiler()->profileTransition("${ident}",
819 m_version, addr,
820 ${ident}_State_to_string(state),
821 ${ident}_Event_to_string(event),
822 ${ident}_State_to_string(next_state),
823 "Resource Stall");
824 }
825 } else if (result == TransitionResult_ProtocolStall) {
826 DEBUG_MSG(GENERATED_COMP, HighPrio, "stalling");
827 DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
828 if (Debug::getProtocolTrace()) {
829 g_system_ptr->getProfiler()->profileTransition("${ident}",
830 m_version, addr,
831 ${ident}_State_to_string(state),
832 ${ident}_Event_to_string(event),
833 ${ident}_State_to_string(next_state),
834 "Protocol Stall");
835 }
836 }
837
838 return result;
839}
840
841TransitionResult
842${ident}_Controller::doTransitionWorker(${ident}_Event event,
843 ${ident}_State state,
844 ${ident}_State& next_state,
845 const Address& addr)
846{
847 switch(HASH_FUN(state, event)) {
848''')
849
850 # This map will allow suppress generating duplicate code
851 cases = orderdict()
852
853 for trans in self.transitions:
854 case_string = "%s_State_%s, %s_Event_%s" % \
855 (self.ident, trans.state.ident, self.ident, trans.event.ident)
856
857 case = self.symtab.codeFormatter()
858 # Only set next_state if it changes
859 if trans.state != trans.nextState:
860 ns_ident = trans.nextState.ident
861 case('next_state = ${ident}_State_${ns_ident};')
862
863 actions = trans.actions
864
865 # Check for resources
866 case_sorter = []
867 res = trans.resources
868 for key,val in res.iteritems():
869 if key.type.ident != "DNUCAStopTable":
870 val = '''
871if (!%s.areNSlotsAvailable(%s))
872 return TransitionResult_ResourceStall;
873''' % (key.code, val)
874 case_sorter.append(val)
875
876
877 # Emit the code sequences in a sorted order. This makes the
878 # output deterministic (without this the output order can vary
879 # since Map's keys() on a vector of pointers is not deterministic
880 for c in sorted(case_sorter):
881 case("$c")
882
883 # Figure out if we stall
884 stall = False
885 for action in actions:
886 if action.ident == "z_stall":
887 stall = True
888 break
889
890 if stall:
891 case('return TransitionResult_ProtocolStall;')
892 else:
893 for action in actions:
894 case('${{action.ident}}(addr);')
895 case('return TransitionResult_Valid;')
896
897 case = str(case)
898
899 # Look to see if this transition code is unique.
900 if case not in cases:
901 cases[case] = []
902
903 cases[case].append(case_string)
904
905 # Walk through all of the unique code blocks and spit out the
906 # corresponding case statement elements
907 for case,transitions in cases.iteritems():
908 # Iterative over all the multiple transitions that share
909 # the same code
910 for trans in transitions:
911 code(' case HASH_FUN($trans):')
912 code(' $case')
913
914 code('''
915 default:
916 WARN_EXPR(m_version);
917 WARN_EXPR(g_eventQueue_ptr->getTime());
918 WARN_EXPR(addr);
919 WARN_EXPR(event);
920 WARN_EXPR(state);
921 ERROR_MSG(\"Invalid transition\");
922 }
923 return TransitionResult_Valid;
924}
925''')
926 code.write(path, "%s_Transitions.cc" % self.ident)
927
928 def printProfilerHH(self, path):
929 code = self.symtab.codeFormatter()
930 ident = self.ident
931
932 code('''
933// Auto generated C++ code started by $__file__:$__line__
934// ${ident}: ${{self.short}}
935
936#ifndef __${ident}_PROFILER_HH_
937#define __${ident}_PROFILER_HH_
938
939#include <iostream>
940
941#include "mem/ruby/common/Global.hh"
942#include "mem/protocol/${ident}_State.hh"
943#include "mem/protocol/${ident}_Event.hh"
944
945class ${ident}_Profiler
946{
947 public:
948 ${ident}_Profiler();
949 void setVersion(int version);
950 void countTransition(${ident}_State state, ${ident}_Event event);
951 void possibleTransition(${ident}_State state, ${ident}_Event event);
952 void dumpStats(std::ostream& out) const;
953 void clearStats();
954
955 private:
956 int m_counters[${ident}_State_NUM][${ident}_Event_NUM];
957 int m_event_counters[${ident}_Event_NUM];
958 bool m_possible[${ident}_State_NUM][${ident}_Event_NUM];
959 int m_version;
960};
961
962#endif // __${ident}_PROFILER_HH__
963''')
964 code.write(path, "%s_Profiler.hh" % self.ident)
965
966 def printProfilerCC(self, path):
967 code = self.symtab.codeFormatter()
968 ident = self.ident
969
970 code('''
971// Auto generated C++ code started by $__file__:$__line__
972// ${ident}: ${{self.short}}
973
974#include "mem/protocol/${ident}_Profiler.hh"
975
976${ident}_Profiler::${ident}_Profiler()
977{
978 for (int state = 0; state < ${ident}_State_NUM; state++) {
979 for (int event = 0; event < ${ident}_Event_NUM; event++) {
980 m_possible[state][event] = false;
981 m_counters[state][event] = 0;
982 }
983 }
984 for (int event = 0; event < ${ident}_Event_NUM; event++) {
985 m_event_counters[event] = 0;
986 }
987}
988
989void
990${ident}_Profiler::setVersion(int version)
991{
992 m_version = version;
993}
994
995void
996${ident}_Profiler::clearStats()
997{
998 for (int state = 0; state < ${ident}_State_NUM; state++) {
999 for (int event = 0; event < ${ident}_Event_NUM; event++) {
1000 m_counters[state][event] = 0;
1001 }
1002 }
1003
1004 for (int event = 0; event < ${ident}_Event_NUM; event++) {
1005 m_event_counters[event] = 0;
1006 }
1007}
1008void
1009${ident}_Profiler::countTransition(${ident}_State state, ${ident}_Event event)
1010{
1011 assert(m_possible[state][event]);
1012 m_counters[state][event]++;
1013 m_event_counters[event]++;
1014}
1015void
1016${ident}_Profiler::possibleTransition(${ident}_State state,
1017 ${ident}_Event event)
1018{
1019 m_possible[state][event] = true;
1020}
1021
1022void
1023${ident}_Profiler::dumpStats(std::ostream& out) const
1024{
1025 using namespace std;
1026
1027 out << " --- ${ident} " << m_version << " ---" << endl;
1028 out << " - Event Counts -" << endl;
1029 for (int event = 0; event < ${ident}_Event_NUM; event++) {
1030 int count = m_event_counters[event];
1031 out << (${ident}_Event) event << " " << count << endl;
1032 }
1033 out << endl;
1034 out << " - Transitions -" << endl;
1035 for (int state = 0; state < ${ident}_State_NUM; state++) {
1036 for (int event = 0; event < ${ident}_Event_NUM; event++) {
1037 if (m_possible[state][event]) {
1038 int count = m_counters[state][event];
1039 out << (${ident}_State) state << " "
1040 << (${ident}_Event) event << " " << count;
1041 if (count == 0) {
1042 out << " <-- ";
1043 }
1044 out << endl;
1045 }
1046 }
1047 out << endl;
1048 }
1049}
1050''')
1051 code.write(path, "%s_Profiler.cc" % self.ident)
1052
1053 # **************************
1054 # ******* HTML Files *******
1055 # **************************
1056 def frameRef(self, click_href, click_target, over_href, over_num, text):
1057 code = self.symtab.codeFormatter(fix_newlines=False)
1058 code("""<A href=\"$click_href\" target=\"$click_target\" onmouseover=\"
1059 if (parent.frames[$over_num].location != parent.location + '$over_href') {
1060 parent.frames[$over_num].location='$over_href'
1061 }\">
1062 ${{html.formatShorthand(text)}}
1063 </A>""")
1064 return str(code)
1065
1066 def writeHTMLFiles(self, path):
1067 # Create table with no row hilighted
1068 self.printHTMLTransitions(path, None)
1069
1070 # Generate transition tables
1071 for state in self.states.itervalues():
1072 self.printHTMLTransitions(path, state)
1073
1074 # Generate action descriptions
1075 for action in self.actions.itervalues():
1076 name = "%s_action_%s.html" % (self.ident, action.ident)
1077 code = html.createSymbol(action, "Action")
1078 code.write(path, name)
1079
1080 # Generate state descriptions
1081 for state in self.states.itervalues():
1082 name = "%s_State_%s.html" % (self.ident, state.ident)
1083 code = html.createSymbol(state, "State")
1084 code.write(path, name)
1085
1086 # Generate event descriptions
1087 for event in self.events.itervalues():
1088 name = "%s_Event_%s.html" % (self.ident, event.ident)
1089 code = html.createSymbol(event, "Event")
1090 code.write(path, name)
1091
1092 def printHTMLTransitions(self, path, active_state):
1093 code = self.symtab.codeFormatter()
1094
1095 code('''
1096<HTML>
1097<BODY link="blue" vlink="blue">
1098
1099<H1 align="center">${{html.formatShorthand(self.short)}}:
1100''')
1101 code.indent()
1102 for i,machine in enumerate(self.symtab.getAllType(StateMachine)):
1103 mid = machine.ident
1104 if i != 0:
1105 extra = " - "
1106 else:
1107 extra = ""
1108 if machine == self:
1109 code('$extra$mid')
1110 else:
1111 code('$extra<A target="Table" href="${mid}_table.html">$mid</A>')
1112 code.dedent()
1113
1114 code("""
1115</H1>
1116
1117<TABLE border=1>
1118<TR>
1119 <TH> </TH>
1120""")
1121
1122 for event in self.events.itervalues():
1123 href = "%s_Event_%s.html" % (self.ident, event.ident)
1124 ref = self.frameRef(href, "Status", href, "1", event.short)
1125 code('<TH bgcolor=white>$ref</TH>')
1126
1127 code('</TR>')
1128 # -- Body of table
1129 for state in self.states.itervalues():
1130 # -- Each row
1131 if state == active_state:
1132 color = "yellow"
1133 else:
1134 color = "white"
1135
1136 click = "%s_table_%s.html" % (self.ident, state.ident)
1137 over = "%s_State_%s.html" % (self.ident, state.ident)
1138 text = html.formatShorthand(state.short)
1139 ref = self.frameRef(click, "Table", over, "1", state.short)
1140 code('''
1141<TR>
1142 <TH bgcolor=$color>$ref</TH>
1143''')
1144
1145 # -- One column for each event
1146 for event in self.events.itervalues():
1147 trans = self.table.get((state,event), None)
1148 if trans is None:
1149 # This is the no transition case
1150 if state == active_state:
1151 color = "#C0C000"
1152 else:
1153 color = "lightgrey"
1154
1155 code('<TD bgcolor=$color>&nbsp;</TD>')
1156 continue
1157
1158 next = trans.nextState
1159 stall_action = False
1160
1161 # -- Get the actions
1162 for action in trans.actions:
1163 if action.ident == "z_stall" or \
1164 action.ident == "zz_recycleMandatoryQueue":
1165 stall_action = True
1166
1167 # -- Print out "actions/next-state"
1168 if stall_action:
1169 if state == active_state:
1170 color = "#C0C000"
1171 else:
1172 color = "lightgrey"
1173
1174 elif active_state and next.ident == active_state.ident:
1175 color = "aqua"
1176 elif state == active_state:
1177 color = "yellow"
1178 else:
1179 color = "white"
1180
1181 code('<TD bgcolor=$color>')
1182 for action in trans.actions:
1183 href = "%s_action_%s.html" % (self.ident, action.ident)
1184 ref = self.frameRef(href, "Status", href, "1",
1185 action.short)
1186 code(' $ref')
1187 if next != state:
1188 if trans.actions:
1189 code('/')
1190 click = "%s_table_%s.html" % (self.ident, next.ident)
1191 over = "%s_State_%s.html" % (self.ident, next.ident)
1192 ref = self.frameRef(click, "Table", over, "1", next.short)
1193 code("$ref")
1194 code("</TD>")
1195
1196 # -- Each row
1197 if state == active_state:
1198 color = "yellow"
1199 else:
1200 color = "white"
1201
1202 click = "%s_table_%s.html" % (self.ident, state.ident)
1203 over = "%s_State_%s.html" % (self.ident, state.ident)
1204 ref = self.frameRef(click, "Table", over, "1", state.short)
1205 code('''
1206 <TH bgcolor=$color>$ref</TH>
1207</TR>
1208''')
1209 code('''
1210<!- Column footer->
1211<TR>
1212 <TH> </TH>
1213''')
1214
1215 for event in self.events.itervalues():
1216 href = "%s_Event_%s.html" % (self.ident, event.ident)
1217 ref = self.frameRef(href, "Status", href, "1", event.short)
1218 code('<TH bgcolor=white>$ref</TH>')
1219 code('''
1220</TR>
1221</TABLE>
1222</BODY></HTML>
1223''')
1224
1225
1226 if active_state:
1227 name = "%s_table_%s.html" % (self.ident, active_state.ident)
1228 else:
1229 name = "%s_table.html" % self.ident
1230 code.write(path, name)
1231
1232__all__ = [ "StateMachine" ]