Deleted Added
sdiff udiff text old ( 8189:d5ad24eb015f ) new ( 8191:777459f7c61f )
full compact
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 = {"int": "Int",
36 "std::string": "String",
37 "bool": "Bool",
38 "CacheMemory": "RubyCache",
39 "WireBuffer": "RubyWireBuffer",
40 "Sequencer": "RubySequencer",
41 "DirectoryMemory": "RubyDirectoryMemory",
42 "MemoryControl": "RubyMemoryControl",
43 "DMASequencer": "DMASequencer"
44 }
45
46class StateMachine(Symbol):
47 def __init__(self, symtab, ident, location, pairs, config_parameters):
48 super(StateMachine, self).__init__(symtab, ident, location, pairs)
49 self.table = None
50 self.config_parameters = config_parameters
51
52 for param in config_parameters:
53 if param.pointer:
54 var = Var(symtab, param.name, location, param.type_ast.type,
55 "(*m_%s_ptr)" % param.name, {}, self)
56 else:
57 var = Var(symtab, param.name, location, param.type_ast.type,
58 "m_%s" % param.name, {}, self)
59 self.symtab.registerSym(param.name, var)
60
61 self.states = orderdict()
62 self.events = orderdict()
63 self.actions = orderdict()
64 self.transitions = []
65 self.in_ports = []
66 self.functions = []
67 self.objects = []
68 self.TBEType = None
69 self.EntryType = None
70
71 self.message_buffer_names = []
72
73 def __repr__(self):
74 return "[StateMachine: %s]" % self.ident
75
76 def addState(self, state):
77 assert self.table is None
78 self.states[state.ident] = state
79
80 def addEvent(self, event):
81 assert self.table is None
82 self.events[event.ident] = event
83
84 def addAction(self, action):
85 assert self.table is None
86
87 # Check for duplicate action
88 for other in self.actions.itervalues():
89 if action.ident == other.ident:
90 action.warning("Duplicate action definition: %s" % action.ident)
91 action.error("Duplicate action definition: %s" % action.ident)
92 if action.short == other.short:
93 other.warning("Duplicate action shorthand: %s" % other.ident)
94 other.warning(" shorthand = %s" % other.short)
95 action.warning("Duplicate action shorthand: %s" % action.ident)
96 action.error(" shorthand = %s" % action.short)
97
98 self.actions[action.ident] = action
99
100 def addTransition(self, trans):
101 assert self.table is None
102 self.transitions.append(trans)
103
104 def addInPort(self, var):
105 self.in_ports.append(var)
106
107 def addFunc(self, func):
108 # register func in the symbol table
109 self.symtab.registerSym(str(func), func)
110 self.functions.append(func)
111
112 def addObject(self, obj):
113 self.objects.append(obj)
114
115 def addType(self, type):
116 type_ident = '%s' % type.c_ident
117
118 if type_ident == "%s_TBE" %self.ident:
119 if self.TBEType != None:
120 self.error("Multiple Transaction Buffer types in a " \
121 "single machine.");
122 self.TBEType = type
123
124 elif "interface" in type and "AbstractCacheEntry" == type["interface"]:
125 if self.EntryType != None:
126 self.error("Multiple AbstractCacheEntry types in a " \
127 "single machine.");
128 self.EntryType = type
129
130 # Needs to be called before accessing the table
131 def buildTable(self):
132 assert self.table is None
133
134 table = {}
135
136 for trans in self.transitions:
137 # Track which actions we touch so we know if we use them
138 # all -- really this should be done for all symbols as
139 # part of the symbol table, then only trigger it for
140 # Actions, States, Events, etc.
141
142 for action in trans.actions:
143 action.used = True
144
145 index = (trans.state, trans.event)
146 if index in table:
147 table[index].warning("Duplicate transition: %s" % table[index])
148 trans.error("Duplicate transition: %s" % trans)
149 table[index] = trans
150
151 # Look at all actions to make sure we used them all
152 for action in self.actions.itervalues():
153 if not action.used:
154 error_msg = "Unused action: %s" % action.ident
155 if "desc" in action:
156 error_msg += ", " + action.desc
157 action.warning(error_msg)
158 self.table = table
159
160 def writeCodeFiles(self, path):
161 self.printControllerPython(path)
162 self.printControllerHH(path)
163 self.printControllerCC(path)
164 self.printCSwitch(path)
165 self.printCWakeup(path)
166 self.printProfilerCC(path)
167 self.printProfilerHH(path)
168 self.printProfileDumperCC(path)
169 self.printProfileDumperHH(path)
170
171 for func in self.functions:
172 func.writeCodeFiles(path)
173
174 def printControllerPython(self, path):
175 code = self.symtab.codeFormatter()
176 ident = self.ident
177 py_ident = "%s_Controller" % ident
178 c_ident = "%s_Controller" % self.ident
179 code('''
180from m5.params import *
181from m5.SimObject import SimObject
182from Controller import RubyController
183
184class $py_ident(RubyController):
185 type = '$py_ident'
186''')
187 code.indent()
188 for param in self.config_parameters:
189 dflt_str = ''
190 if param.default is not None:
191 dflt_str = str(param.default) + ', '
192 if python_class_map.has_key(param.type_ast.type.c_ident):
193 python_type = python_class_map[param.type_ast.type.c_ident]
194 code('${{param.name}} = Param.${{python_type}}(${dflt_str}"")')
195 else:
196 self.error("Unknown c++ to python class conversion for c++ " \
197 "type: '%s'. Please update the python_class_map " \
198 "in StateMachine.py", param.type_ast.type.c_ident)
199 code.dedent()
200 code.write(path, '%s.py' % py_ident)
201
202
203 def printControllerHH(self, path):
204 '''Output the method declarations for the class declaration'''
205 code = self.symtab.codeFormatter()
206 ident = self.ident
207 c_ident = "%s_Controller" % self.ident
208
209 self.message_buffer_names = []
210
211 code('''
212/** \\file $c_ident.hh
213 *
214 * Auto generated C++ code started by $__file__:$__line__
215 * Created by slicc definition of Module "${{self.short}}"
216 */
217
218#ifndef __${ident}_CONTROLLER_HH__
219#define __${ident}_CONTROLLER_HH__
220
221#include <iostream>
222#include <sstream>
223#include <string>
224
225#include "params/$c_ident.hh"
226
227#include "mem/ruby/common/Global.hh"
228#include "mem/ruby/common/Consumer.hh"
229#include "mem/ruby/slicc_interface/AbstractController.hh"
230#include "mem/protocol/TransitionResult.hh"
231#include "mem/protocol/Types.hh"
232#include "mem/protocol/${ident}_Profiler.hh"
233#include "mem/protocol/${ident}_ProfileDumper.hh"
234''')
235
236 seen_types = set()
237 for var in self.objects:
238 if var.type.ident not in seen_types and not var.type.isPrimitive:
239 code('#include "mem/protocol/${{var.type.c_ident}}.hh"')
240 seen_types.add(var.type.ident)
241
242 # for adding information to the protocol debug trace
243 code('''
244extern std::stringstream ${ident}_transitionComment;
245
246class $c_ident : public AbstractController
247{
248// the coherence checker needs to call isBlockExclusive() and isBlockShared()
249// making the Chip a friend class is an easy way to do this for now
250
251public:
252 typedef ${c_ident}Params Params;
253 $c_ident(const Params *p);
254 static int getNumControllers();
255 void init();
256 MessageBuffer* getMandatoryQueue() const;
257 const int & getVersion() const;
258 const std::string toString() const;
259 const std::string getName() const;
260 const MachineType getMachineType() const;
261 void stallBuffer(MessageBuffer* buf, Address addr);
262 void wakeUpBuffers(Address addr);
263 void wakeUpAllBuffers();
264 void initNetworkPtr(Network* net_ptr) { m_net_ptr = net_ptr; }
265 void print(std::ostream& out) const;
266 void printConfig(std::ostream& out) const;
267 void wakeup();
268 void printStats(std::ostream& out) const;
269 void clearStats();
270 void blockOnQueue(Address addr, MessageBuffer* port);
271 void unblock(Address addr);
272
273private:
274''')
275
276 code.indent()
277 # added by SS
278 for param in self.config_parameters:
279 if param.pointer:
280 code('${{param.type_ast.type}}* m_${{param.ident}}_ptr;')
281 else:
282 code('${{param.type_ast.type}} m_${{param.ident}};')
283
284 code('''
285int m_number_of_TBEs;
286
287TransitionResult doTransition(${ident}_Event event,
288''')
289
290 if self.EntryType != None:
291 code('''
292 ${{self.EntryType.c_ident}}* m_cache_entry_ptr,
293''')
294 if self.TBEType != None:
295 code('''
296 ${{self.TBEType.c_ident}}* m_tbe_ptr,
297''')
298
299 code('''
300 const Address& addr);
301
302TransitionResult doTransitionWorker(${ident}_Event event,
303 ${ident}_State state,
304 ${ident}_State& next_state,
305''')
306
307 if self.TBEType != None:
308 code('''
309 ${{self.TBEType.c_ident}}*& m_tbe_ptr,
310''')
311 if self.EntryType != None:
312 code('''
313 ${{self.EntryType.c_ident}}*& m_cache_entry_ptr,
314''')
315
316 code('''
317 const Address& addr);
318
319std::string m_name;
320int m_transitions_per_cycle;
321int m_buffer_size;
322int m_recycle_latency;
323std::map<std::string, std::string> m_cfg;
324NodeID m_version;
325Network* m_net_ptr;
326MachineID m_machineID;
327bool m_is_blocking;
328std::map<Address, MessageBuffer*> m_block_map;
329typedef std::vector<MessageBuffer*> MsgVecType;
330typedef m5::hash_map< Address, MsgVecType* > WaitingBufType;
331WaitingBufType m_waiting_buffers;
332int m_max_in_port_rank;
333int m_cur_in_port_rank;
334static ${ident}_ProfileDumper s_profileDumper;
335${ident}_Profiler m_profiler;
336static int m_num_controllers;
337
338// Internal functions
339''')
340
341 for func in self.functions:
342 proto = func.prototype
343 if proto:
344 code('$proto')
345
346 if self.EntryType != None:
347 code('''
348
349// Set and Reset for cache_entry variable
350void set_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, AbstractCacheEntry* m_new_cache_entry);
351void unset_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr);
352// Set permissions for the cache_entry
353void set_permission(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, AccessPermission perm);
354''')
355
356 if self.TBEType != None:
357 code('''
358
359// Set and Reset for tbe variable
360void set_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${ident}_TBE* m_new_tbe);
361void unset_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr);
362''')
363
364 code('''
365
366// Actions
367''')
368 if self.TBEType != None and self.EntryType != None:
369 for action in self.actions.itervalues():
370 code('/** \\brief ${{action.desc}} */')
371 code('void ${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr);')
372 elif self.TBEType != None:
373 for action in self.actions.itervalues():
374 code('/** \\brief ${{action.desc}} */')
375 code('void ${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, const Address& addr);')
376 elif self.EntryType != None:
377 for action in self.actions.itervalues():
378 code('/** \\brief ${{action.desc}} */')
379 code('void ${{action.ident}}(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr);')
380 else:
381 for action in self.actions.itervalues():
382 code('/** \\brief ${{action.desc}} */')
383 code('void ${{action.ident}}(const Address& addr);')
384
385 # the controller internal variables
386 code('''
387
388// Objects
389''')
390 for var in self.objects:
391 th = var.get("template_hack", "")
392 code('${{var.type.c_ident}}$th* m_${{var.c_ident}}_ptr;')
393
394 if var.type.ident == "MessageBuffer":
395 self.message_buffer_names.append("m_%s_ptr" % var.c_ident)
396
397 code.dedent()
398 code('};')
399 code('#endif // __${ident}_CONTROLLER_H__')
400 code.write(path, '%s.hh' % c_ident)
401
402 def printControllerCC(self, path):
403 '''Output the actions for performing the actions'''
404
405 code = self.symtab.codeFormatter()
406 ident = self.ident
407 c_ident = "%s_Controller" % self.ident
408
409 code('''
410/** \\file $c_ident.cc
411 *
412 * Auto generated C++ code started by $__file__:$__line__
413 * Created by slicc definition of Module "${{self.short}}"
414 */
415
416#include <cassert>
417#include <sstream>
418#include <string>
419
420#include "base/cprintf.hh"
421#include "mem/protocol/${ident}_Controller.hh"
422#include "mem/protocol/${ident}_State.hh"
423#include "mem/protocol/${ident}_Event.hh"
424#include "mem/protocol/Types.hh"
425#include "mem/ruby/common/Global.hh"
426#include "mem/ruby/slicc_interface/RubySlicc_includes.hh"
427#include "mem/ruby/system/System.hh"
428
429using namespace std;
430''')
431
432 # include object classes
433 seen_types = set()
434 for var in self.objects:
435 if var.type.ident not in seen_types and not var.type.isPrimitive:
436 code('#include "mem/protocol/${{var.type.c_ident}}.hh"')
437 seen_types.add(var.type.ident)
438
439 code('''
440$c_ident *
441${c_ident}Params::create()
442{
443 return new $c_ident(this);
444}
445
446int $c_ident::m_num_controllers = 0;
447${ident}_ProfileDumper $c_ident::s_profileDumper;
448
449// for adding information to the protocol debug trace
450stringstream ${ident}_transitionComment;
451#define APPEND_TRANSITION_COMMENT(str) (${ident}_transitionComment << str)
452
453/** \\brief constructor */
454$c_ident::$c_ident(const Params *p)
455 : AbstractController(p)
456{
457 m_version = p->version;
458 m_transitions_per_cycle = p->transitions_per_cycle;
459 m_buffer_size = p->buffer_size;
460 m_recycle_latency = p->recycle_latency;
461 m_number_of_TBEs = p->number_of_TBEs;
462 m_is_blocking = false;
463''')
464 #
465 # max_port_rank is used to size vectors and thus should be one plus the
466 # largest port rank
467 #
468 max_port_rank = self.in_ports[0].pairs["max_port_rank"] + 1
469 code(' m_max_in_port_rank = $max_port_rank;')
470 code.indent()
471
472 #
473 # After initializing the universal machine parameters, initialize the
474 # this machines config parameters. Also detemine if these configuration
475 # params include a sequencer. This information will be used later for
476 # contecting the sequencer back to the L1 cache controller.
477 #
478 contains_dma_sequencer = False
479 sequencers = []
480 for param in self.config_parameters:
481 if param.name == "dma_sequencer":
482 contains_dma_sequencer = True
483 elif re.compile("sequencer").search(param.name):
484 sequencers.append(param.name)
485 if param.pointer:
486 code('m_${{param.name}}_ptr = p->${{param.name}};')
487 else:
488 code('m_${{param.name}} = p->${{param.name}};')
489
490 #
491 # For the l1 cache controller, add the special atomic support which
492 # includes passing the sequencer a pointer to the controller.
493 #
494 if self.ident == "L1Cache":
495 if not sequencers:
496 self.error("The L1Cache controller must include the sequencer " \
497 "configuration parameter")
498
499 for seq in sequencers:
500 code('''
501m_${{seq}}_ptr->setController(this);
502 ''')
503 #
504 # For the DMA controller, pass the sequencer a pointer to the
505 # controller.
506 #
507 if self.ident == "DMA":
508 if not contains_dma_sequencer:
509 self.error("The DMA controller must include the sequencer " \
510 "configuration parameter")
511
512 code('''
513m_dma_sequencer_ptr->setController(this);
514''')
515
516 code('m_num_controllers++;')
517 for var in self.objects:
518 if var.ident.find("mandatoryQueue") >= 0:
519 code('m_${{var.c_ident}}_ptr = new ${{var.type.c_ident}}();')
520
521 code.dedent()
522 code('''
523}
524
525void
526$c_ident::init()
527{
528 MachineType machine_type;
529 int base;
530
531 m_machineID.type = MachineType_${ident};
532 m_machineID.num = m_version;
533
534 // initialize objects
535 m_profiler.setVersion(m_version);
536 s_profileDumper.registerProfiler(&m_profiler);
537
538''')
539
540 code.indent()
541 for var in self.objects:
542 vtype = var.type
543 vid = "m_%s_ptr" % var.c_ident
544 if "network" not in var:
545 # Not a network port object
546 if "primitive" in vtype:
547 code('$vid = new ${{vtype.c_ident}};')
548 if "default" in var:
549 code('(*$vid) = ${{var["default"]}};')
550 else:
551 # Normal Object
552 # added by SS
553 if "factory" in var:
554 code('$vid = ${{var["factory"]}};')
555 elif var.ident.find("mandatoryQueue") < 0:
556 th = var.get("template_hack", "")
557 expr = "%s = new %s%s" % (vid, vtype.c_ident, th)
558
559 args = ""
560 if "non_obj" not in vtype and not vtype.isEnumeration:
561 if expr.find("TBETable") >= 0:
562 args = "m_number_of_TBEs"
563 else:
564 args = var.get("constructor_hack", "")
565
566 code('$expr($args);')
567
568 code('assert($vid != NULL);')
569
570 if "default" in var:
571 code('*$vid = ${{var["default"]}}; // Object default')
572 elif "default" in vtype:
573 comment = "Type %s default" % vtype.ident
574 code('*$vid = ${{vtype["default"]}}; // $comment')
575
576 # Set ordering
577 if "ordered" in var and "trigger_queue" not in var:
578 # A buffer
579 code('$vid->setOrdering(${{var["ordered"]}});')
580
581 # Set randomization
582 if "random" in var:
583 # A buffer
584 code('$vid->setRandomization(${{var["random"]}});')
585
586 # Set Priority
587 if vtype.isBuffer and \
588 "rank" in var and "trigger_queue" not in var:
589 code('$vid->setPriority(${{var["rank"]}});')
590
591 else:
592 # Network port object
593 network = var["network"]
594 ordered = var["ordered"]
595 vnet = var["virtual_network"]
596
597 assert var.machine is not None
598 code('''
599machine_type = string_to_MachineType("${{var.machine.ident}}");
600base = MachineType_base_number(machine_type);
601$vid = m_net_ptr->get${network}NetQueue(m_version + base, $ordered, $vnet);
602''')
603
604 code('assert($vid != NULL);')
605
606 # Set ordering
607 if "ordered" in var:
608 # A buffer
609 code('$vid->setOrdering(${{var["ordered"]}});')
610
611 # Set randomization
612 if "random" in var:
613 # A buffer
614 code('$vid->setRandomization(${{var["random"]}});')
615
616 # Set Priority
617 if "rank" in var:
618 code('$vid->setPriority(${{var["rank"]}})')
619
620 # Set buffer size
621 if vtype.isBuffer:
622 code('''
623if (m_buffer_size > 0) {
624 $vid->resize(m_buffer_size);
625}
626''')
627
628 # set description (may be overriden later by port def)
629 code('''
630$vid->setDescription("[Version " + to_string(m_version) + ", ${ident}, name=${{var.c_ident}}]");
631
632''')
633
634 if vtype.isBuffer:
635 if "recycle_latency" in var:
636 code('$vid->setRecycleLatency(${{var["recycle_latency"]}});')
637 else:
638 code('$vid->setRecycleLatency(m_recycle_latency);')
639
640
641 # Set the queue consumers
642 code()
643 for port in self.in_ports:
644 code('${{port.code}}.setConsumer(this);')
645
646 # Set the queue descriptions
647 code()
648 for port in self.in_ports:
649 code('${{port.code}}.setDescription("[Version " + to_string(m_version) + ", $ident, $port]");')
650
651 # Initialize the transition profiling
652 code()
653 for trans in self.transitions:
654 # Figure out if we stall
655 stall = False
656 for action in trans.actions:
657 if action.ident == "z_stall":
658 stall = True
659
660 # Only possible if it is not a 'z' case
661 if not stall:
662 state = "%s_State_%s" % (self.ident, trans.state.ident)
663 event = "%s_Event_%s" % (self.ident, trans.event.ident)
664 code('m_profiler.possibleTransition($state, $event);')
665
666 code.dedent()
667 code('}')
668
669 has_mandatory_q = False
670 for port in self.in_ports:
671 if port.code.find("mandatoryQueue_ptr") >= 0:
672 has_mandatory_q = True
673
674 if has_mandatory_q:
675 mq_ident = "m_%s_mandatoryQueue_ptr" % self.ident
676 else:
677 mq_ident = "NULL"
678
679 code('''
680int
681$c_ident::getNumControllers()
682{
683 return m_num_controllers;
684}
685
686MessageBuffer*
687$c_ident::getMandatoryQueue() const
688{
689 return $mq_ident;
690}
691
692const int &
693$c_ident::getVersion() const
694{
695 return m_version;
696}
697
698const string
699$c_ident::toString() const
700{
701 return "$c_ident";
702}
703
704const string
705$c_ident::getName() const
706{
707 return m_name;
708}
709
710const MachineType
711$c_ident::getMachineType() const
712{
713 return MachineType_${ident};
714}
715
716void
717$c_ident::stallBuffer(MessageBuffer* buf, Address addr)
718{
719 if (m_waiting_buffers.count(addr) == 0) {
720 MsgVecType* msgVec = new MsgVecType;
721 msgVec->resize(m_max_in_port_rank, NULL);
722 m_waiting_buffers[addr] = msgVec;
723 }
724 (*(m_waiting_buffers[addr]))[m_cur_in_port_rank] = buf;
725}
726
727void
728$c_ident::wakeUpBuffers(Address addr)
729{
730 if (m_waiting_buffers.count(addr) > 0) {
731 //
732 // Wake up all possible lower rank (i.e. lower priority) buffers that could
733 // be waiting on this message.
734 //
735 for (int in_port_rank = m_cur_in_port_rank - 1;
736 in_port_rank >= 0;
737 in_port_rank--) {
738 if ((*(m_waiting_buffers[addr]))[in_port_rank] != NULL) {
739 (*(m_waiting_buffers[addr]))[in_port_rank]->reanalyzeMessages(addr);
740 }
741 }
742 delete m_waiting_buffers[addr];
743 m_waiting_buffers.erase(addr);
744 }
745}
746
747void
748$c_ident::wakeUpAllBuffers()
749{
750 //
751 // Wake up all possible buffers that could be waiting on any message.
752 //
753
754 std::vector<MsgVecType*> wokeUpMsgVecs;
755
756 if(m_waiting_buffers.size() > 0) {
757 for (WaitingBufType::iterator buf_iter = m_waiting_buffers.begin();
758 buf_iter != m_waiting_buffers.end();
759 ++buf_iter) {
760 for (MsgVecType::iterator vec_iter = buf_iter->second->begin();
761 vec_iter != buf_iter->second->end();
762 ++vec_iter) {
763 if (*vec_iter != NULL) {
764 (*vec_iter)->reanalyzeAllMessages();
765 }
766 }
767 wokeUpMsgVecs.push_back(buf_iter->second);
768 }
769
770 for (std::vector<MsgVecType*>::iterator wb_iter = wokeUpMsgVecs.begin();
771 wb_iter != wokeUpMsgVecs.end();
772 ++wb_iter) {
773 delete (*wb_iter);
774 }
775
776 m_waiting_buffers.clear();
777 }
778}
779
780void
781$c_ident::blockOnQueue(Address addr, MessageBuffer* port)
782{
783 m_is_blocking = true;
784 m_block_map[addr] = port;
785}
786
787void
788$c_ident::unblock(Address addr)
789{
790 m_block_map.erase(addr);
791 if (m_block_map.size() == 0) {
792 m_is_blocking = false;
793 }
794}
795
796void
797$c_ident::print(ostream& out) const
798{
799 out << "[$c_ident " << m_version << "]";
800}
801
802void
803$c_ident::printConfig(ostream& out) const
804{
805 out << "$c_ident config: " << m_name << endl;
806 out << " version: " << m_version << endl;
807 map<string, string>::const_iterator it;
808 for (it = m_cfg.begin(); it != m_cfg.end(); it++)
809 out << " " << it->first << ": " << it->second << endl;
810}
811
812void
813$c_ident::printStats(ostream& out) const
814{
815''')
816 #
817 # Cache and Memory Controllers have specific profilers associated with
818 # them. Print out these stats before dumping state transition stats.
819 #
820 for param in self.config_parameters:
821 if param.type_ast.type.ident == "CacheMemory" or \
822 param.type_ast.type.ident == "DirectoryMemory" or \
823 param.type_ast.type.ident == "MemoryControl":
824 assert(param.pointer)
825 code(' m_${{param.ident}}_ptr->printStats(out);')
826
827 code('''
828 if (m_version == 0) {
829 s_profileDumper.dumpStats(out);
830 }
831}
832
833void $c_ident::clearStats() {
834''')
835 #
836 # Cache and Memory Controllers have specific profilers associated with
837 # them. These stats must be cleared too.
838 #
839 for param in self.config_parameters:
840 if param.type_ast.type.ident == "CacheMemory" or \
841 param.type_ast.type.ident == "MemoryControl":
842 assert(param.pointer)
843 code(' m_${{param.ident}}_ptr->clearStats();')
844
845 code('''
846 m_profiler.clearStats();
847}
848''')
849
850 if self.EntryType != None:
851 code('''
852
853// Set and Reset for cache_entry variable
854void
855$c_ident::set_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, AbstractCacheEntry* m_new_cache_entry)
856{
857 m_cache_entry_ptr = (${{self.EntryType.c_ident}}*)m_new_cache_entry;
858}
859
860void
861$c_ident::unset_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr)
862{
863 m_cache_entry_ptr = 0;
864}
865
866void
867$c_ident::set_permission(${{self.EntryType.c_ident}}*& m_cache_entry_ptr,
868 AccessPermission perm)
869{
870 if (m_cache_entry_ptr != NULL) {
871 m_cache_entry_ptr->changePermission(perm);
872 }
873}
874''')
875
876 if self.TBEType != None:
877 code('''
878
879// Set and Reset for tbe variable
880void
881$c_ident::set_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.TBEType.c_ident}}* m_new_tbe)
882{
883 m_tbe_ptr = m_new_tbe;
884}
885
886void
887$c_ident::unset_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr)
888{
889 m_tbe_ptr = NULL;
890}
891''')
892
893 code('''
894
895// Actions
896''')
897 if self.TBEType != None and self.EntryType != None:
898 for action in self.actions.itervalues():
899 if "c_code" not in action:
900 continue
901
902 code('''
903/** \\brief ${{action.desc}} */
904void
905$c_ident::${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr)
906{
907 DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
908 ${{action["c_code"]}}
909}
910
911''')
912 elif self.TBEType != None:
913 for action in self.actions.itervalues():
914 if "c_code" not in action:
915 continue
916
917 code('''
918/** \\brief ${{action.desc}} */
919void
920$c_ident::${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, const Address& addr)
921{
922 DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
923 ${{action["c_code"]}}
924}
925
926''')
927 elif self.EntryType != None:
928 for action in self.actions.itervalues():
929 if "c_code" not in action:
930 continue
931
932 code('''
933/** \\brief ${{action.desc}} */
934void
935$c_ident::${{action.ident}}(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr)
936{
937 DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
938 ${{action["c_code"]}}
939}
940
941''')
942 else:
943 for action in self.actions.itervalues():
944 if "c_code" not in action:
945 continue
946
947 code('''
948/** \\brief ${{action.desc}} */
949void
950$c_ident::${{action.ident}}(const Address& addr)
951{
952 DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
953 ${{action["c_code"]}}
954}
955
956''')
957 code.write(path, "%s.cc" % c_ident)
958
959 def printCWakeup(self, path):
960 '''Output the wakeup loop for the events'''
961
962 code = self.symtab.codeFormatter()
963 ident = self.ident
964
965 code('''
966// Auto generated C++ code started by $__file__:$__line__
967// ${ident}: ${{self.short}}
968
969#include <cassert>
970
971#include "base/misc.hh"
972#include "mem/ruby/common/Global.hh"
973#include "mem/ruby/slicc_interface/RubySlicc_includes.hh"
974#include "mem/protocol/${ident}_Controller.hh"
975#include "mem/protocol/${ident}_State.hh"
976#include "mem/protocol/${ident}_Event.hh"
977#include "mem/protocol/Types.hh"
978#include "mem/ruby/system/System.hh"
979
980using namespace std;
981
982void
983${ident}_Controller::wakeup()
984{
985 int counter = 0;
986 while (true) {
987 // Some cases will put us into an infinite loop without this limit
988 assert(counter <= m_transitions_per_cycle);
989 if (counter == m_transitions_per_cycle) {
990 // Count how often we are fully utilized
991 g_system_ptr->getProfiler()->controllerBusy(m_machineID);
992
993 // Wakeup in another cycle and try again
994 g_eventQueue_ptr->scheduleEvent(this, 1);
995 break;
996 }
997''')
998
999 code.indent()
1000 code.indent()
1001
1002 # InPorts
1003 #
1004 for port in self.in_ports:
1005 code.indent()
1006 code('// ${ident}InPort $port')
1007 if port.pairs.has_key("rank"):
1008 code('m_cur_in_port_rank = ${{port.pairs["rank"]}};')
1009 else:
1010 code('m_cur_in_port_rank = 0;')
1011 code('${{port["c_code_in_port"]}}')
1012 code.dedent()
1013
1014 code('')
1015
1016 code.dedent()
1017 code.dedent()
1018 code('''
1019 break; // If we got this far, we have nothing left todo
1020 }
1021 // g_eventQueue_ptr->scheduleEvent(this, 1);
1022}
1023''')
1024
1025 code.write(path, "%s_Wakeup.cc" % self.ident)
1026
1027 def printCSwitch(self, path):
1028 '''Output switch statement for transition table'''
1029
1030 code = self.symtab.codeFormatter()
1031 ident = self.ident
1032
1033 code('''
1034// Auto generated C++ code started by $__file__:$__line__
1035// ${ident}: ${{self.short}}
1036
1037#include <cassert>
1038
1039#include "base/misc.hh"
1040#include "base/trace.hh"
1041#include "mem/ruby/common/Global.hh"
1042#include "mem/protocol/${ident}_Controller.hh"
1043#include "mem/protocol/${ident}_State.hh"
1044#include "mem/protocol/${ident}_Event.hh"
1045#include "mem/protocol/Types.hh"
1046#include "mem/ruby/system/System.hh"
1047
1048#define HASH_FUN(state, event) ((int(state)*${ident}_Event_NUM)+int(event))
1049
1050#define GET_TRANSITION_COMMENT() (${ident}_transitionComment.str())
1051#define CLEAR_TRANSITION_COMMENT() (${ident}_transitionComment.str(""))
1052
1053TransitionResult
1054${ident}_Controller::doTransition(${ident}_Event event,
1055''')
1056 if self.EntryType != None:
1057 code('''
1058 ${{self.EntryType.c_ident}}* m_cache_entry_ptr,
1059''')
1060 if self.TBEType != None:
1061 code('''
1062 ${{self.TBEType.c_ident}}* m_tbe_ptr,
1063''')
1064 code('''
1065 const Address &addr)
1066{
1067''')
1068 if self.TBEType != None and self.EntryType != None:
1069 code('${ident}_State state = ${ident}_getState(m_tbe_ptr, m_cache_entry_ptr, addr);')
1070 elif self.TBEType != None:
1071 code('${ident}_State state = ${ident}_getState(m_tbe_ptr, addr);')
1072 elif self.EntryType != None:
1073 code('${ident}_State state = ${ident}_getState(m_cache_entry_ptr, addr);')
1074 else:
1075 code('${ident}_State state = ${ident}_getState(addr);')
1076
1077 code('''
1078 ${ident}_State next_state = state;
1079
1080 DPRINTF(RubyGenerated, "%s, Time: %lld, state: %s, event: %s, addr: %s\\n",
1081 *this,
1082 g_eventQueue_ptr->getTime(),
1083 ${ident}_State_to_string(state),
1084 ${ident}_Event_to_string(event),
1085 addr);
1086
1087 TransitionResult result =
1088''')
1089 if self.TBEType != None and self.EntryType != None:
1090 code('doTransitionWorker(event, state, next_state, m_tbe_ptr, m_cache_entry_ptr, addr);')
1091 elif self.TBEType != None:
1092 code('doTransitionWorker(event, state, next_state, m_tbe_ptr, addr);')
1093 elif self.EntryType != None:
1094 code('doTransitionWorker(event, state, next_state, m_cache_entry_ptr, addr);')
1095 else:
1096 code('doTransitionWorker(event, state, next_state, addr);')
1097
1098 code('''
1099 if (result == TransitionResult_Valid) {
1100 DPRINTF(RubyGenerated, "next_state: %s\\n",
1101 ${ident}_State_to_string(next_state));
1102 m_profiler.countTransition(state, event);
1103 DPRINTFR(ProtocolTrace, "%7d %3s %10s%20s %6s>%-6s %s %s\\n",
1104 g_eventQueue_ptr->getTime(), m_version, "${ident}",
1105 ${ident}_Event_to_string(event),
1106 ${ident}_State_to_string(state),
1107 ${ident}_State_to_string(next_state),
1108 addr, GET_TRANSITION_COMMENT());
1109
1110 CLEAR_TRANSITION_COMMENT();
1111''')
1112 if self.TBEType != None and self.EntryType != None:
1113 code('${ident}_setState(m_tbe_ptr, m_cache_entry_ptr, addr, next_state);')
1114 code('set_permission(m_cache_entry_ptr, ${ident}_State_to_permission(next_state));')
1115 elif self.TBEType != None:
1116 code('${ident}_setState(m_tbe_ptr, addr, next_state);')
1117 elif self.EntryType != None:
1118 code('${ident}_setState(m_cache_entry_ptr, addr, next_state);')
1119 code('set_permission(m_cache_entry_ptr, ${ident}_State_to_permission(next_state));')
1120 else:
1121 code('${ident}_setState(addr, next_state);')
1122
1123 code('''
1124 } else if (result == TransitionResult_ResourceStall) {
1125 DPRINTFR(ProtocolTrace, "%7s %3s %10s%20s %6s>%-6s %s %s\\n",
1126 g_eventQueue_ptr->getTime(), m_version, "${ident}",
1127 ${ident}_Event_to_string(event),
1128 ${ident}_State_to_string(state),
1129 ${ident}_State_to_string(next_state),
1130 addr, "Resource Stall");
1131 } else if (result == TransitionResult_ProtocolStall) {
1132 DPRINTF(RubyGenerated, "stalling\\n");
1133 DPRINTFR(ProtocolTrace, "%7s %3s %10s%20s %6s>%-6s %s %s\\n",
1134 g_eventQueue_ptr->getTime(), m_version, "${ident}",
1135 ${ident}_Event_to_string(event),
1136 ${ident}_State_to_string(state),
1137 ${ident}_State_to_string(next_state),
1138 addr, "Protocol Stall");
1139 }
1140
1141 return result;
1142}
1143
1144TransitionResult
1145${ident}_Controller::doTransitionWorker(${ident}_Event event,
1146 ${ident}_State state,
1147 ${ident}_State& next_state,
1148''')
1149
1150 if self.TBEType != None:
1151 code('''
1152 ${{self.TBEType.c_ident}}*& m_tbe_ptr,
1153''')
1154 if self.EntryType != None:
1155 code('''
1156 ${{self.EntryType.c_ident}}*& m_cache_entry_ptr,
1157''')
1158 code('''
1159 const Address& addr)
1160{
1161 switch(HASH_FUN(state, event)) {
1162''')
1163
1164 # This map will allow suppress generating duplicate code
1165 cases = orderdict()
1166
1167 for trans in self.transitions:
1168 case_string = "%s_State_%s, %s_Event_%s" % \
1169 (self.ident, trans.state.ident, self.ident, trans.event.ident)
1170
1171 case = self.symtab.codeFormatter()
1172 # Only set next_state if it changes
1173 if trans.state != trans.nextState:
1174 ns_ident = trans.nextState.ident
1175 case('next_state = ${ident}_State_${ns_ident};')
1176
1177 actions = trans.actions
1178
1179 # Check for resources
1180 case_sorter = []
1181 res = trans.resources
1182 for key,val in res.iteritems():
1183 if key.type.ident != "DNUCAStopTable":
1184 val = '''
1185if (!%s.areNSlotsAvailable(%s))
1186 return TransitionResult_ResourceStall;
1187''' % (key.code, val)
1188 case_sorter.append(val)
1189
1190
1191 # Emit the code sequences in a sorted order. This makes the
1192 # output deterministic (without this the output order can vary
1193 # since Map's keys() on a vector of pointers is not deterministic
1194 for c in sorted(case_sorter):
1195 case("$c")
1196
1197 # Figure out if we stall
1198 stall = False
1199 for action in actions:
1200 if action.ident == "z_stall":
1201 stall = True
1202 break
1203
1204 if stall:
1205 case('return TransitionResult_ProtocolStall;')
1206 else:
1207 if self.TBEType != None and self.EntryType != None:
1208 for action in actions:
1209 case('${{action.ident}}(m_tbe_ptr, m_cache_entry_ptr, addr);')
1210 elif self.TBEType != None:
1211 for action in actions:
1212 case('${{action.ident}}(m_tbe_ptr, addr);')
1213 elif self.EntryType != None:
1214 for action in actions:
1215 case('${{action.ident}}(m_cache_entry_ptr, addr);')
1216 else:
1217 for action in actions:
1218 case('${{action.ident}}(addr);')
1219 case('return TransitionResult_Valid;')
1220
1221 case = str(case)
1222
1223 # Look to see if this transition code is unique.
1224 if case not in cases:
1225 cases[case] = []
1226
1227 cases[case].append(case_string)
1228
1229 # Walk through all of the unique code blocks and spit out the
1230 # corresponding case statement elements
1231 for case,transitions in cases.iteritems():
1232 # Iterative over all the multiple transitions that share
1233 # the same code
1234 for trans in transitions:
1235 code(' case HASH_FUN($trans):')
1236 code(' $case')
1237
1238 code('''
1239 default:
1240 fatal("Invalid transition\\n"
1241 "%s time: %d addr: %s event: %s state: %s\\n",
1242 name(), g_eventQueue_ptr->getTime(), addr, event, state);
1243 }
1244 return TransitionResult_Valid;
1245}
1246''')
1247 code.write(path, "%s_Transitions.cc" % self.ident)
1248
1249 def printProfileDumperHH(self, path):
1250 code = self.symtab.codeFormatter()
1251 ident = self.ident
1252
1253 code('''
1254// Auto generated C++ code started by $__file__:$__line__
1255// ${ident}: ${{self.short}}
1256
1257#ifndef __${ident}_PROFILE_DUMPER_HH__
1258#define __${ident}_PROFILE_DUMPER_HH__
1259
1260#include <cassert>
1261#include <iostream>
1262#include <vector>
1263
1264#include "${ident}_Profiler.hh"
1265#include "${ident}_Event.hh"
1266
1267typedef std::vector<${ident}_Profiler *> ${ident}_profilers;
1268
1269class ${ident}_ProfileDumper
1270{
1271 public:
1272 ${ident}_ProfileDumper();
1273 void registerProfiler(${ident}_Profiler* profiler);
1274 void dumpStats(std::ostream& out) const;
1275
1276 private:
1277 ${ident}_profilers m_profilers;
1278};
1279
1280#endif // __${ident}_PROFILE_DUMPER_HH__
1281''')
1282 code.write(path, "%s_ProfileDumper.hh" % self.ident)
1283
1284 def printProfileDumperCC(self, path):
1285 code = self.symtab.codeFormatter()
1286 ident = self.ident
1287
1288 code('''
1289// Auto generated C++ code started by $__file__:$__line__
1290// ${ident}: ${{self.short}}
1291
1292#include "mem/protocol/${ident}_ProfileDumper.hh"
1293
1294${ident}_ProfileDumper::${ident}_ProfileDumper()
1295{
1296}
1297
1298void
1299${ident}_ProfileDumper::registerProfiler(${ident}_Profiler* profiler)
1300{
1301 m_profilers.push_back(profiler);
1302}
1303
1304void
1305${ident}_ProfileDumper::dumpStats(std::ostream& out) const
1306{
1307 out << " --- ${ident} ---\\n";
1308 out << " - Event Counts -\\n";
1309 for (${ident}_Event event = ${ident}_Event_FIRST;
1310 event < ${ident}_Event_NUM;
1311 ++event) {
1312 out << (${ident}_Event) event << " [";
1313 uint64 total = 0;
1314 for (int i = 0; i < m_profilers.size(); i++) {
1315 out << m_profilers[i]->getEventCount(event) << " ";
1316 total += m_profilers[i]->getEventCount(event);
1317 }
1318 out << "] " << total << "\\n";
1319 }
1320 out << "\\n";
1321 out << " - Transitions -\\n";
1322 for (${ident}_State state = ${ident}_State_FIRST;
1323 state < ${ident}_State_NUM;
1324 ++state) {
1325 for (${ident}_Event event = ${ident}_Event_FIRST;
1326 event < ${ident}_Event_NUM;
1327 ++event) {
1328 if (m_profilers[0]->isPossible(state, event)) {
1329 out << (${ident}_State) state << " "
1330 << (${ident}_Event) event << " [";
1331 uint64 total = 0;
1332 for (int i = 0; i < m_profilers.size(); i++) {
1333 out << m_profilers[i]->getTransitionCount(state, event) << " ";
1334 total += m_profilers[i]->getTransitionCount(state, event);
1335 }
1336 out << "] " << total << "\\n";
1337 }
1338 }
1339 out << "\\n";
1340 }
1341}
1342''')
1343 code.write(path, "%s_ProfileDumper.cc" % self.ident)
1344
1345 def printProfilerHH(self, path):
1346 code = self.symtab.codeFormatter()
1347 ident = self.ident
1348
1349 code('''
1350// Auto generated C++ code started by $__file__:$__line__
1351// ${ident}: ${{self.short}}
1352
1353#ifndef __${ident}_PROFILER_HH__
1354#define __${ident}_PROFILER_HH__
1355
1356#include <cassert>
1357#include <iostream>
1358
1359#include "mem/ruby/common/Global.hh"
1360#include "mem/protocol/${ident}_State.hh"
1361#include "mem/protocol/${ident}_Event.hh"
1362
1363class ${ident}_Profiler
1364{
1365 public:
1366 ${ident}_Profiler();
1367 void setVersion(int version);
1368 void countTransition(${ident}_State state, ${ident}_Event event);
1369 void possibleTransition(${ident}_State state, ${ident}_Event event);
1370 uint64 getEventCount(${ident}_Event event);
1371 bool isPossible(${ident}_State state, ${ident}_Event event);
1372 uint64 getTransitionCount(${ident}_State state, ${ident}_Event event);
1373 void clearStats();
1374
1375 private:
1376 int m_counters[${ident}_State_NUM][${ident}_Event_NUM];
1377 int m_event_counters[${ident}_Event_NUM];
1378 bool m_possible[${ident}_State_NUM][${ident}_Event_NUM];
1379 int m_version;
1380};
1381
1382#endif // __${ident}_PROFILER_HH__
1383''')
1384 code.write(path, "%s_Profiler.hh" % self.ident)
1385
1386 def printProfilerCC(self, path):
1387 code = self.symtab.codeFormatter()
1388 ident = self.ident
1389
1390 code('''
1391// Auto generated C++ code started by $__file__:$__line__
1392// ${ident}: ${{self.short}}
1393
1394#include <cassert>
1395
1396#include "mem/protocol/${ident}_Profiler.hh"
1397
1398${ident}_Profiler::${ident}_Profiler()
1399{
1400 for (int state = 0; state < ${ident}_State_NUM; state++) {
1401 for (int event = 0; event < ${ident}_Event_NUM; event++) {
1402 m_possible[state][event] = false;
1403 m_counters[state][event] = 0;
1404 }
1405 }
1406 for (int event = 0; event < ${ident}_Event_NUM; event++) {
1407 m_event_counters[event] = 0;
1408 }
1409}
1410
1411void
1412${ident}_Profiler::setVersion(int version)
1413{
1414 m_version = version;
1415}
1416
1417void
1418${ident}_Profiler::clearStats()
1419{
1420 for (int state = 0; state < ${ident}_State_NUM; state++) {
1421 for (int event = 0; event < ${ident}_Event_NUM; event++) {
1422 m_counters[state][event] = 0;
1423 }
1424 }
1425
1426 for (int event = 0; event < ${ident}_Event_NUM; event++) {
1427 m_event_counters[event] = 0;
1428 }
1429}
1430void
1431${ident}_Profiler::countTransition(${ident}_State state, ${ident}_Event event)
1432{
1433 assert(m_possible[state][event]);
1434 m_counters[state][event]++;
1435 m_event_counters[event]++;
1436}
1437void
1438${ident}_Profiler::possibleTransition(${ident}_State state,
1439 ${ident}_Event event)
1440{
1441 m_possible[state][event] = true;
1442}
1443
1444uint64
1445${ident}_Profiler::getEventCount(${ident}_Event event)
1446{
1447 return m_event_counters[event];
1448}
1449
1450bool
1451${ident}_Profiler::isPossible(${ident}_State state, ${ident}_Event event)
1452{
1453 return m_possible[state][event];
1454}
1455
1456uint64
1457${ident}_Profiler::getTransitionCount(${ident}_State state,
1458 ${ident}_Event event)
1459{
1460 return m_counters[state][event];
1461}
1462
1463''')
1464 code.write(path, "%s_Profiler.cc" % self.ident)
1465
1466 # **************************
1467 # ******* HTML Files *******
1468 # **************************
1469 def frameRef(self, click_href, click_target, over_href, over_num, text):
1470 code = self.symtab.codeFormatter(fix_newlines=False)
1471 code("""<A href=\"$click_href\" target=\"$click_target\" onmouseover=\"
1472 if (parent.frames[$over_num].location != parent.location + '$over_href') {
1473 parent.frames[$over_num].location='$over_href'
1474 }\">
1475 ${{html.formatShorthand(text)}}
1476 </A>""")
1477 return str(code)
1478
1479 def writeHTMLFiles(self, path):
1480 # Create table with no row hilighted
1481 self.printHTMLTransitions(path, None)
1482
1483 # Generate transition tables
1484 for state in self.states.itervalues():
1485 self.printHTMLTransitions(path, state)
1486
1487 # Generate action descriptions
1488 for action in self.actions.itervalues():
1489 name = "%s_action_%s.html" % (self.ident, action.ident)
1490 code = html.createSymbol(action, "Action")
1491 code.write(path, name)
1492
1493 # Generate state descriptions
1494 for state in self.states.itervalues():
1495 name = "%s_State_%s.html" % (self.ident, state.ident)
1496 code = html.createSymbol(state, "State")
1497 code.write(path, name)
1498
1499 # Generate event descriptions
1500 for event in self.events.itervalues():
1501 name = "%s_Event_%s.html" % (self.ident, event.ident)
1502 code = html.createSymbol(event, "Event")
1503 code.write(path, name)
1504
1505 def printHTMLTransitions(self, path, active_state):
1506 code = self.symtab.codeFormatter()
1507
1508 code('''
1509<HTML>
1510<BODY link="blue" vlink="blue">
1511
1512<H1 align="center">${{html.formatShorthand(self.short)}}:
1513''')
1514 code.indent()
1515 for i,machine in enumerate(self.symtab.getAllType(StateMachine)):
1516 mid = machine.ident
1517 if i != 0:
1518 extra = " - "
1519 else:
1520 extra = ""
1521 if machine == self:
1522 code('$extra$mid')
1523 else:
1524 code('$extra<A target="Table" href="${mid}_table.html">$mid</A>')
1525 code.dedent()
1526
1527 code("""
1528</H1>
1529
1530<TABLE border=1>
1531<TR>
1532 <TH> </TH>
1533""")
1534
1535 for event in self.events.itervalues():
1536 href = "%s_Event_%s.html" % (self.ident, event.ident)
1537 ref = self.frameRef(href, "Status", href, "1", event.short)
1538 code('<TH bgcolor=white>$ref</TH>')
1539
1540 code('</TR>')
1541 # -- Body of table
1542 for state in self.states.itervalues():
1543 # -- Each row
1544 if state == active_state:
1545 color = "yellow"
1546 else:
1547 color = "white"
1548
1549 click = "%s_table_%s.html" % (self.ident, state.ident)
1550 over = "%s_State_%s.html" % (self.ident, state.ident)
1551 text = html.formatShorthand(state.short)
1552 ref = self.frameRef(click, "Table", over, "1", state.short)
1553 code('''
1554<TR>
1555 <TH bgcolor=$color>$ref</TH>
1556''')
1557
1558 # -- One column for each event
1559 for event in self.events.itervalues():
1560 trans = self.table.get((state,event), None)
1561 if trans is None:
1562 # This is the no transition case
1563 if state == active_state:
1564 color = "#C0C000"
1565 else:
1566 color = "lightgrey"
1567
1568 code('<TD bgcolor=$color>&nbsp;</TD>')
1569 continue
1570
1571 next = trans.nextState
1572 stall_action = False
1573
1574 # -- Get the actions
1575 for action in trans.actions:
1576 if action.ident == "z_stall" or \
1577 action.ident == "zz_recycleMandatoryQueue":
1578 stall_action = True
1579
1580 # -- Print out "actions/next-state"
1581 if stall_action:
1582 if state == active_state:
1583 color = "#C0C000"
1584 else:
1585 color = "lightgrey"
1586
1587 elif active_state and next.ident == active_state.ident:
1588 color = "aqua"
1589 elif state == active_state:
1590 color = "yellow"
1591 else:
1592 color = "white"
1593
1594 code('<TD bgcolor=$color>')
1595 for action in trans.actions:
1596 href = "%s_action_%s.html" % (self.ident, action.ident)
1597 ref = self.frameRef(href, "Status", href, "1",
1598 action.short)
1599 code(' $ref')
1600 if next != state:
1601 if trans.actions:
1602 code('/')
1603 click = "%s_table_%s.html" % (self.ident, next.ident)
1604 over = "%s_State_%s.html" % (self.ident, next.ident)
1605 ref = self.frameRef(click, "Table", over, "1", next.short)
1606 code("$ref")
1607 code("</TD>")
1608
1609 # -- Each row
1610 if state == active_state:
1611 color = "yellow"
1612 else:
1613 color = "white"
1614
1615 click = "%s_table_%s.html" % (self.ident, state.ident)
1616 over = "%s_State_%s.html" % (self.ident, state.ident)
1617 ref = self.frameRef(click, "Table", over, "1", state.short)
1618 code('''
1619 <TH bgcolor=$color>$ref</TH>
1620</TR>
1621''')
1622 code('''
1623<!- Column footer->
1624<TR>
1625 <TH> </TH>
1626''')
1627
1628 for event in self.events.itervalues():
1629 href = "%s_Event_%s.html" % (self.ident, event.ident)
1630 ref = self.frameRef(href, "Status", href, "1", event.short)
1631 code('<TH bgcolor=white>$ref</TH>')
1632 code('''
1633</TR>
1634</TABLE>
1635</BODY></HTML>
1636''')
1637
1638
1639 if active_state:
1640 name = "%s_table_%s.html" % (self.ident, active_state.ident)
1641 else:
1642 name = "%s_table.html" % self.ident
1643 code.write(path, name)
1644
1645__all__ = [ "StateMachine" ]