a58c1e9c73f3c44509583388a52c0bb20c03cbcf
[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 "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
341 using 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
358 int $c_ident::m_num_controllers = 0;
359
360 // for adding information to the protocol debug trace
361 stringstream ${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('''
402 m_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('''
414 m_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
426 void
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('''
498 machine_type = string_to_MachineType("${{var.machine.ident}}");
499 base = 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('''
522 if (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('''
576 int
577 $c_ident::getNumControllers()
578 {
579 return m_num_controllers;
580 }
581
582 MessageBuffer*
583 $c_ident::getMandatoryQueue() const
584 {
585 return $mq_ident;
586 }
587
588 const int &
589 $c_ident::getVersion() const
590 {
591 return m_version;
592 }
593
594 const string
595 $c_ident::toString() const
596 {
597 return "$c_ident";
598 }
599
600 const string
601 $c_ident::getName() const
602 {
603 return m_name;
604 }
605
606 const MachineType
607 $c_ident::getMachineType() const
608 {
609 return MachineType_${ident};
610 }
611
612 void
613 $c_ident::blockOnQueue(Address addr, MessageBuffer* port)
614 {
615 m_is_blocking = true;
616 m_block_map[addr] = port;
617 }
618
619 void
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
628 void
629 $c_ident::print(ostream& out) const
630 {
631 out << "[$c_ident " << m_version << "]";
632 }
633
634 void
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
644 void
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 \
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
663 void $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}} */
688 void
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
717 using namespace std;
718
719 void
720 ${ident}_Controller::wakeup()
721 {
722 // DEBUG_EXPR(GENERATED_COMP, MedPrio, *this);
723 // DEBUG_EXPR(GENERATED_COMP, MedPrio, g_eventQueue_ptr->getTime());
724
725 int counter = 0;
726 while (true) {
727 // Some cases will put us into an infinite loop without this limit
728 assert(counter <= m_transitions_per_cycle);
729 if (counter == m_transitions_per_cycle) {
730 // Count how often we are fully utilized
731 g_system_ptr->getProfiler()->controllerBusy(m_machineID);
732
733 // Wakeup in another cycle and try again
734 g_eventQueue_ptr->scheduleEvent(this, 1);
735 break;
736 }
737 ''')
738
739 code.indent()
740 code.indent()
741
742 # InPorts
743 #
744 for port in self.in_ports:
745 code.indent()
746 code('// ${ident}InPort $port')
747 code('${{port["c_code_in_port"]}}')
748 code.dedent()
749
750 code('')
751
752 code.dedent()
753 code.dedent()
754 code('''
755 break; // If we got this far, we have nothing left todo
756 }
757 // g_eventQueue_ptr->scheduleEvent(this, 1);
758 // DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
759 }
760 ''')
761
762 code.write(path, "%s_Wakeup.cc" % self.ident)
763
764 def printCSwitch(self, path):
765 '''Output switch statement for transition table'''
766
767 code = self.symtab.codeFormatter()
768 ident = self.ident
769
770 code('''
771 // Auto generated C++ code started by $__file__:$__line__
772 // ${ident}: ${{self.short}}
773
774 #include "mem/ruby/common/Global.hh"
775 #include "mem/protocol/${ident}_Controller.hh"
776 #include "mem/protocol/${ident}_State.hh"
777 #include "mem/protocol/${ident}_Event.hh"
778 #include "mem/protocol/Types.hh"
779 #include "mem/ruby/system/System.hh"
780
781 #define HASH_FUN(state, event) ((int(state)*${ident}_Event_NUM)+int(event))
782
783 #define GET_TRANSITION_COMMENT() (${ident}_transitionComment.str())
784 #define CLEAR_TRANSITION_COMMENT() (${ident}_transitionComment.str(""))
785
786 TransitionResult
787 ${ident}_Controller::doTransition(${ident}_Event event,
788 ${ident}_State state,
789 const Address &addr)
790 {
791 ${ident}_State next_state = state;
792
793 DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
794 DEBUG_MSG(GENERATED_COMP, MedPrio, *this);
795 DEBUG_EXPR(GENERATED_COMP, MedPrio, g_eventQueue_ptr->getTime());
796 DEBUG_EXPR(GENERATED_COMP, MedPrio,state);
797 DEBUG_EXPR(GENERATED_COMP, MedPrio,event);
798 DEBUG_EXPR(GENERATED_COMP, MedPrio,addr);
799
800 TransitionResult result =
801 doTransitionWorker(event, state, next_state, addr);
802
803 if (result == TransitionResult_Valid) {
804 DEBUG_EXPR(GENERATED_COMP, MedPrio, next_state);
805 DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
806 s_profiler.countTransition(state, event);
807 if (Debug::getProtocolTrace()) {
808 g_system_ptr->getProfiler()->profileTransition("${ident}",
809 m_version, addr,
810 ${ident}_State_to_string(state),
811 ${ident}_Event_to_string(event),
812 ${ident}_State_to_string(next_state),
813 GET_TRANSITION_COMMENT());
814 }
815 CLEAR_TRANSITION_COMMENT();
816 ${ident}_setState(addr, next_state);
817
818 } else if (result == TransitionResult_ResourceStall) {
819 if (Debug::getProtocolTrace()) {
820 g_system_ptr->getProfiler()->profileTransition("${ident}",
821 m_version, addr,
822 ${ident}_State_to_string(state),
823 ${ident}_Event_to_string(event),
824 ${ident}_State_to_string(next_state),
825 "Resource Stall");
826 }
827 } else if (result == TransitionResult_ProtocolStall) {
828 DEBUG_MSG(GENERATED_COMP, HighPrio, "stalling");
829 DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
830 if (Debug::getProtocolTrace()) {
831 g_system_ptr->getProfiler()->profileTransition("${ident}",
832 m_version, addr,
833 ${ident}_State_to_string(state),
834 ${ident}_Event_to_string(event),
835 ${ident}_State_to_string(next_state),
836 "Protocol Stall");
837 }
838 }
839
840 return result;
841 }
842
843 TransitionResult
844 ${ident}_Controller::doTransitionWorker(${ident}_Event event,
845 ${ident}_State state,
846 ${ident}_State& next_state,
847 const Address& addr)
848 {
849 switch(HASH_FUN(state, event)) {
850 ''')
851
852 # This map will allow suppress generating duplicate code
853 cases = orderdict()
854
855 for trans in self.transitions:
856 case_string = "%s_State_%s, %s_Event_%s" % \
857 (self.ident, trans.state.ident, self.ident, trans.event.ident)
858
859 case = self.symtab.codeFormatter()
860 # Only set next_state if it changes
861 if trans.state != trans.nextState:
862 ns_ident = trans.nextState.ident
863 case('next_state = ${ident}_State_${ns_ident};')
864
865 actions = trans.actions
866
867 # Check for resources
868 case_sorter = []
869 res = trans.resources
870 for key,val in res.iteritems():
871 if key.type.ident != "DNUCAStopTable":
872 val = '''
873 if (!%s.areNSlotsAvailable(%s))
874 return TransitionResult_ResourceStall;
875 ''' % (key.code, val)
876 case_sorter.append(val)
877
878
879 # Emit the code sequences in a sorted order. This makes the
880 # output deterministic (without this the output order can vary
881 # since Map's keys() on a vector of pointers is not deterministic
882 for c in sorted(case_sorter):
883 case("$c")
884
885 # Figure out if we stall
886 stall = False
887 for action in actions:
888 if action.ident == "z_stall":
889 stall = True
890 break
891
892 if stall:
893 case('return TransitionResult_ProtocolStall;')
894 else:
895 for action in actions:
896 case('${{action.ident}}(addr);')
897 case('return TransitionResult_Valid;')
898
899 case = str(case)
900
901 # Look to see if this transition code is unique.
902 if case not in cases:
903 cases[case] = []
904
905 cases[case].append(case_string)
906
907 # Walk through all of the unique code blocks and spit out the
908 # corresponding case statement elements
909 for case,transitions in cases.iteritems():
910 # Iterative over all the multiple transitions that share
911 # the same code
912 for trans in transitions:
913 code(' case HASH_FUN($trans):')
914 code(' $case')
915
916 code('''
917 default:
918 WARN_EXPR(m_version);
919 WARN_EXPR(g_eventQueue_ptr->getTime());
920 WARN_EXPR(addr);
921 WARN_EXPR(event);
922 WARN_EXPR(state);
923 ERROR_MSG(\"Invalid transition\");
924 }
925 return TransitionResult_Valid;
926 }
927 ''')
928 code.write(path, "%s_Transitions.cc" % self.ident)
929
930 def printProfilerHH(self, path):
931 code = self.symtab.codeFormatter()
932 ident = self.ident
933
934 code('''
935 // Auto generated C++ code started by $__file__:$__line__
936 // ${ident}: ${{self.short}}
937
938 #ifndef __${ident}_PROFILER_HH_
939 #define __${ident}_PROFILER_HH_
940
941 #include <iostream>
942
943 #include "mem/ruby/common/Global.hh"
944 #include "mem/protocol/${ident}_State.hh"
945 #include "mem/protocol/${ident}_Event.hh"
946
947 class ${ident}_Profiler
948 {
949 public:
950 ${ident}_Profiler();
951 void setVersion(int version);
952 void countTransition(${ident}_State state, ${ident}_Event event);
953 void possibleTransition(${ident}_State state, ${ident}_Event event);
954 void dumpStats(std::ostream& out) const;
955 void clearStats();
956
957 private:
958 int m_counters[${ident}_State_NUM][${ident}_Event_NUM];
959 int m_event_counters[${ident}_Event_NUM];
960 bool m_possible[${ident}_State_NUM][${ident}_Event_NUM];
961 int m_version;
962 };
963
964 #endif // __${ident}_PROFILER_HH__
965 ''')
966 code.write(path, "%s_Profiler.hh" % self.ident)
967
968 def printProfilerCC(self, path):
969 code = self.symtab.codeFormatter()
970 ident = self.ident
971
972 code('''
973 // Auto generated C++ code started by $__file__:$__line__
974 // ${ident}: ${{self.short}}
975
976 #include "mem/protocol/${ident}_Profiler.hh"
977
978 ${ident}_Profiler::${ident}_Profiler()
979 {
980 for (int state = 0; state < ${ident}_State_NUM; state++) {
981 for (int event = 0; event < ${ident}_Event_NUM; event++) {
982 m_possible[state][event] = false;
983 m_counters[state][event] = 0;
984 }
985 }
986 for (int event = 0; event < ${ident}_Event_NUM; event++) {
987 m_event_counters[event] = 0;
988 }
989 }
990
991 void
992 ${ident}_Profiler::setVersion(int version)
993 {
994 m_version = version;
995 }
996
997 void
998 ${ident}_Profiler::clearStats()
999 {
1000 for (int state = 0; state < ${ident}_State_NUM; state++) {
1001 for (int event = 0; event < ${ident}_Event_NUM; event++) {
1002 m_counters[state][event] = 0;
1003 }
1004 }
1005
1006 for (int event = 0; event < ${ident}_Event_NUM; event++) {
1007 m_event_counters[event] = 0;
1008 }
1009 }
1010 void
1011 ${ident}_Profiler::countTransition(${ident}_State state, ${ident}_Event event)
1012 {
1013 assert(m_possible[state][event]);
1014 m_counters[state][event]++;
1015 m_event_counters[event]++;
1016 }
1017 void
1018 ${ident}_Profiler::possibleTransition(${ident}_State state,
1019 ${ident}_Event event)
1020 {
1021 m_possible[state][event] = true;
1022 }
1023
1024 void
1025 ${ident}_Profiler::dumpStats(std::ostream& out) const
1026 {
1027 using namespace std;
1028
1029 out << " --- ${ident} " << m_version << " ---" << endl;
1030 out << " - Event Counts -" << endl;
1031 for (int event = 0; event < ${ident}_Event_NUM; event++) {
1032 int count = m_event_counters[event];
1033 out << (${ident}_Event) event << " " << count << endl;
1034 }
1035 out << endl;
1036 out << " - Transitions -" << endl;
1037 for (int state = 0; state < ${ident}_State_NUM; state++) {
1038 for (int event = 0; event < ${ident}_Event_NUM; event++) {
1039 if (m_possible[state][event]) {
1040 int count = m_counters[state][event];
1041 out << (${ident}_State) state << " "
1042 << (${ident}_Event) event << " " << count;
1043 if (count == 0) {
1044 out << " <-- ";
1045 }
1046 out << endl;
1047 }
1048 }
1049 out << endl;
1050 }
1051 }
1052 ''')
1053 code.write(path, "%s_Profiler.cc" % self.ident)
1054
1055 # **************************
1056 # ******* HTML Files *******
1057 # **************************
1058 def frameRef(self, click_href, click_target, over_href, over_num, text):
1059 code = self.symtab.codeFormatter(fix_newlines=False)
1060 code("""<A href=\"$click_href\" target=\"$click_target\" onmouseover=\"
1061 if (parent.frames[$over_num].location != parent.location + '$over_href') {
1062 parent.frames[$over_num].location='$over_href'
1063 }\">
1064 ${{html.formatShorthand(text)}}
1065 </A>""")
1066 return str(code)
1067
1068 def writeHTMLFiles(self, path):
1069 # Create table with no row hilighted
1070 self.printHTMLTransitions(path, None)
1071
1072 # Generate transition tables
1073 for state in self.states.itervalues():
1074 self.printHTMLTransitions(path, state)
1075
1076 # Generate action descriptions
1077 for action in self.actions.itervalues():
1078 name = "%s_action_%s.html" % (self.ident, action.ident)
1079 code = html.createSymbol(action, "Action")
1080 code.write(path, name)
1081
1082 # Generate state descriptions
1083 for state in self.states.itervalues():
1084 name = "%s_State_%s.html" % (self.ident, state.ident)
1085 code = html.createSymbol(state, "State")
1086 code.write(path, name)
1087
1088 # Generate event descriptions
1089 for event in self.events.itervalues():
1090 name = "%s_Event_%s.html" % (self.ident, event.ident)
1091 code = html.createSymbol(event, "Event")
1092 code.write(path, name)
1093
1094 def printHTMLTransitions(self, path, active_state):
1095 code = self.symtab.codeFormatter()
1096
1097 code('''
1098 <HTML>
1099 <BODY link="blue" vlink="blue">
1100
1101 <H1 align="center">${{html.formatShorthand(self.short)}}:
1102 ''')
1103 code.indent()
1104 for i,machine in enumerate(self.symtab.getAllType(StateMachine)):
1105 mid = machine.ident
1106 if i != 0:
1107 extra = " - "
1108 else:
1109 extra = ""
1110 if machine == self:
1111 code('$extra$mid')
1112 else:
1113 code('$extra<A target="Table" href="${mid}_table.html">$mid</A>')
1114 code.dedent()
1115
1116 code("""
1117 </H1>
1118
1119 <TABLE border=1>
1120 <TR>
1121 <TH> </TH>
1122 """)
1123
1124 for event in self.events.itervalues():
1125 href = "%s_Event_%s.html" % (self.ident, event.ident)
1126 ref = self.frameRef(href, "Status", href, "1", event.short)
1127 code('<TH bgcolor=white>$ref</TH>')
1128
1129 code('</TR>')
1130 # -- Body of table
1131 for state in self.states.itervalues():
1132 # -- Each row
1133 if state == active_state:
1134 color = "yellow"
1135 else:
1136 color = "white"
1137
1138 click = "%s_table_%s.html" % (self.ident, state.ident)
1139 over = "%s_State_%s.html" % (self.ident, state.ident)
1140 text = html.formatShorthand(state.short)
1141 ref = self.frameRef(click, "Table", over, "1", state.short)
1142 code('''
1143 <TR>
1144 <TH bgcolor=$color>$ref</TH>
1145 ''')
1146
1147 # -- One column for each event
1148 for event in self.events.itervalues():
1149 trans = self.table.get((state,event), None)
1150 if trans is None:
1151 # This is the no transition case
1152 if state == active_state:
1153 color = "#C0C000"
1154 else:
1155 color = "lightgrey"
1156
1157 code('<TD bgcolor=$color>&nbsp;</TD>')
1158 continue
1159
1160 next = trans.nextState
1161 stall_action = False
1162
1163 # -- Get the actions
1164 for action in trans.actions:
1165 if action.ident == "z_stall" or \
1166 action.ident == "zz_recycleMandatoryQueue":
1167 stall_action = True
1168
1169 # -- Print out "actions/next-state"
1170 if stall_action:
1171 if state == active_state:
1172 color = "#C0C000"
1173 else:
1174 color = "lightgrey"
1175
1176 elif active_state and next.ident == active_state.ident:
1177 color = "aqua"
1178 elif state == active_state:
1179 color = "yellow"
1180 else:
1181 color = "white"
1182
1183 code('<TD bgcolor=$color>')
1184 for action in trans.actions:
1185 href = "%s_action_%s.html" % (self.ident, action.ident)
1186 ref = self.frameRef(href, "Status", href, "1",
1187 action.short)
1188 code(' $ref')
1189 if next != state:
1190 if trans.actions:
1191 code('/')
1192 click = "%s_table_%s.html" % (self.ident, next.ident)
1193 over = "%s_State_%s.html" % (self.ident, next.ident)
1194 ref = self.frameRef(click, "Table", over, "1", next.short)
1195 code("$ref")
1196 code("</TD>")
1197
1198 # -- Each row
1199 if state == active_state:
1200 color = "yellow"
1201 else:
1202 color = "white"
1203
1204 click = "%s_table_%s.html" % (self.ident, state.ident)
1205 over = "%s_State_%s.html" % (self.ident, state.ident)
1206 ref = self.frameRef(click, "Table", over, "1", state.short)
1207 code('''
1208 <TH bgcolor=$color>$ref</TH>
1209 </TR>
1210 ''')
1211 code('''
1212 <!- Column footer->
1213 <TR>
1214 <TH> </TH>
1215 ''')
1216
1217 for event in self.events.itervalues():
1218 href = "%s_Event_%s.html" % (self.ident, event.ident)
1219 ref = self.frameRef(href, "Status", href, "1", event.short)
1220 code('<TH bgcolor=white>$ref</TH>')
1221 code('''
1222 </TR>
1223 </TABLE>
1224 </BODY></HTML>
1225 ''')
1226
1227
1228 if active_state:
1229 name = "%s_table_%s.html" % (self.ident, active_state.ident)
1230 else:
1231 name = "%s_table.html" % self.ident
1232 code.write(path, name)
1233
1234 __all__ = [ "StateMachine" ]