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