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