StateMachine.py (10963:51f40b101a56) StateMachine.py (10964:2b4fe083d17b)
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 # determine the port->msg buffer mappings
183 def getBufferMaps(self, ident):
184 msg_bufs = []
185 port_to_buf_map = {}
186 in_msg_bufs = {}
187 for port in self.in_ports:
188 buf_name = "m_%s_ptr" % port.buffer_expr.name
189 msg_bufs.append(buf_name)
190 port_to_buf_map[port] = msg_bufs.index(buf_name)
191 if buf_name not in in_msg_bufs:
192 in_msg_bufs[buf_name] = [port]
193 else:
194 in_msg_bufs[buf_name].append(port)
195 return port_to_buf_map, in_msg_bufs, msg_bufs
196
197 def writeCodeFiles(self, path, includes):
198 self.printControllerPython(path)
199 self.printControllerHH(path)
200 self.printControllerCC(path, includes)
201 self.printCSwitch(path)
202 self.printCWakeup(path, includes)
203
204 def printControllerPython(self, path):
205 code = self.symtab.codeFormatter()
206 ident = self.ident
207
208 py_ident = "%s_Controller" % ident
209 c_ident = "%s_Controller" % self.ident
210
211 code('''
212from m5.params import *
213from m5.SimObject import SimObject
214from Controller import RubyController
215
216class $py_ident(RubyController):
217 type = '$py_ident'
218 cxx_header = 'mem/protocol/${c_ident}.hh'
219''')
220 code.indent()
221 for param in self.config_parameters:
222 dflt_str = ''
223
224 if param.rvalue is not None:
225 dflt_str = str(param.rvalue.inline()) + ', '
226
227 if param.type_ast.type.c_ident == "MessageBuffer":
228 if param["network"] == "To":
229 code('${{param.ident}} = MasterPort(${dflt_str}"")')
230 else:
231 code('${{param.ident}} = SlavePort(${dflt_str}"")')
232
233 elif python_class_map.has_key(param.type_ast.type.c_ident):
234 python_type = python_class_map[param.type_ast.type.c_ident]
235 code('${{param.ident}} = Param.${{python_type}}(${dflt_str}"")')
236
237 else:
238 self.error("Unknown c++ to python class conversion for c++ " \
239 "type: '%s'. Please update the python_class_map " \
240 "in StateMachine.py", param.type_ast.type.c_ident)
241 code.dedent()
242 code.write(path, '%s.py' % py_ident)
243
244
245 def printControllerHH(self, path):
246 '''Output the method declarations for the class declaration'''
247 code = self.symtab.codeFormatter()
248 ident = self.ident
249 c_ident = "%s_Controller" % self.ident
250
251 code('''
252/** \\file $c_ident.hh
253 *
254 * Auto generated C++ code started by $__file__:$__line__
255 * Created by slicc definition of Module "${{self.short}}"
256 */
257
258#ifndef __${ident}_CONTROLLER_HH__
259#define __${ident}_CONTROLLER_HH__
260
261#include <iostream>
262#include <sstream>
263#include <string>
264
265#include "mem/protocol/TransitionResult.hh"
266#include "mem/protocol/Types.hh"
267#include "mem/ruby/common/Consumer.hh"
268#include "mem/ruby/slicc_interface/AbstractController.hh"
269#include "params/$c_ident.hh"
270''')
271
272 seen_types = set()
273 for var in self.objects:
274 if var.type.ident not in seen_types and not var.type.isPrimitive:
275 code('#include "mem/protocol/${{var.type.c_ident}}.hh"')
276 seen_types.add(var.type.ident)
277
278 # for adding information to the protocol debug trace
279 code('''
280extern std::stringstream ${ident}_transitionComment;
281
282class $c_ident : public AbstractController
283{
284 public:
285 typedef ${c_ident}Params Params;
286 $c_ident(const Params *p);
287 static int getNumControllers();
288 void init();
289
290 MessageBuffer* getMandatoryQueue() const;
291 void setNetQueue(const std::string& name, MessageBuffer *b);
292
293 void print(std::ostream& out) const;
294 void wakeup();
295 void resetStats();
296 void regStats();
297 void collateStats();
298
299 void recordCacheTrace(int cntrl, CacheRecorder* tr);
300 Sequencer* getSequencer() const;
301
302 int functionalWriteBuffers(PacketPtr&);
303
304 void countTransition(${ident}_State state, ${ident}_Event event);
305 void possibleTransition(${ident}_State state, ${ident}_Event event);
306 uint64 getEventCount(${ident}_Event event);
307 bool isPossible(${ident}_State state, ${ident}_Event event);
308 uint64 getTransitionCount(${ident}_State state, ${ident}_Event event);
309
310private:
311''')
312
313 code.indent()
314 # added by SS
315 for param in self.config_parameters:
316 if param.pointer:
317 code('${{param.type_ast.type}}* m_${{param.ident}}_ptr;')
318 else:
319 code('${{param.type_ast.type}} m_${{param.ident}};')
320
321 code('''
322TransitionResult doTransition(${ident}_Event event,
323''')
324
325 if self.EntryType != None:
326 code('''
327 ${{self.EntryType.c_ident}}* m_cache_entry_ptr,
328''')
329 if self.TBEType != None:
330 code('''
331 ${{self.TBEType.c_ident}}* m_tbe_ptr,
332''')
333
334 code('''
335 const Address addr);
336
337TransitionResult doTransitionWorker(${ident}_Event event,
338 ${ident}_State state,
339 ${ident}_State& next_state,
340''')
341
342 if self.TBEType != None:
343 code('''
344 ${{self.TBEType.c_ident}}*& m_tbe_ptr,
345''')
346 if self.EntryType != None:
347 code('''
348 ${{self.EntryType.c_ident}}*& m_cache_entry_ptr,
349''')
350
351 code('''
352 const Address& addr);
353
354int m_counters[${ident}_State_NUM][${ident}_Event_NUM];
355int m_event_counters[${ident}_Event_NUM];
356bool m_possible[${ident}_State_NUM][${ident}_Event_NUM];
357
358static std::vector<Stats::Vector *> eventVec;
359static std::vector<std::vector<Stats::Vector *> > transVec;
360static int m_num_controllers;
361
362// Internal functions
363''')
364
365 for func in self.functions:
366 proto = func.prototype
367 if proto:
368 code('$proto')
369
370 if self.EntryType != None:
371 code('''
372
373// Set and Reset for cache_entry variable
374void set_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, AbstractCacheEntry* m_new_cache_entry);
375void unset_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr);
376''')
377
378 if self.TBEType != None:
379 code('''
380
381// Set and Reset for tbe variable
382void set_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${ident}_TBE* m_new_tbe);
383void unset_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr);
384''')
385
386 # Prototype the actions that the controller can take
387 code('''
388
389// Actions
390''')
391 if self.TBEType != None and self.EntryType != None:
392 for action in self.actions.itervalues():
393 code('/** \\brief ${{action.desc}} */')
394 code('void ${{action.ident}}(${{self.TBEType.c_ident}}*& '
395 'm_tbe_ptr, ${{self.EntryType.c_ident}}*& '
396 'm_cache_entry_ptr, const Address& addr);')
397 elif self.TBEType != None:
398 for action in self.actions.itervalues():
399 code('/** \\brief ${{action.desc}} */')
400 code('void ${{action.ident}}(${{self.TBEType.c_ident}}*& '
401 'm_tbe_ptr, const Address& addr);')
402 elif self.EntryType != None:
403 for action in self.actions.itervalues():
404 code('/** \\brief ${{action.desc}} */')
405 code('void ${{action.ident}}(${{self.EntryType.c_ident}}*& '
406 'm_cache_entry_ptr, const Address& addr);')
407 else:
408 for action in self.actions.itervalues():
409 code('/** \\brief ${{action.desc}} */')
410 code('void ${{action.ident}}(const Address& addr);')
411
412 # the controller internal variables
413 code('''
414
415// Objects
416''')
417 for var in self.objects:
418 th = var.get("template", "")
419 code('${{var.type.c_ident}}$th* m_${{var.ident}}_ptr;')
420
421 code.dedent()
422 code('};')
423 code('#endif // __${ident}_CONTROLLER_H__')
424 code.write(path, '%s.hh' % c_ident)
425
426 def printControllerCC(self, path, includes):
427 '''Output the actions for performing the actions'''
428
429 code = self.symtab.codeFormatter()
430 ident = self.ident
431 c_ident = "%s_Controller" % self.ident
432
433 code('''
434/** \\file $c_ident.cc
435 *
436 * Auto generated C++ code started by $__file__:$__line__
437 * Created by slicc definition of Module "${{self.short}}"
438 */
439
440#include <sys/types.h>
441#include <typeinfo>
442#include <unistd.h>
443
444#include <cassert>
445#include <sstream>
446#include <string>
447
448#include "base/compiler.hh"
449#include "base/cprintf.hh"
450#include "debug/RubyGenerated.hh"
451#include "debug/RubySlicc.hh"
452#include "mem/protocol/${ident}_Controller.hh"
453#include "mem/protocol/${ident}_Event.hh"
454#include "mem/protocol/${ident}_State.hh"
455#include "mem/protocol/Types.hh"
456#include "mem/ruby/system/System.hh"
457''')
458 for include_path in includes:
459 code('#include "${{include_path}}"')
460
461 code('''
462
463using namespace std;
464''')
465
466 # include object classes
467 seen_types = set()
468 for var in self.objects:
469 if var.type.ident not in seen_types and not var.type.isPrimitive:
470 code('#include "mem/protocol/${{var.type.c_ident}}.hh"')
471 seen_types.add(var.type.ident)
472
473 num_in_ports = len(self.in_ports)
474
475 code('''
476$c_ident *
477${c_ident}Params::create()
478{
479 return new $c_ident(this);
480}
481
482int $c_ident::m_num_controllers = 0;
483std::vector<Stats::Vector *> $c_ident::eventVec;
484std::vector<std::vector<Stats::Vector *> > $c_ident::transVec;
485
486// for adding information to the protocol debug trace
487stringstream ${ident}_transitionComment;
488
489#ifndef NDEBUG
490#define APPEND_TRANSITION_COMMENT(str) (${ident}_transitionComment << str)
491#else
492#define APPEND_TRANSITION_COMMENT(str) do {} while (0)
493#endif
494
495/** \\brief constructor */
496$c_ident::$c_ident(const Params *p)
497 : AbstractController(p)
498{
499 m_machineID.type = MachineType_${ident};
500 m_machineID.num = m_version;
501 m_num_controllers++;
502
503 m_in_ports = $num_in_ports;
504''')
505 code.indent()
506
507 #
508 # After initializing the universal machine parameters, initialize the
509 # this machines config parameters. Also if these configuration params
510 # include a sequencer, connect the it to the controller.
511 #
512 for param in self.config_parameters:
513
514 # Do not initialize messgage buffers since they are initialized
515 # when the port based connections are made.
516 if param.type_ast.type.c_ident == "MessageBuffer":
517 continue
518
519 if param.pointer:
520 code('m_${{param.ident}}_ptr = p->${{param.ident}};')
521 else:
522 code('m_${{param.ident}} = p->${{param.ident}};')
523
524 if re.compile("sequencer").search(param.ident):
525 code('m_${{param.ident}}_ptr->setController(this);')
526
527 for var in self.objects:
528 if var.ident.find("mandatoryQueue") >= 0:
529 code('''
530m_${{var.ident}}_ptr = new ${{var.type.c_ident}}();
531m_${{var.ident}}_ptr->setReceiver(this);
532''')
533
534 code('''
535
536for (int state = 0; state < ${ident}_State_NUM; state++) {
537 for (int event = 0; event < ${ident}_Event_NUM; event++) {
538 m_possible[state][event] = false;
539 m_counters[state][event] = 0;
540 }
541}
542for (int event = 0; event < ${ident}_Event_NUM; event++) {
543 m_event_counters[event] = 0;
544}
545''')
546 code.dedent()
547 code('''
548}
549
550void
551$c_ident::setNetQueue(const std::string& name, MessageBuffer *b)
552{
553 MachineType machine_type = string_to_MachineType("${{self.ident}}");
554 int base M5_VAR_USED = MachineType_base_number(machine_type);
555
556''')
557 code.indent()
558
559 # set for maintaining the vnet, direction pairs already seen for this
560 # machine. This map helps in implementing the check for avoiding
561 # multiple message buffers being mapped to the same vnet.
562 vnet_dir_set = set()
563
564 for var in self.config_parameters:
565 if "network" in var:
566 vtype = var.type_ast.type
567 vid = "m_%s_ptr" % var.ident
568
569 code('''
570if ("${{var.ident}}" == name) {
571 $vid = b;
572 assert($vid != NULL);
573''')
574 code.indent()
575 # Network port object
576 network = var["network"]
577 ordered = var["ordered"]
578
579 if "virtual_network" in var:
580 vnet = var["virtual_network"]
581 vnet_type = var["vnet_type"]
582
583 assert (vnet, network) not in vnet_dir_set
584 vnet_dir_set.add((vnet,network))
585
586 code('''
587m_net_ptr->set${network}NetQueue(m_version + base, $ordered, $vnet,
588 "$vnet_type", b);
589''')
590 # Set the end
591 if network == "To":
592 code('$vid->setSender(this);')
593 else:
594 code('$vid->setReceiver(this);')
595
596 # Set ordering
597 code('$vid->setOrdering(${{var["ordered"]}});')
598
599 # Set randomization
600 if "random" in var:
601 # A buffer
602 code('$vid->setRandomization(${{var["random"]}});')
603
604 # Set Priority
605 if "rank" in var:
606 code('$vid->setPriority(${{var["rank"]}})')
607
608 # Set buffer size
609 code('$vid->resize(m_buffer_size);')
610
611 if "recycle_latency" in var:
612 code('$vid->setRecycleLatency( ' \
613 'Cycles(${{var["recycle_latency"]}}));')
614 else:
615 code('$vid->setRecycleLatency(m_recycle_latency);')
616
617 # set description (may be overriden later by port def)
618 code('''
619$vid->setDescription("[Version " + to_string(m_version) + ", ${ident}, name=${{var.ident}}]");
620''')
621 code.dedent()
622 code('}\n')
623
624 code.dedent()
625 code('''
626}
627
628void
629$c_ident::init()
630{
631 // initialize objects
632
633''')
634
635 code.indent()
636
637 for var in self.objects:
638 vtype = var.type
639 vid = "m_%s_ptr" % var.ident
640 if "network" not in var:
641 # Not a network port object
642 if "primitive" in vtype:
643 code('$vid = new ${{vtype.c_ident}};')
644 if "default" in var:
645 code('(*$vid) = ${{var["default"]}};')
646 else:
647 # Normal Object
648 if var.ident.find("mandatoryQueue") < 0:
649 th = var.get("template", "")
650 expr = "%s = new %s%s" % (vid, vtype.c_ident, th)
651 args = ""
652 if "non_obj" not in vtype and not vtype.isEnumeration:
653 args = var.get("constructor", "")
654 code('$expr($args);')
655
656 code('assert($vid != NULL);')
657
658 if "default" in var:
659 code('*$vid = ${{var["default"]}}; // Object default')
660 elif "default" in vtype:
661 comment = "Type %s default" % vtype.ident
662 code('*$vid = ${{vtype["default"]}}; // $comment')
663
664 # Set ordering
665 if "ordered" in var:
666 # A buffer
667 code('$vid->setOrdering(${{var["ordered"]}});')
668
669 # Set randomization
670 if "random" in var:
671 # A buffer
672 code('$vid->setRandomization(${{var["random"]}});')
673
674 # Set Priority
675 if vtype.isBuffer and "rank" in var:
676 code('$vid->setPriority(${{var["rank"]}});')
677
678 # Set sender and receiver for trigger queue
679 if var.ident.find("triggerQueue") >= 0:
680 code('$vid->setSender(this);')
681 code('$vid->setReceiver(this);')
682 elif vtype.c_ident == "TimerTable":
683 code('$vid->setClockObj(this);')
684 elif var.ident.find("optionalQueue") >= 0:
685 code('$vid->setSender(this);')
686 code('$vid->setReceiver(this);')
687
688 if vtype.isBuffer:
689 if "recycle_latency" in var:
690 code('$vid->setRecycleLatency( ' \
691 'Cycles(${{var["recycle_latency"]}}));')
692 else:
693 code('$vid->setRecycleLatency(m_recycle_latency);')
694
695 # Set the prefetchers
696 code()
697 for prefetcher in self.prefetchers:
698 code('${{prefetcher.code}}.setController(this);')
699
700 code()
701 for port in self.in_ports:
702 # Set the queue consumers
703 code('${{port.code}}.setConsumer(this);')
704 # Set the queue descriptions
705 code('${{port.code}}.setDescription("[Version " + to_string(m_version) + ", $ident, $port]");')
706
707 # Initialize the transition profiling
708 code()
709 for trans in self.transitions:
710 # Figure out if we stall
711 stall = False
712 for action in trans.actions:
713 if action.ident == "z_stall":
714 stall = True
715
716 # Only possible if it is not a 'z' case
717 if not stall:
718 state = "%s_State_%s" % (self.ident, trans.state.ident)
719 event = "%s_Event_%s" % (self.ident, trans.event.ident)
720 code('possibleTransition($state, $event);')
721
722 code.dedent()
723 code('''
724 AbstractController::init();
725 resetStats();
726}
727''')
728
729 mq_ident = "NULL"
730 for port in self.in_ports:
731 if port.code.find("mandatoryQueue_ptr") >= 0:
732 mq_ident = "m_mandatoryQueue_ptr"
733
734 seq_ident = "NULL"
735 for param in self.config_parameters:
736 if param.ident == "sequencer":
737 assert(param.pointer)
738 seq_ident = "m_%s_ptr" % param.ident
739
740 code('''
741
742void
743$c_ident::regStats()
744{
745 AbstractController::regStats();
746
747 if (m_version == 0) {
748 for (${ident}_Event event = ${ident}_Event_FIRST;
749 event < ${ident}_Event_NUM; ++event) {
750 Stats::Vector *t = new Stats::Vector();
751 t->init(m_num_controllers);
752 t->name(params()->ruby_system->name() + ".${c_ident}." +
753 ${ident}_Event_to_string(event));
754 t->flags(Stats::pdf | Stats::total | Stats::oneline |
755 Stats::nozero);
756
757 eventVec.push_back(t);
758 }
759
760 for (${ident}_State state = ${ident}_State_FIRST;
761 state < ${ident}_State_NUM; ++state) {
762
763 transVec.push_back(std::vector<Stats::Vector *>());
764
765 for (${ident}_Event event = ${ident}_Event_FIRST;
766 event < ${ident}_Event_NUM; ++event) {
767
768 Stats::Vector *t = new Stats::Vector();
769 t->init(m_num_controllers);
770 t->name(params()->ruby_system->name() + ".${c_ident}." +
771 ${ident}_State_to_string(state) +
772 "." + ${ident}_Event_to_string(event));
773
774 t->flags(Stats::pdf | Stats::total | Stats::oneline |
775 Stats::nozero);
776 transVec[state].push_back(t);
777 }
778 }
779 }
780}
781
782void
783$c_ident::collateStats()
784{
785 for (${ident}_Event event = ${ident}_Event_FIRST;
786 event < ${ident}_Event_NUM; ++event) {
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 (*eventVec[event])[i] =
793 (($c_ident *)(*it).second)->getEventCount(event);
794 }
795 }
796
797 for (${ident}_State state = ${ident}_State_FIRST;
798 state < ${ident}_State_NUM; ++state) {
799
800 for (${ident}_Event event = ${ident}_Event_FIRST;
801 event < ${ident}_Event_NUM; ++event) {
802
803 for (unsigned int i = 0; i < m_num_controllers; ++i) {
804 RubySystem *rs = params()->ruby_system;
805 std::map<uint32_t, AbstractController *>::iterator it =
806 rs->m_abstract_controls[MachineType_${ident}].find(i);
807 assert(it != rs->m_abstract_controls[MachineType_${ident}].end());
808 (*transVec[state][event])[i] =
809 (($c_ident *)(*it).second)->getTransitionCount(state, event);
810 }
811 }
812 }
813}
814
815void
816$c_ident::countTransition(${ident}_State state, ${ident}_Event event)
817{
818 assert(m_possible[state][event]);
819 m_counters[state][event]++;
820 m_event_counters[event]++;
821}
822void
823$c_ident::possibleTransition(${ident}_State state,
824 ${ident}_Event event)
825{
826 m_possible[state][event] = true;
827}
828
829uint64
830$c_ident::getEventCount(${ident}_Event event)
831{
832 return m_event_counters[event];
833}
834
835bool
836$c_ident::isPossible(${ident}_State state, ${ident}_Event event)
837{
838 return m_possible[state][event];
839}
840
841uint64
842$c_ident::getTransitionCount(${ident}_State state,
843 ${ident}_Event event)
844{
845 return m_counters[state][event];
846}
847
848int
849$c_ident::getNumControllers()
850{
851 return m_num_controllers;
852}
853
854MessageBuffer*
855$c_ident::getMandatoryQueue() const
856{
857 return $mq_ident;
858}
859
860Sequencer*
861$c_ident::getSequencer() const
862{
863 return $seq_ident;
864}
865
866void
867$c_ident::print(ostream& out) const
868{
869 out << "[$c_ident " << m_version << "]";
870}
871
872void $c_ident::resetStats()
873{
874 for (int state = 0; state < ${ident}_State_NUM; state++) {
875 for (int event = 0; event < ${ident}_Event_NUM; event++) {
876 m_counters[state][event] = 0;
877 }
878 }
879
880 for (int event = 0; event < ${ident}_Event_NUM; event++) {
881 m_event_counters[event] = 0;
882 }
883
884 AbstractController::resetStats();
885}
886''')
887
888 if self.EntryType != None:
889 code('''
890
891// Set and Reset for cache_entry variable
892void
893$c_ident::set_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, AbstractCacheEntry* m_new_cache_entry)
894{
895 m_cache_entry_ptr = (${{self.EntryType.c_ident}}*)m_new_cache_entry;
896}
897
898void
899$c_ident::unset_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr)
900{
901 m_cache_entry_ptr = 0;
902}
903''')
904
905 if self.TBEType != None:
906 code('''
907
908// Set and Reset for tbe variable
909void
910$c_ident::set_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.TBEType.c_ident}}* m_new_tbe)
911{
912 m_tbe_ptr = m_new_tbe;
913}
914
915void
916$c_ident::unset_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr)
917{
918 m_tbe_ptr = NULL;
919}
920''')
921
922 code('''
923
924void
925$c_ident::recordCacheTrace(int cntrl, CacheRecorder* tr)
926{
927''')
928 #
929 # Record cache contents for all associated caches.
930 #
931 code.indent()
932 for param in self.config_parameters:
933 if param.type_ast.type.ident == "CacheMemory":
934 assert(param.pointer)
935 code('m_${{param.ident}}_ptr->recordCacheContents(cntrl, tr);')
936
937 code.dedent()
938 code('''
939}
940
941// Actions
942''')
943 if self.TBEType != None and self.EntryType != None:
944 for action in self.actions.itervalues():
945 if "c_code" not in action:
946 continue
947
948 code('''
949/** \\brief ${{action.desc}} */
950void
951$c_ident::${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr)
952{
953 DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
954 try {
955 ${{action["c_code"]}}
956 } catch (const RejectException & e) {
957 fatal("Error in action ${{ident}}:${{action.ident}}: "
958 "executed a peek statement with the wrong message "
959 "type specified. ");
960 }
961}
962
963''')
964 elif self.TBEType != None:
965 for action in self.actions.itervalues():
966 if "c_code" not in action:
967 continue
968
969 code('''
970/** \\brief ${{action.desc}} */
971void
972$c_ident::${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, const Address& addr)
973{
974 DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
975 ${{action["c_code"]}}
976}
977
978''')
979 elif self.EntryType != None:
980 for action in self.actions.itervalues():
981 if "c_code" not in action:
982 continue
983
984 code('''
985/** \\brief ${{action.desc}} */
986void
987$c_ident::${{action.ident}}(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr)
988{
989 DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
990 ${{action["c_code"]}}
991}
992
993''')
994 else:
995 for action in self.actions.itervalues():
996 if "c_code" not in action:
997 continue
998
999 code('''
1000/** \\brief ${{action.desc}} */
1001void
1002$c_ident::${{action.ident}}(const Address& addr)
1003{
1004 DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
1005 ${{action["c_code"]}}
1006}
1007
1008''')
1009 for func in self.functions:
1010 code(func.generateCode())
1011
1012 # Function for functional writes to messages buffered in the controller
1013 code('''
1014int
1015$c_ident::functionalWriteBuffers(PacketPtr& pkt)
1016{
1017 int num_functional_writes = 0;
1018''')
1019 for var in self.objects:
1020 vtype = var.type
1021 if vtype.isBuffer:
1022 vid = "m_%s_ptr" % var.ident
1023 code('num_functional_writes += $vid->functionalWrite(pkt);')
1024
1025 for var in self.config_parameters:
1026 vtype = var.type_ast.type
1027 if vtype.isBuffer:
1028 vid = "m_%s_ptr" % var.ident
1029 code('num_functional_writes += $vid->functionalWrite(pkt);')
1030
1031 code('''
1032 return num_functional_writes;
1033}
1034''')
1035
1036 code.write(path, "%s.cc" % c_ident)
1037
1038 def printCWakeup(self, path, includes):
1039 '''Output the wakeup loop for the events'''
1040
1041 code = self.symtab.codeFormatter()
1042 ident = self.ident
1043
1044 outputRequest_types = True
1045 if len(self.request_types) == 0:
1046 outputRequest_types = False
1047
1048 code('''
1049// Auto generated C++ code started by $__file__:$__line__
1050// ${ident}: ${{self.short}}
1051
1052#include <sys/types.h>
1053#include <typeinfo>
1054#include <unistd.h>
1055
1056#include <cassert>
1057
1058#include "base/misc.hh"
1059#include "debug/RubySlicc.hh"
1060#include "mem/protocol/${ident}_Controller.hh"
1061#include "mem/protocol/${ident}_Event.hh"
1062#include "mem/protocol/${ident}_State.hh"
1063''')
1064
1065 if outputRequest_types:
1066 code('''#include "mem/protocol/${ident}_RequestType.hh"''')
1067
1068 code('''
1069#include "mem/protocol/Types.hh"
1070#include "mem/ruby/system/System.hh"
1071''')
1072
1073
1074 for include_path in includes:
1075 code('#include "${{include_path}}"')
1076
1077 port_to_buf_map, in_msg_bufs, msg_bufs = self.getBufferMaps(ident)
1078
1079 code('''
1080
1081using namespace std;
1082
1083void
1084${ident}_Controller::wakeup()
1085{
1086 int counter = 0;
1087 while (true) {
1088 unsigned char rejected[${{len(msg_bufs)}}];
1089 memset(rejected, 0, sizeof(unsigned char)*${{len(msg_bufs)}});
1090 // Some cases will put us into an infinite loop without this limit
1091 assert(counter <= m_transitions_per_cycle);
1092 if (counter == m_transitions_per_cycle) {
1093 // Count how often we are fully utilized
1094 m_fully_busy_cycles++;
1095
1096 // Wakeup in another cycle and try again
1097 scheduleEvent(Cycles(1));
1098 break;
1099 }
1100''')
1101
1102 code.indent()
1103 code.indent()
1104
1105 # InPorts
1106 #
1107 for port in self.in_ports:
1108 code.indent()
1109 code('// ${ident}InPort $port')
1110 if port.pairs.has_key("rank"):
1111 code('m_cur_in_port = ${{port.pairs["rank"]}};')
1112 else:
1113 code('m_cur_in_port = 0;')
1114 if port in port_to_buf_map:
1115 code('try {')
1116 code.indent()
1117 code('${{port["c_code_in_port"]}}')
1118
1119 if port in port_to_buf_map:
1120 code.dedent()
1121 code('''
1122 } catch (const RejectException & e) {
1123 rejected[${{port_to_buf_map[port]}}]++;
1124 }
1125''')
1126 code.dedent()
1127 code('')
1128
1129 code.dedent()
1130 code.dedent()
1131 code('''
1132 // If we got this far, we have nothing left todo or something went
1133 // wrong''')
1134 for buf_name, ports in in_msg_bufs.items():
1135 if len(ports) > 1:
1136 # only produce checks when a buffer is shared by multiple ports
1137 code('''
1138 if (${{buf_name}}->isReady() && rejected[${{port_to_buf_map[ports[0]]}}] == ${{len(ports)}})
1139 {
1140 // no port claimed the message on the top of this buffer
1141 panic("Runtime Error at Ruby Time: %d. "
1142 "All ports rejected a message. "
1143 "You are probably sending a message type to this controller "
1144 "over a virtual network that do not define an in_port for "
1145 "the incoming message type.\\n",
1146 Cycles(1));
1147 }
1148''')
1149 code('''
1150 break;
1151 }
1152}
1153''')
1154
1155 code.write(path, "%s_Wakeup.cc" % self.ident)
1156
1157 def printCSwitch(self, path):
1158 '''Output switch statement for transition table'''
1159
1160 code = self.symtab.codeFormatter()
1161 ident = self.ident
1162
1163 code('''
1164// Auto generated C++ code started by $__file__:$__line__
1165// ${ident}: ${{self.short}}
1166
1167#include <cassert>
1168
1169#include "base/misc.hh"
1170#include "base/trace.hh"
1171#include "debug/ProtocolTrace.hh"
1172#include "debug/RubyGenerated.hh"
1173#include "mem/protocol/${ident}_Controller.hh"
1174#include "mem/protocol/${ident}_Event.hh"
1175#include "mem/protocol/${ident}_State.hh"
1176#include "mem/protocol/Types.hh"
1177#include "mem/ruby/system/System.hh"
1178
1179#define HASH_FUN(state, event) ((int(state)*${ident}_Event_NUM)+int(event))
1180
1181#define GET_TRANSITION_COMMENT() (${ident}_transitionComment.str())
1182#define CLEAR_TRANSITION_COMMENT() (${ident}_transitionComment.str(""))
1183
1184TransitionResult
1185${ident}_Controller::doTransition(${ident}_Event event,
1186''')
1187 if self.EntryType != None:
1188 code('''
1189 ${{self.EntryType.c_ident}}* m_cache_entry_ptr,
1190''')
1191 if self.TBEType != None:
1192 code('''
1193 ${{self.TBEType.c_ident}}* m_tbe_ptr,
1194''')
1195 code('''
1196 const Address addr)
1197{
1198''')
1199 code.indent()
1200
1201 if self.TBEType != None and self.EntryType != None:
1202 code('${ident}_State state = getState(m_tbe_ptr, m_cache_entry_ptr, addr);')
1203 elif self.TBEType != None:
1204 code('${ident}_State state = getState(m_tbe_ptr, addr);')
1205 elif self.EntryType != None:
1206 code('${ident}_State state = getState(m_cache_entry_ptr, addr);')
1207 else:
1208 code('${ident}_State state = getState(addr);')
1209
1210 code('''
1211${ident}_State next_state = state;
1212
1213DPRINTF(RubyGenerated, "%s, Time: %lld, state: %s, event: %s, addr: %s\\n",
1214 *this, curCycle(), ${ident}_State_to_string(state),
1215 ${ident}_Event_to_string(event), addr);
1216
1217TransitionResult result =
1218''')
1219 if self.TBEType != None and self.EntryType != None:
1220 code('doTransitionWorker(event, state, next_state, m_tbe_ptr, m_cache_entry_ptr, addr);')
1221 elif self.TBEType != None:
1222 code('doTransitionWorker(event, state, next_state, m_tbe_ptr, addr);')
1223 elif self.EntryType != None:
1224 code('doTransitionWorker(event, state, next_state, m_cache_entry_ptr, addr);')
1225 else:
1226 code('doTransitionWorker(event, state, next_state, addr);')
1227
1228 port_to_buf_map, in_msg_bufs, msg_bufs = self.getBufferMaps(ident)
1229
1230 code('''
1231
1232if (result == TransitionResult_Valid) {
1233 DPRINTF(RubyGenerated, "next_state: %s\\n",
1234 ${ident}_State_to_string(next_state));
1235 countTransition(state, event);
1236
1237 DPRINTFR(ProtocolTrace, "%15d %3s %10s%20s %6s>%-6s %s %s\\n",
1238 curTick(), m_version, "${ident}",
1239 ${ident}_Event_to_string(event),
1240 ${ident}_State_to_string(state),
1241 ${ident}_State_to_string(next_state),
1242 addr, GET_TRANSITION_COMMENT());
1243
1244 CLEAR_TRANSITION_COMMENT();
1245''')
1246 if self.TBEType != None and self.EntryType != None:
1247 code('setState(m_tbe_ptr, m_cache_entry_ptr, addr, next_state);')
1248 code('setAccessPermission(m_cache_entry_ptr, addr, next_state);')
1249 elif self.TBEType != None:
1250 code('setState(m_tbe_ptr, addr, next_state);')
1251 code('setAccessPermission(addr, next_state);')
1252 elif self.EntryType != None:
1253 code('setState(m_cache_entry_ptr, addr, next_state);')
1254 code('setAccessPermission(m_cache_entry_ptr, addr, next_state);')
1255 else:
1256 code('setState(addr, next_state);')
1257 code('setAccessPermission(addr, next_state);')
1258
1259 code('''
1260} else if (result == TransitionResult_ResourceStall) {
1261 DPRINTFR(ProtocolTrace, "%15s %3s %10s%20s %6s>%-6s %s %s\\n",
1262 curTick(), m_version, "${ident}",
1263 ${ident}_Event_to_string(event),
1264 ${ident}_State_to_string(state),
1265 ${ident}_State_to_string(next_state),
1266 addr, "Resource Stall");
1267} else if (result == TransitionResult_ProtocolStall) {
1268 DPRINTF(RubyGenerated, "stalling\\n");
1269 DPRINTFR(ProtocolTrace, "%15s %3s %10s%20s %6s>%-6s %s %s\\n",
1270 curTick(), m_version, "${ident}",
1271 ${ident}_Event_to_string(event),
1272 ${ident}_State_to_string(state),
1273 ${ident}_State_to_string(next_state),
1274 addr, "Protocol Stall");
1275}
1276
1277return result;
1278''')
1279 code.dedent()
1280 code('''
1281}
1282
1283TransitionResult
1284${ident}_Controller::doTransitionWorker(${ident}_Event event,
1285 ${ident}_State state,
1286 ${ident}_State& next_state,
1287''')
1288
1289 if self.TBEType != None:
1290 code('''
1291 ${{self.TBEType.c_ident}}*& m_tbe_ptr,
1292''')
1293 if self.EntryType != None:
1294 code('''
1295 ${{self.EntryType.c_ident}}*& m_cache_entry_ptr,
1296''')
1297 code('''
1298 const Address& addr)
1299{
1300 switch(HASH_FUN(state, event)) {
1301''')
1302
1303 # This map will allow suppress generating duplicate code
1304 cases = orderdict()
1305
1306 for trans in self.transitions:
1307 case_string = "%s_State_%s, %s_Event_%s" % \
1308 (self.ident, trans.state.ident, self.ident, trans.event.ident)
1309
1310 case = self.symtab.codeFormatter()
1311 # Only set next_state if it changes
1312 if trans.state != trans.nextState:
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 # determine the port->msg buffer mappings
183 def getBufferMaps(self, ident):
184 msg_bufs = []
185 port_to_buf_map = {}
186 in_msg_bufs = {}
187 for port in self.in_ports:
188 buf_name = "m_%s_ptr" % port.buffer_expr.name
189 msg_bufs.append(buf_name)
190 port_to_buf_map[port] = msg_bufs.index(buf_name)
191 if buf_name not in in_msg_bufs:
192 in_msg_bufs[buf_name] = [port]
193 else:
194 in_msg_bufs[buf_name].append(port)
195 return port_to_buf_map, in_msg_bufs, msg_bufs
196
197 def writeCodeFiles(self, path, includes):
198 self.printControllerPython(path)
199 self.printControllerHH(path)
200 self.printControllerCC(path, includes)
201 self.printCSwitch(path)
202 self.printCWakeup(path, includes)
203
204 def printControllerPython(self, path):
205 code = self.symtab.codeFormatter()
206 ident = self.ident
207
208 py_ident = "%s_Controller" % ident
209 c_ident = "%s_Controller" % self.ident
210
211 code('''
212from m5.params import *
213from m5.SimObject import SimObject
214from Controller import RubyController
215
216class $py_ident(RubyController):
217 type = '$py_ident'
218 cxx_header = 'mem/protocol/${c_ident}.hh'
219''')
220 code.indent()
221 for param in self.config_parameters:
222 dflt_str = ''
223
224 if param.rvalue is not None:
225 dflt_str = str(param.rvalue.inline()) + ', '
226
227 if param.type_ast.type.c_ident == "MessageBuffer":
228 if param["network"] == "To":
229 code('${{param.ident}} = MasterPort(${dflt_str}"")')
230 else:
231 code('${{param.ident}} = SlavePort(${dflt_str}"")')
232
233 elif python_class_map.has_key(param.type_ast.type.c_ident):
234 python_type = python_class_map[param.type_ast.type.c_ident]
235 code('${{param.ident}} = Param.${{python_type}}(${dflt_str}"")')
236
237 else:
238 self.error("Unknown c++ to python class conversion for c++ " \
239 "type: '%s'. Please update the python_class_map " \
240 "in StateMachine.py", param.type_ast.type.c_ident)
241 code.dedent()
242 code.write(path, '%s.py' % py_ident)
243
244
245 def printControllerHH(self, path):
246 '''Output the method declarations for the class declaration'''
247 code = self.symtab.codeFormatter()
248 ident = self.ident
249 c_ident = "%s_Controller" % self.ident
250
251 code('''
252/** \\file $c_ident.hh
253 *
254 * Auto generated C++ code started by $__file__:$__line__
255 * Created by slicc definition of Module "${{self.short}}"
256 */
257
258#ifndef __${ident}_CONTROLLER_HH__
259#define __${ident}_CONTROLLER_HH__
260
261#include <iostream>
262#include <sstream>
263#include <string>
264
265#include "mem/protocol/TransitionResult.hh"
266#include "mem/protocol/Types.hh"
267#include "mem/ruby/common/Consumer.hh"
268#include "mem/ruby/slicc_interface/AbstractController.hh"
269#include "params/$c_ident.hh"
270''')
271
272 seen_types = set()
273 for var in self.objects:
274 if var.type.ident not in seen_types and not var.type.isPrimitive:
275 code('#include "mem/protocol/${{var.type.c_ident}}.hh"')
276 seen_types.add(var.type.ident)
277
278 # for adding information to the protocol debug trace
279 code('''
280extern std::stringstream ${ident}_transitionComment;
281
282class $c_ident : public AbstractController
283{
284 public:
285 typedef ${c_ident}Params Params;
286 $c_ident(const Params *p);
287 static int getNumControllers();
288 void init();
289
290 MessageBuffer* getMandatoryQueue() const;
291 void setNetQueue(const std::string& name, MessageBuffer *b);
292
293 void print(std::ostream& out) const;
294 void wakeup();
295 void resetStats();
296 void regStats();
297 void collateStats();
298
299 void recordCacheTrace(int cntrl, CacheRecorder* tr);
300 Sequencer* getSequencer() const;
301
302 int functionalWriteBuffers(PacketPtr&);
303
304 void countTransition(${ident}_State state, ${ident}_Event event);
305 void possibleTransition(${ident}_State state, ${ident}_Event event);
306 uint64 getEventCount(${ident}_Event event);
307 bool isPossible(${ident}_State state, ${ident}_Event event);
308 uint64 getTransitionCount(${ident}_State state, ${ident}_Event event);
309
310private:
311''')
312
313 code.indent()
314 # added by SS
315 for param in self.config_parameters:
316 if param.pointer:
317 code('${{param.type_ast.type}}* m_${{param.ident}}_ptr;')
318 else:
319 code('${{param.type_ast.type}} m_${{param.ident}};')
320
321 code('''
322TransitionResult doTransition(${ident}_Event event,
323''')
324
325 if self.EntryType != None:
326 code('''
327 ${{self.EntryType.c_ident}}* m_cache_entry_ptr,
328''')
329 if self.TBEType != None:
330 code('''
331 ${{self.TBEType.c_ident}}* m_tbe_ptr,
332''')
333
334 code('''
335 const Address addr);
336
337TransitionResult doTransitionWorker(${ident}_Event event,
338 ${ident}_State state,
339 ${ident}_State& next_state,
340''')
341
342 if self.TBEType != None:
343 code('''
344 ${{self.TBEType.c_ident}}*& m_tbe_ptr,
345''')
346 if self.EntryType != None:
347 code('''
348 ${{self.EntryType.c_ident}}*& m_cache_entry_ptr,
349''')
350
351 code('''
352 const Address& addr);
353
354int m_counters[${ident}_State_NUM][${ident}_Event_NUM];
355int m_event_counters[${ident}_Event_NUM];
356bool m_possible[${ident}_State_NUM][${ident}_Event_NUM];
357
358static std::vector<Stats::Vector *> eventVec;
359static std::vector<std::vector<Stats::Vector *> > transVec;
360static int m_num_controllers;
361
362// Internal functions
363''')
364
365 for func in self.functions:
366 proto = func.prototype
367 if proto:
368 code('$proto')
369
370 if self.EntryType != None:
371 code('''
372
373// Set and Reset for cache_entry variable
374void set_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, AbstractCacheEntry* m_new_cache_entry);
375void unset_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr);
376''')
377
378 if self.TBEType != None:
379 code('''
380
381// Set and Reset for tbe variable
382void set_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${ident}_TBE* m_new_tbe);
383void unset_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr);
384''')
385
386 # Prototype the actions that the controller can take
387 code('''
388
389// Actions
390''')
391 if self.TBEType != None and self.EntryType != None:
392 for action in self.actions.itervalues():
393 code('/** \\brief ${{action.desc}} */')
394 code('void ${{action.ident}}(${{self.TBEType.c_ident}}*& '
395 'm_tbe_ptr, ${{self.EntryType.c_ident}}*& '
396 'm_cache_entry_ptr, const Address& addr);')
397 elif self.TBEType != None:
398 for action in self.actions.itervalues():
399 code('/** \\brief ${{action.desc}} */')
400 code('void ${{action.ident}}(${{self.TBEType.c_ident}}*& '
401 'm_tbe_ptr, const Address& addr);')
402 elif self.EntryType != None:
403 for action in self.actions.itervalues():
404 code('/** \\brief ${{action.desc}} */')
405 code('void ${{action.ident}}(${{self.EntryType.c_ident}}*& '
406 'm_cache_entry_ptr, const Address& addr);')
407 else:
408 for action in self.actions.itervalues():
409 code('/** \\brief ${{action.desc}} */')
410 code('void ${{action.ident}}(const Address& addr);')
411
412 # the controller internal variables
413 code('''
414
415// Objects
416''')
417 for var in self.objects:
418 th = var.get("template", "")
419 code('${{var.type.c_ident}}$th* m_${{var.ident}}_ptr;')
420
421 code.dedent()
422 code('};')
423 code('#endif // __${ident}_CONTROLLER_H__')
424 code.write(path, '%s.hh' % c_ident)
425
426 def printControllerCC(self, path, includes):
427 '''Output the actions for performing the actions'''
428
429 code = self.symtab.codeFormatter()
430 ident = self.ident
431 c_ident = "%s_Controller" % self.ident
432
433 code('''
434/** \\file $c_ident.cc
435 *
436 * Auto generated C++ code started by $__file__:$__line__
437 * Created by slicc definition of Module "${{self.short}}"
438 */
439
440#include <sys/types.h>
441#include <typeinfo>
442#include <unistd.h>
443
444#include <cassert>
445#include <sstream>
446#include <string>
447
448#include "base/compiler.hh"
449#include "base/cprintf.hh"
450#include "debug/RubyGenerated.hh"
451#include "debug/RubySlicc.hh"
452#include "mem/protocol/${ident}_Controller.hh"
453#include "mem/protocol/${ident}_Event.hh"
454#include "mem/protocol/${ident}_State.hh"
455#include "mem/protocol/Types.hh"
456#include "mem/ruby/system/System.hh"
457''')
458 for include_path in includes:
459 code('#include "${{include_path}}"')
460
461 code('''
462
463using namespace std;
464''')
465
466 # include object classes
467 seen_types = set()
468 for var in self.objects:
469 if var.type.ident not in seen_types and not var.type.isPrimitive:
470 code('#include "mem/protocol/${{var.type.c_ident}}.hh"')
471 seen_types.add(var.type.ident)
472
473 num_in_ports = len(self.in_ports)
474
475 code('''
476$c_ident *
477${c_ident}Params::create()
478{
479 return new $c_ident(this);
480}
481
482int $c_ident::m_num_controllers = 0;
483std::vector<Stats::Vector *> $c_ident::eventVec;
484std::vector<std::vector<Stats::Vector *> > $c_ident::transVec;
485
486// for adding information to the protocol debug trace
487stringstream ${ident}_transitionComment;
488
489#ifndef NDEBUG
490#define APPEND_TRANSITION_COMMENT(str) (${ident}_transitionComment << str)
491#else
492#define APPEND_TRANSITION_COMMENT(str) do {} while (0)
493#endif
494
495/** \\brief constructor */
496$c_ident::$c_ident(const Params *p)
497 : AbstractController(p)
498{
499 m_machineID.type = MachineType_${ident};
500 m_machineID.num = m_version;
501 m_num_controllers++;
502
503 m_in_ports = $num_in_ports;
504''')
505 code.indent()
506
507 #
508 # After initializing the universal machine parameters, initialize the
509 # this machines config parameters. Also if these configuration params
510 # include a sequencer, connect the it to the controller.
511 #
512 for param in self.config_parameters:
513
514 # Do not initialize messgage buffers since they are initialized
515 # when the port based connections are made.
516 if param.type_ast.type.c_ident == "MessageBuffer":
517 continue
518
519 if param.pointer:
520 code('m_${{param.ident}}_ptr = p->${{param.ident}};')
521 else:
522 code('m_${{param.ident}} = p->${{param.ident}};')
523
524 if re.compile("sequencer").search(param.ident):
525 code('m_${{param.ident}}_ptr->setController(this);')
526
527 for var in self.objects:
528 if var.ident.find("mandatoryQueue") >= 0:
529 code('''
530m_${{var.ident}}_ptr = new ${{var.type.c_ident}}();
531m_${{var.ident}}_ptr->setReceiver(this);
532''')
533
534 code('''
535
536for (int state = 0; state < ${ident}_State_NUM; state++) {
537 for (int event = 0; event < ${ident}_Event_NUM; event++) {
538 m_possible[state][event] = false;
539 m_counters[state][event] = 0;
540 }
541}
542for (int event = 0; event < ${ident}_Event_NUM; event++) {
543 m_event_counters[event] = 0;
544}
545''')
546 code.dedent()
547 code('''
548}
549
550void
551$c_ident::setNetQueue(const std::string& name, MessageBuffer *b)
552{
553 MachineType machine_type = string_to_MachineType("${{self.ident}}");
554 int base M5_VAR_USED = MachineType_base_number(machine_type);
555
556''')
557 code.indent()
558
559 # set for maintaining the vnet, direction pairs already seen for this
560 # machine. This map helps in implementing the check for avoiding
561 # multiple message buffers being mapped to the same vnet.
562 vnet_dir_set = set()
563
564 for var in self.config_parameters:
565 if "network" in var:
566 vtype = var.type_ast.type
567 vid = "m_%s_ptr" % var.ident
568
569 code('''
570if ("${{var.ident}}" == name) {
571 $vid = b;
572 assert($vid != NULL);
573''')
574 code.indent()
575 # Network port object
576 network = var["network"]
577 ordered = var["ordered"]
578
579 if "virtual_network" in var:
580 vnet = var["virtual_network"]
581 vnet_type = var["vnet_type"]
582
583 assert (vnet, network) not in vnet_dir_set
584 vnet_dir_set.add((vnet,network))
585
586 code('''
587m_net_ptr->set${network}NetQueue(m_version + base, $ordered, $vnet,
588 "$vnet_type", b);
589''')
590 # Set the end
591 if network == "To":
592 code('$vid->setSender(this);')
593 else:
594 code('$vid->setReceiver(this);')
595
596 # Set ordering
597 code('$vid->setOrdering(${{var["ordered"]}});')
598
599 # Set randomization
600 if "random" in var:
601 # A buffer
602 code('$vid->setRandomization(${{var["random"]}});')
603
604 # Set Priority
605 if "rank" in var:
606 code('$vid->setPriority(${{var["rank"]}})')
607
608 # Set buffer size
609 code('$vid->resize(m_buffer_size);')
610
611 if "recycle_latency" in var:
612 code('$vid->setRecycleLatency( ' \
613 'Cycles(${{var["recycle_latency"]}}));')
614 else:
615 code('$vid->setRecycleLatency(m_recycle_latency);')
616
617 # set description (may be overriden later by port def)
618 code('''
619$vid->setDescription("[Version " + to_string(m_version) + ", ${ident}, name=${{var.ident}}]");
620''')
621 code.dedent()
622 code('}\n')
623
624 code.dedent()
625 code('''
626}
627
628void
629$c_ident::init()
630{
631 // initialize objects
632
633''')
634
635 code.indent()
636
637 for var in self.objects:
638 vtype = var.type
639 vid = "m_%s_ptr" % var.ident
640 if "network" not in var:
641 # Not a network port object
642 if "primitive" in vtype:
643 code('$vid = new ${{vtype.c_ident}};')
644 if "default" in var:
645 code('(*$vid) = ${{var["default"]}};')
646 else:
647 # Normal Object
648 if var.ident.find("mandatoryQueue") < 0:
649 th = var.get("template", "")
650 expr = "%s = new %s%s" % (vid, vtype.c_ident, th)
651 args = ""
652 if "non_obj" not in vtype and not vtype.isEnumeration:
653 args = var.get("constructor", "")
654 code('$expr($args);')
655
656 code('assert($vid != NULL);')
657
658 if "default" in var:
659 code('*$vid = ${{var["default"]}}; // Object default')
660 elif "default" in vtype:
661 comment = "Type %s default" % vtype.ident
662 code('*$vid = ${{vtype["default"]}}; // $comment')
663
664 # Set ordering
665 if "ordered" in var:
666 # A buffer
667 code('$vid->setOrdering(${{var["ordered"]}});')
668
669 # Set randomization
670 if "random" in var:
671 # A buffer
672 code('$vid->setRandomization(${{var["random"]}});')
673
674 # Set Priority
675 if vtype.isBuffer and "rank" in var:
676 code('$vid->setPriority(${{var["rank"]}});')
677
678 # Set sender and receiver for trigger queue
679 if var.ident.find("triggerQueue") >= 0:
680 code('$vid->setSender(this);')
681 code('$vid->setReceiver(this);')
682 elif vtype.c_ident == "TimerTable":
683 code('$vid->setClockObj(this);')
684 elif var.ident.find("optionalQueue") >= 0:
685 code('$vid->setSender(this);')
686 code('$vid->setReceiver(this);')
687
688 if vtype.isBuffer:
689 if "recycle_latency" in var:
690 code('$vid->setRecycleLatency( ' \
691 'Cycles(${{var["recycle_latency"]}}));')
692 else:
693 code('$vid->setRecycleLatency(m_recycle_latency);')
694
695 # Set the prefetchers
696 code()
697 for prefetcher in self.prefetchers:
698 code('${{prefetcher.code}}.setController(this);')
699
700 code()
701 for port in self.in_ports:
702 # Set the queue consumers
703 code('${{port.code}}.setConsumer(this);')
704 # Set the queue descriptions
705 code('${{port.code}}.setDescription("[Version " + to_string(m_version) + ", $ident, $port]");')
706
707 # Initialize the transition profiling
708 code()
709 for trans in self.transitions:
710 # Figure out if we stall
711 stall = False
712 for action in trans.actions:
713 if action.ident == "z_stall":
714 stall = True
715
716 # Only possible if it is not a 'z' case
717 if not stall:
718 state = "%s_State_%s" % (self.ident, trans.state.ident)
719 event = "%s_Event_%s" % (self.ident, trans.event.ident)
720 code('possibleTransition($state, $event);')
721
722 code.dedent()
723 code('''
724 AbstractController::init();
725 resetStats();
726}
727''')
728
729 mq_ident = "NULL"
730 for port in self.in_ports:
731 if port.code.find("mandatoryQueue_ptr") >= 0:
732 mq_ident = "m_mandatoryQueue_ptr"
733
734 seq_ident = "NULL"
735 for param in self.config_parameters:
736 if param.ident == "sequencer":
737 assert(param.pointer)
738 seq_ident = "m_%s_ptr" % param.ident
739
740 code('''
741
742void
743$c_ident::regStats()
744{
745 AbstractController::regStats();
746
747 if (m_version == 0) {
748 for (${ident}_Event event = ${ident}_Event_FIRST;
749 event < ${ident}_Event_NUM; ++event) {
750 Stats::Vector *t = new Stats::Vector();
751 t->init(m_num_controllers);
752 t->name(params()->ruby_system->name() + ".${c_ident}." +
753 ${ident}_Event_to_string(event));
754 t->flags(Stats::pdf | Stats::total | Stats::oneline |
755 Stats::nozero);
756
757 eventVec.push_back(t);
758 }
759
760 for (${ident}_State state = ${ident}_State_FIRST;
761 state < ${ident}_State_NUM; ++state) {
762
763 transVec.push_back(std::vector<Stats::Vector *>());
764
765 for (${ident}_Event event = ${ident}_Event_FIRST;
766 event < ${ident}_Event_NUM; ++event) {
767
768 Stats::Vector *t = new Stats::Vector();
769 t->init(m_num_controllers);
770 t->name(params()->ruby_system->name() + ".${c_ident}." +
771 ${ident}_State_to_string(state) +
772 "." + ${ident}_Event_to_string(event));
773
774 t->flags(Stats::pdf | Stats::total | Stats::oneline |
775 Stats::nozero);
776 transVec[state].push_back(t);
777 }
778 }
779 }
780}
781
782void
783$c_ident::collateStats()
784{
785 for (${ident}_Event event = ${ident}_Event_FIRST;
786 event < ${ident}_Event_NUM; ++event) {
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 (*eventVec[event])[i] =
793 (($c_ident *)(*it).second)->getEventCount(event);
794 }
795 }
796
797 for (${ident}_State state = ${ident}_State_FIRST;
798 state < ${ident}_State_NUM; ++state) {
799
800 for (${ident}_Event event = ${ident}_Event_FIRST;
801 event < ${ident}_Event_NUM; ++event) {
802
803 for (unsigned int i = 0; i < m_num_controllers; ++i) {
804 RubySystem *rs = params()->ruby_system;
805 std::map<uint32_t, AbstractController *>::iterator it =
806 rs->m_abstract_controls[MachineType_${ident}].find(i);
807 assert(it != rs->m_abstract_controls[MachineType_${ident}].end());
808 (*transVec[state][event])[i] =
809 (($c_ident *)(*it).second)->getTransitionCount(state, event);
810 }
811 }
812 }
813}
814
815void
816$c_ident::countTransition(${ident}_State state, ${ident}_Event event)
817{
818 assert(m_possible[state][event]);
819 m_counters[state][event]++;
820 m_event_counters[event]++;
821}
822void
823$c_ident::possibleTransition(${ident}_State state,
824 ${ident}_Event event)
825{
826 m_possible[state][event] = true;
827}
828
829uint64
830$c_ident::getEventCount(${ident}_Event event)
831{
832 return m_event_counters[event];
833}
834
835bool
836$c_ident::isPossible(${ident}_State state, ${ident}_Event event)
837{
838 return m_possible[state][event];
839}
840
841uint64
842$c_ident::getTransitionCount(${ident}_State state,
843 ${ident}_Event event)
844{
845 return m_counters[state][event];
846}
847
848int
849$c_ident::getNumControllers()
850{
851 return m_num_controllers;
852}
853
854MessageBuffer*
855$c_ident::getMandatoryQueue() const
856{
857 return $mq_ident;
858}
859
860Sequencer*
861$c_ident::getSequencer() const
862{
863 return $seq_ident;
864}
865
866void
867$c_ident::print(ostream& out) const
868{
869 out << "[$c_ident " << m_version << "]";
870}
871
872void $c_ident::resetStats()
873{
874 for (int state = 0; state < ${ident}_State_NUM; state++) {
875 for (int event = 0; event < ${ident}_Event_NUM; event++) {
876 m_counters[state][event] = 0;
877 }
878 }
879
880 for (int event = 0; event < ${ident}_Event_NUM; event++) {
881 m_event_counters[event] = 0;
882 }
883
884 AbstractController::resetStats();
885}
886''')
887
888 if self.EntryType != None:
889 code('''
890
891// Set and Reset for cache_entry variable
892void
893$c_ident::set_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, AbstractCacheEntry* m_new_cache_entry)
894{
895 m_cache_entry_ptr = (${{self.EntryType.c_ident}}*)m_new_cache_entry;
896}
897
898void
899$c_ident::unset_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr)
900{
901 m_cache_entry_ptr = 0;
902}
903''')
904
905 if self.TBEType != None:
906 code('''
907
908// Set and Reset for tbe variable
909void
910$c_ident::set_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.TBEType.c_ident}}* m_new_tbe)
911{
912 m_tbe_ptr = m_new_tbe;
913}
914
915void
916$c_ident::unset_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr)
917{
918 m_tbe_ptr = NULL;
919}
920''')
921
922 code('''
923
924void
925$c_ident::recordCacheTrace(int cntrl, CacheRecorder* tr)
926{
927''')
928 #
929 # Record cache contents for all associated caches.
930 #
931 code.indent()
932 for param in self.config_parameters:
933 if param.type_ast.type.ident == "CacheMemory":
934 assert(param.pointer)
935 code('m_${{param.ident}}_ptr->recordCacheContents(cntrl, tr);')
936
937 code.dedent()
938 code('''
939}
940
941// Actions
942''')
943 if self.TBEType != None and self.EntryType != None:
944 for action in self.actions.itervalues():
945 if "c_code" not in action:
946 continue
947
948 code('''
949/** \\brief ${{action.desc}} */
950void
951$c_ident::${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr)
952{
953 DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
954 try {
955 ${{action["c_code"]}}
956 } catch (const RejectException & e) {
957 fatal("Error in action ${{ident}}:${{action.ident}}: "
958 "executed a peek statement with the wrong message "
959 "type specified. ");
960 }
961}
962
963''')
964 elif self.TBEType != None:
965 for action in self.actions.itervalues():
966 if "c_code" not in action:
967 continue
968
969 code('''
970/** \\brief ${{action.desc}} */
971void
972$c_ident::${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, const Address& addr)
973{
974 DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
975 ${{action["c_code"]}}
976}
977
978''')
979 elif self.EntryType != None:
980 for action in self.actions.itervalues():
981 if "c_code" not in action:
982 continue
983
984 code('''
985/** \\brief ${{action.desc}} */
986void
987$c_ident::${{action.ident}}(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr)
988{
989 DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
990 ${{action["c_code"]}}
991}
992
993''')
994 else:
995 for action in self.actions.itervalues():
996 if "c_code" not in action:
997 continue
998
999 code('''
1000/** \\brief ${{action.desc}} */
1001void
1002$c_ident::${{action.ident}}(const Address& addr)
1003{
1004 DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
1005 ${{action["c_code"]}}
1006}
1007
1008''')
1009 for func in self.functions:
1010 code(func.generateCode())
1011
1012 # Function for functional writes to messages buffered in the controller
1013 code('''
1014int
1015$c_ident::functionalWriteBuffers(PacketPtr& pkt)
1016{
1017 int num_functional_writes = 0;
1018''')
1019 for var in self.objects:
1020 vtype = var.type
1021 if vtype.isBuffer:
1022 vid = "m_%s_ptr" % var.ident
1023 code('num_functional_writes += $vid->functionalWrite(pkt);')
1024
1025 for var in self.config_parameters:
1026 vtype = var.type_ast.type
1027 if vtype.isBuffer:
1028 vid = "m_%s_ptr" % var.ident
1029 code('num_functional_writes += $vid->functionalWrite(pkt);')
1030
1031 code('''
1032 return num_functional_writes;
1033}
1034''')
1035
1036 code.write(path, "%s.cc" % c_ident)
1037
1038 def printCWakeup(self, path, includes):
1039 '''Output the wakeup loop for the events'''
1040
1041 code = self.symtab.codeFormatter()
1042 ident = self.ident
1043
1044 outputRequest_types = True
1045 if len(self.request_types) == 0:
1046 outputRequest_types = False
1047
1048 code('''
1049// Auto generated C++ code started by $__file__:$__line__
1050// ${ident}: ${{self.short}}
1051
1052#include <sys/types.h>
1053#include <typeinfo>
1054#include <unistd.h>
1055
1056#include <cassert>
1057
1058#include "base/misc.hh"
1059#include "debug/RubySlicc.hh"
1060#include "mem/protocol/${ident}_Controller.hh"
1061#include "mem/protocol/${ident}_Event.hh"
1062#include "mem/protocol/${ident}_State.hh"
1063''')
1064
1065 if outputRequest_types:
1066 code('''#include "mem/protocol/${ident}_RequestType.hh"''')
1067
1068 code('''
1069#include "mem/protocol/Types.hh"
1070#include "mem/ruby/system/System.hh"
1071''')
1072
1073
1074 for include_path in includes:
1075 code('#include "${{include_path}}"')
1076
1077 port_to_buf_map, in_msg_bufs, msg_bufs = self.getBufferMaps(ident)
1078
1079 code('''
1080
1081using namespace std;
1082
1083void
1084${ident}_Controller::wakeup()
1085{
1086 int counter = 0;
1087 while (true) {
1088 unsigned char rejected[${{len(msg_bufs)}}];
1089 memset(rejected, 0, sizeof(unsigned char)*${{len(msg_bufs)}});
1090 // Some cases will put us into an infinite loop without this limit
1091 assert(counter <= m_transitions_per_cycle);
1092 if (counter == m_transitions_per_cycle) {
1093 // Count how often we are fully utilized
1094 m_fully_busy_cycles++;
1095
1096 // Wakeup in another cycle and try again
1097 scheduleEvent(Cycles(1));
1098 break;
1099 }
1100''')
1101
1102 code.indent()
1103 code.indent()
1104
1105 # InPorts
1106 #
1107 for port in self.in_ports:
1108 code.indent()
1109 code('// ${ident}InPort $port')
1110 if port.pairs.has_key("rank"):
1111 code('m_cur_in_port = ${{port.pairs["rank"]}};')
1112 else:
1113 code('m_cur_in_port = 0;')
1114 if port in port_to_buf_map:
1115 code('try {')
1116 code.indent()
1117 code('${{port["c_code_in_port"]}}')
1118
1119 if port in port_to_buf_map:
1120 code.dedent()
1121 code('''
1122 } catch (const RejectException & e) {
1123 rejected[${{port_to_buf_map[port]}}]++;
1124 }
1125''')
1126 code.dedent()
1127 code('')
1128
1129 code.dedent()
1130 code.dedent()
1131 code('''
1132 // If we got this far, we have nothing left todo or something went
1133 // wrong''')
1134 for buf_name, ports in in_msg_bufs.items():
1135 if len(ports) > 1:
1136 # only produce checks when a buffer is shared by multiple ports
1137 code('''
1138 if (${{buf_name}}->isReady() && rejected[${{port_to_buf_map[ports[0]]}}] == ${{len(ports)}})
1139 {
1140 // no port claimed the message on the top of this buffer
1141 panic("Runtime Error at Ruby Time: %d. "
1142 "All ports rejected a message. "
1143 "You are probably sending a message type to this controller "
1144 "over a virtual network that do not define an in_port for "
1145 "the incoming message type.\\n",
1146 Cycles(1));
1147 }
1148''')
1149 code('''
1150 break;
1151 }
1152}
1153''')
1154
1155 code.write(path, "%s_Wakeup.cc" % self.ident)
1156
1157 def printCSwitch(self, path):
1158 '''Output switch statement for transition table'''
1159
1160 code = self.symtab.codeFormatter()
1161 ident = self.ident
1162
1163 code('''
1164// Auto generated C++ code started by $__file__:$__line__
1165// ${ident}: ${{self.short}}
1166
1167#include <cassert>
1168
1169#include "base/misc.hh"
1170#include "base/trace.hh"
1171#include "debug/ProtocolTrace.hh"
1172#include "debug/RubyGenerated.hh"
1173#include "mem/protocol/${ident}_Controller.hh"
1174#include "mem/protocol/${ident}_Event.hh"
1175#include "mem/protocol/${ident}_State.hh"
1176#include "mem/protocol/Types.hh"
1177#include "mem/ruby/system/System.hh"
1178
1179#define HASH_FUN(state, event) ((int(state)*${ident}_Event_NUM)+int(event))
1180
1181#define GET_TRANSITION_COMMENT() (${ident}_transitionComment.str())
1182#define CLEAR_TRANSITION_COMMENT() (${ident}_transitionComment.str(""))
1183
1184TransitionResult
1185${ident}_Controller::doTransition(${ident}_Event event,
1186''')
1187 if self.EntryType != None:
1188 code('''
1189 ${{self.EntryType.c_ident}}* m_cache_entry_ptr,
1190''')
1191 if self.TBEType != None:
1192 code('''
1193 ${{self.TBEType.c_ident}}* m_tbe_ptr,
1194''')
1195 code('''
1196 const Address addr)
1197{
1198''')
1199 code.indent()
1200
1201 if self.TBEType != None and self.EntryType != None:
1202 code('${ident}_State state = getState(m_tbe_ptr, m_cache_entry_ptr, addr);')
1203 elif self.TBEType != None:
1204 code('${ident}_State state = getState(m_tbe_ptr, addr);')
1205 elif self.EntryType != None:
1206 code('${ident}_State state = getState(m_cache_entry_ptr, addr);')
1207 else:
1208 code('${ident}_State state = getState(addr);')
1209
1210 code('''
1211${ident}_State next_state = state;
1212
1213DPRINTF(RubyGenerated, "%s, Time: %lld, state: %s, event: %s, addr: %s\\n",
1214 *this, curCycle(), ${ident}_State_to_string(state),
1215 ${ident}_Event_to_string(event), addr);
1216
1217TransitionResult result =
1218''')
1219 if self.TBEType != None and self.EntryType != None:
1220 code('doTransitionWorker(event, state, next_state, m_tbe_ptr, m_cache_entry_ptr, addr);')
1221 elif self.TBEType != None:
1222 code('doTransitionWorker(event, state, next_state, m_tbe_ptr, addr);')
1223 elif self.EntryType != None:
1224 code('doTransitionWorker(event, state, next_state, m_cache_entry_ptr, addr);')
1225 else:
1226 code('doTransitionWorker(event, state, next_state, addr);')
1227
1228 port_to_buf_map, in_msg_bufs, msg_bufs = self.getBufferMaps(ident)
1229
1230 code('''
1231
1232if (result == TransitionResult_Valid) {
1233 DPRINTF(RubyGenerated, "next_state: %s\\n",
1234 ${ident}_State_to_string(next_state));
1235 countTransition(state, event);
1236
1237 DPRINTFR(ProtocolTrace, "%15d %3s %10s%20s %6s>%-6s %s %s\\n",
1238 curTick(), m_version, "${ident}",
1239 ${ident}_Event_to_string(event),
1240 ${ident}_State_to_string(state),
1241 ${ident}_State_to_string(next_state),
1242 addr, GET_TRANSITION_COMMENT());
1243
1244 CLEAR_TRANSITION_COMMENT();
1245''')
1246 if self.TBEType != None and self.EntryType != None:
1247 code('setState(m_tbe_ptr, m_cache_entry_ptr, addr, next_state);')
1248 code('setAccessPermission(m_cache_entry_ptr, addr, next_state);')
1249 elif self.TBEType != None:
1250 code('setState(m_tbe_ptr, addr, next_state);')
1251 code('setAccessPermission(addr, next_state);')
1252 elif self.EntryType != None:
1253 code('setState(m_cache_entry_ptr, addr, next_state);')
1254 code('setAccessPermission(m_cache_entry_ptr, addr, next_state);')
1255 else:
1256 code('setState(addr, next_state);')
1257 code('setAccessPermission(addr, next_state);')
1258
1259 code('''
1260} else if (result == TransitionResult_ResourceStall) {
1261 DPRINTFR(ProtocolTrace, "%15s %3s %10s%20s %6s>%-6s %s %s\\n",
1262 curTick(), m_version, "${ident}",
1263 ${ident}_Event_to_string(event),
1264 ${ident}_State_to_string(state),
1265 ${ident}_State_to_string(next_state),
1266 addr, "Resource Stall");
1267} else if (result == TransitionResult_ProtocolStall) {
1268 DPRINTF(RubyGenerated, "stalling\\n");
1269 DPRINTFR(ProtocolTrace, "%15s %3s %10s%20s %6s>%-6s %s %s\\n",
1270 curTick(), m_version, "${ident}",
1271 ${ident}_Event_to_string(event),
1272 ${ident}_State_to_string(state),
1273 ${ident}_State_to_string(next_state),
1274 addr, "Protocol Stall");
1275}
1276
1277return result;
1278''')
1279 code.dedent()
1280 code('''
1281}
1282
1283TransitionResult
1284${ident}_Controller::doTransitionWorker(${ident}_Event event,
1285 ${ident}_State state,
1286 ${ident}_State& next_state,
1287''')
1288
1289 if self.TBEType != None:
1290 code('''
1291 ${{self.TBEType.c_ident}}*& m_tbe_ptr,
1292''')
1293 if self.EntryType != None:
1294 code('''
1295 ${{self.EntryType.c_ident}}*& m_cache_entry_ptr,
1296''')
1297 code('''
1298 const Address& addr)
1299{
1300 switch(HASH_FUN(state, event)) {
1301''')
1302
1303 # This map will allow suppress generating duplicate code
1304 cases = orderdict()
1305
1306 for trans in self.transitions:
1307 case_string = "%s_State_%s, %s_Event_%s" % \
1308 (self.ident, trans.state.ident, self.ident, trans.event.ident)
1309
1310 case = self.symtab.codeFormatter()
1311 # Only set next_state if it changes
1312 if trans.state != trans.nextState:
1313 ns_ident = trans.nextState.ident
1314 case('next_state = ${ident}_State_${ns_ident};')
1313 if trans.nextState.isWildcard():
1314 # When * is encountered as an end state of a transition,
1315 # the next state is determined by calling the
1316 # machine-specific getNextState function. The next state
1317 # is determined before any actions of the transition
1318 # execute, and therefore the next state calculation cannot
1319 # depend on any of the transitionactions.
1320 case('next_state = getNextState(addr);')
1321 else:
1322 ns_ident = trans.nextState.ident
1323 case('next_state = ${ident}_State_${ns_ident};')
1315
1316 actions = trans.actions
1317 request_types = trans.request_types
1318
1319 # Check for resources
1320 case_sorter = []
1321 res = trans.resources
1322 for key,val in res.iteritems():
1323 val = '''
1324if (!%s.areNSlotsAvailable(%s))
1325 return TransitionResult_ResourceStall;
1326''' % (key.code, val)
1327 case_sorter.append(val)
1328
1329 # Check all of the request_types for resource constraints
1330 for request_type in request_types:
1331 val = '''
1332if (!checkResourceAvailable(%s_RequestType_%s, addr)) {
1333 return TransitionResult_ResourceStall;
1334}
1335''' % (self.ident, request_type.ident)
1336 case_sorter.append(val)
1337
1338 # Emit the code sequences in a sorted order. This makes the
1339 # output deterministic (without this the output order can vary
1340 # since Map's keys() on a vector of pointers is not deterministic
1341 for c in sorted(case_sorter):
1342 case("$c")
1343
1344 # Record access types for this transition
1345 for request_type in request_types:
1346 case('recordRequestType(${ident}_RequestType_${{request_type.ident}}, addr);')
1347
1348 # Figure out if we stall
1349 stall = False
1350 for action in actions:
1351 if action.ident == "z_stall":
1352 stall = True
1353 break
1354
1355 if stall:
1356 case('return TransitionResult_ProtocolStall;')
1357 else:
1358 if self.TBEType != None and self.EntryType != None:
1359 for action in actions:
1360 case('${{action.ident}}(m_tbe_ptr, m_cache_entry_ptr, addr);')
1361 elif self.TBEType != None:
1362 for action in actions:
1363 case('${{action.ident}}(m_tbe_ptr, addr);')
1364 elif self.EntryType != None:
1365 for action in actions:
1366 case('${{action.ident}}(m_cache_entry_ptr, addr);')
1367 else:
1368 for action in actions:
1369 case('${{action.ident}}(addr);')
1370 case('return TransitionResult_Valid;')
1371
1372 case = str(case)
1373
1374 # Look to see if this transition code is unique.
1375 if case not in cases:
1376 cases[case] = []
1377
1378 cases[case].append(case_string)
1379
1380 # Walk through all of the unique code blocks and spit out the
1381 # corresponding case statement elements
1382 for case,transitions in cases.iteritems():
1383 # Iterative over all the multiple transitions that share
1384 # the same code
1385 for trans in transitions:
1386 code(' case HASH_FUN($trans):')
1387 code(' $case\n')
1388
1389 code('''
1390 default:
1391 panic("Invalid transition\\n"
1392 "%s time: %d addr: %s event: %s state: %s\\n",
1393 name(), curCycle(), addr, event, state);
1394 }
1395
1396 return TransitionResult_Valid;
1397}
1398''')
1399 code.write(path, "%s_Transitions.cc" % self.ident)
1400
1401
1402 # **************************
1403 # ******* HTML Files *******
1404 # **************************
1405 def frameRef(self, click_href, click_target, over_href, over_num, text):
1406 code = self.symtab.codeFormatter(fix_newlines=False)
1407 code("""<A href=\"$click_href\" target=\"$click_target\" onmouseover=\"
1408 if (parent.frames[$over_num].location != parent.location + '$over_href') {
1409 parent.frames[$over_num].location='$over_href'
1410 }\">
1411 ${{html.formatShorthand(text)}}
1412 </A>""")
1413 return str(code)
1414
1415 def writeHTMLFiles(self, path):
1416 # Create table with no row hilighted
1417 self.printHTMLTransitions(path, None)
1418
1419 # Generate transition tables
1420 for state in self.states.itervalues():
1421 self.printHTMLTransitions(path, state)
1422
1423 # Generate action descriptions
1424 for action in self.actions.itervalues():
1425 name = "%s_action_%s.html" % (self.ident, action.ident)
1426 code = html.createSymbol(action, "Action")
1427 code.write(path, name)
1428
1429 # Generate state descriptions
1430 for state in self.states.itervalues():
1431 name = "%s_State_%s.html" % (self.ident, state.ident)
1432 code = html.createSymbol(state, "State")
1433 code.write(path, name)
1434
1435 # Generate event descriptions
1436 for event in self.events.itervalues():
1437 name = "%s_Event_%s.html" % (self.ident, event.ident)
1438 code = html.createSymbol(event, "Event")
1439 code.write(path, name)
1440
1441 def printHTMLTransitions(self, path, active_state):
1442 code = self.symtab.codeFormatter()
1443
1444 code('''
1445<HTML>
1446<BODY link="blue" vlink="blue">
1447
1448<H1 align="center">${{html.formatShorthand(self.short)}}:
1449''')
1450 code.indent()
1451 for i,machine in enumerate(self.symtab.getAllType(StateMachine)):
1452 mid = machine.ident
1453 if i != 0:
1454 extra = " - "
1455 else:
1456 extra = ""
1457 if machine == self:
1458 code('$extra$mid')
1459 else:
1460 code('$extra<A target="Table" href="${mid}_table.html">$mid</A>')
1461 code.dedent()
1462
1463 code("""
1464</H1>
1465
1466<TABLE border=1>
1467<TR>
1468 <TH> </TH>
1469""")
1470
1471 for event in self.events.itervalues():
1472 href = "%s_Event_%s.html" % (self.ident, event.ident)
1473 ref = self.frameRef(href, "Status", href, "1", event.short)
1474 code('<TH bgcolor=white>$ref</TH>')
1475
1476 code('</TR>')
1477 # -- Body of table
1478 for state in self.states.itervalues():
1479 # -- Each row
1480 if state == active_state:
1481 color = "yellow"
1482 else:
1483 color = "white"
1484
1485 click = "%s_table_%s.html" % (self.ident, state.ident)
1486 over = "%s_State_%s.html" % (self.ident, state.ident)
1487 text = html.formatShorthand(state.short)
1488 ref = self.frameRef(click, "Table", over, "1", state.short)
1489 code('''
1490<TR>
1491 <TH bgcolor=$color>$ref</TH>
1492''')
1493
1494 # -- One column for each event
1495 for event in self.events.itervalues():
1496 trans = self.table.get((state,event), None)
1497 if trans is None:
1498 # This is the no transition case
1499 if state == active_state:
1500 color = "#C0C000"
1501 else:
1502 color = "lightgrey"
1503
1504 code('<TD bgcolor=$color>&nbsp;</TD>')
1505 continue
1506
1507 next = trans.nextState
1508 stall_action = False
1509
1510 # -- Get the actions
1511 for action in trans.actions:
1512 if action.ident == "z_stall" or \
1513 action.ident == "zz_recycleMandatoryQueue":
1514 stall_action = True
1515
1516 # -- Print out "actions/next-state"
1517 if stall_action:
1518 if state == active_state:
1519 color = "#C0C000"
1520 else:
1521 color = "lightgrey"
1522
1523 elif active_state and next.ident == active_state.ident:
1524 color = "aqua"
1525 elif state == active_state:
1526 color = "yellow"
1527 else:
1528 color = "white"
1529
1530 code('<TD bgcolor=$color>')
1531 for action in trans.actions:
1532 href = "%s_action_%s.html" % (self.ident, action.ident)
1533 ref = self.frameRef(href, "Status", href, "1",
1534 action.short)
1535 code(' $ref')
1536 if next != state:
1537 if trans.actions:
1538 code('/')
1539 click = "%s_table_%s.html" % (self.ident, next.ident)
1540 over = "%s_State_%s.html" % (self.ident, next.ident)
1541 ref = self.frameRef(click, "Table", over, "1", next.short)
1542 code("$ref")
1543 code("</TD>")
1544
1545 # -- Each row
1546 if state == active_state:
1547 color = "yellow"
1548 else:
1549 color = "white"
1550
1551 click = "%s_table_%s.html" % (self.ident, state.ident)
1552 over = "%s_State_%s.html" % (self.ident, state.ident)
1553 ref = self.frameRef(click, "Table", over, "1", state.short)
1554 code('''
1555 <TH bgcolor=$color>$ref</TH>
1556</TR>
1557''')
1558 code('''
1559<!- Column footer->
1560<TR>
1561 <TH> </TH>
1562''')
1563
1564 for event in self.events.itervalues():
1565 href = "%s_Event_%s.html" % (self.ident, event.ident)
1566 ref = self.frameRef(href, "Status", href, "1", event.short)
1567 code('<TH bgcolor=white>$ref</TH>')
1568 code('''
1569</TR>
1570</TABLE>
1571</BODY></HTML>
1572''')
1573
1574
1575 if active_state:
1576 name = "%s_table_%s.html" % (self.ident, active_state.ident)
1577 else:
1578 name = "%s_table.html" % self.ident
1579 code.write(path, name)
1580
1581__all__ = [ "StateMachine" ]
1324
1325 actions = trans.actions
1326 request_types = trans.request_types
1327
1328 # Check for resources
1329 case_sorter = []
1330 res = trans.resources
1331 for key,val in res.iteritems():
1332 val = '''
1333if (!%s.areNSlotsAvailable(%s))
1334 return TransitionResult_ResourceStall;
1335''' % (key.code, val)
1336 case_sorter.append(val)
1337
1338 # Check all of the request_types for resource constraints
1339 for request_type in request_types:
1340 val = '''
1341if (!checkResourceAvailable(%s_RequestType_%s, addr)) {
1342 return TransitionResult_ResourceStall;
1343}
1344''' % (self.ident, request_type.ident)
1345 case_sorter.append(val)
1346
1347 # Emit the code sequences in a sorted order. This makes the
1348 # output deterministic (without this the output order can vary
1349 # since Map's keys() on a vector of pointers is not deterministic
1350 for c in sorted(case_sorter):
1351 case("$c")
1352
1353 # Record access types for this transition
1354 for request_type in request_types:
1355 case('recordRequestType(${ident}_RequestType_${{request_type.ident}}, addr);')
1356
1357 # Figure out if we stall
1358 stall = False
1359 for action in actions:
1360 if action.ident == "z_stall":
1361 stall = True
1362 break
1363
1364 if stall:
1365 case('return TransitionResult_ProtocolStall;')
1366 else:
1367 if self.TBEType != None and self.EntryType != None:
1368 for action in actions:
1369 case('${{action.ident}}(m_tbe_ptr, m_cache_entry_ptr, addr);')
1370 elif self.TBEType != None:
1371 for action in actions:
1372 case('${{action.ident}}(m_tbe_ptr, addr);')
1373 elif self.EntryType != None:
1374 for action in actions:
1375 case('${{action.ident}}(m_cache_entry_ptr, addr);')
1376 else:
1377 for action in actions:
1378 case('${{action.ident}}(addr);')
1379 case('return TransitionResult_Valid;')
1380
1381 case = str(case)
1382
1383 # Look to see if this transition code is unique.
1384 if case not in cases:
1385 cases[case] = []
1386
1387 cases[case].append(case_string)
1388
1389 # Walk through all of the unique code blocks and spit out the
1390 # corresponding case statement elements
1391 for case,transitions in cases.iteritems():
1392 # Iterative over all the multiple transitions that share
1393 # the same code
1394 for trans in transitions:
1395 code(' case HASH_FUN($trans):')
1396 code(' $case\n')
1397
1398 code('''
1399 default:
1400 panic("Invalid transition\\n"
1401 "%s time: %d addr: %s event: %s state: %s\\n",
1402 name(), curCycle(), addr, event, state);
1403 }
1404
1405 return TransitionResult_Valid;
1406}
1407''')
1408 code.write(path, "%s_Transitions.cc" % self.ident)
1409
1410
1411 # **************************
1412 # ******* HTML Files *******
1413 # **************************
1414 def frameRef(self, click_href, click_target, over_href, over_num, text):
1415 code = self.symtab.codeFormatter(fix_newlines=False)
1416 code("""<A href=\"$click_href\" target=\"$click_target\" onmouseover=\"
1417 if (parent.frames[$over_num].location != parent.location + '$over_href') {
1418 parent.frames[$over_num].location='$over_href'
1419 }\">
1420 ${{html.formatShorthand(text)}}
1421 </A>""")
1422 return str(code)
1423
1424 def writeHTMLFiles(self, path):
1425 # Create table with no row hilighted
1426 self.printHTMLTransitions(path, None)
1427
1428 # Generate transition tables
1429 for state in self.states.itervalues():
1430 self.printHTMLTransitions(path, state)
1431
1432 # Generate action descriptions
1433 for action in self.actions.itervalues():
1434 name = "%s_action_%s.html" % (self.ident, action.ident)
1435 code = html.createSymbol(action, "Action")
1436 code.write(path, name)
1437
1438 # Generate state descriptions
1439 for state in self.states.itervalues():
1440 name = "%s_State_%s.html" % (self.ident, state.ident)
1441 code = html.createSymbol(state, "State")
1442 code.write(path, name)
1443
1444 # Generate event descriptions
1445 for event in self.events.itervalues():
1446 name = "%s_Event_%s.html" % (self.ident, event.ident)
1447 code = html.createSymbol(event, "Event")
1448 code.write(path, name)
1449
1450 def printHTMLTransitions(self, path, active_state):
1451 code = self.symtab.codeFormatter()
1452
1453 code('''
1454<HTML>
1455<BODY link="blue" vlink="blue">
1456
1457<H1 align="center">${{html.formatShorthand(self.short)}}:
1458''')
1459 code.indent()
1460 for i,machine in enumerate(self.symtab.getAllType(StateMachine)):
1461 mid = machine.ident
1462 if i != 0:
1463 extra = " - "
1464 else:
1465 extra = ""
1466 if machine == self:
1467 code('$extra$mid')
1468 else:
1469 code('$extra<A target="Table" href="${mid}_table.html">$mid</A>')
1470 code.dedent()
1471
1472 code("""
1473</H1>
1474
1475<TABLE border=1>
1476<TR>
1477 <TH> </TH>
1478""")
1479
1480 for event in self.events.itervalues():
1481 href = "%s_Event_%s.html" % (self.ident, event.ident)
1482 ref = self.frameRef(href, "Status", href, "1", event.short)
1483 code('<TH bgcolor=white>$ref</TH>')
1484
1485 code('</TR>')
1486 # -- Body of table
1487 for state in self.states.itervalues():
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 text = html.formatShorthand(state.short)
1497 ref = self.frameRef(click, "Table", over, "1", state.short)
1498 code('''
1499<TR>
1500 <TH bgcolor=$color>$ref</TH>
1501''')
1502
1503 # -- One column for each event
1504 for event in self.events.itervalues():
1505 trans = self.table.get((state,event), None)
1506 if trans is None:
1507 # This is the no transition case
1508 if state == active_state:
1509 color = "#C0C000"
1510 else:
1511 color = "lightgrey"
1512
1513 code('<TD bgcolor=$color>&nbsp;</TD>')
1514 continue
1515
1516 next = trans.nextState
1517 stall_action = False
1518
1519 # -- Get the actions
1520 for action in trans.actions:
1521 if action.ident == "z_stall" or \
1522 action.ident == "zz_recycleMandatoryQueue":
1523 stall_action = True
1524
1525 # -- Print out "actions/next-state"
1526 if stall_action:
1527 if state == active_state:
1528 color = "#C0C000"
1529 else:
1530 color = "lightgrey"
1531
1532 elif active_state and next.ident == active_state.ident:
1533 color = "aqua"
1534 elif state == active_state:
1535 color = "yellow"
1536 else:
1537 color = "white"
1538
1539 code('<TD bgcolor=$color>')
1540 for action in trans.actions:
1541 href = "%s_action_%s.html" % (self.ident, action.ident)
1542 ref = self.frameRef(href, "Status", href, "1",
1543 action.short)
1544 code(' $ref')
1545 if next != state:
1546 if trans.actions:
1547 code('/')
1548 click = "%s_table_%s.html" % (self.ident, next.ident)
1549 over = "%s_State_%s.html" % (self.ident, next.ident)
1550 ref = self.frameRef(click, "Table", over, "1", next.short)
1551 code("$ref")
1552 code("</TD>")
1553
1554 # -- Each row
1555 if state == active_state:
1556 color = "yellow"
1557 else:
1558 color = "white"
1559
1560 click = "%s_table_%s.html" % (self.ident, state.ident)
1561 over = "%s_State_%s.html" % (self.ident, state.ident)
1562 ref = self.frameRef(click, "Table", over, "1", state.short)
1563 code('''
1564 <TH bgcolor=$color>$ref</TH>
1565</TR>
1566''')
1567 code('''
1568<!- Column footer->
1569<TR>
1570 <TH> </TH>
1571''')
1572
1573 for event in self.events.itervalues():
1574 href = "%s_Event_%s.html" % (self.ident, event.ident)
1575 ref = self.frameRef(href, "Status", href, "1", event.short)
1576 code('<TH bgcolor=white>$ref</TH>')
1577 code('''
1578</TR>
1579</TABLE>
1580</BODY></HTML>
1581''')
1582
1583
1584 if active_state:
1585 name = "%s_table_%s.html" % (self.ident, active_state.ident)
1586 else:
1587 name = "%s_table.html" % self.ident
1588 code.write(path, name)
1589
1590__all__ = [ "StateMachine" ]