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