1 # Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
2 # Copyright (c) 2009 The Hewlett-Packard Development Company
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.
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.
28 from m5
.util
import orderdict
30 from slicc
.symbols
.Symbol
import Symbol
31 from slicc
.symbols
.Var
import Var
32 import slicc
.generate
.html
as html
34 python_class_map
= {"int": "Int",
35 "std::string": "String",
37 "CacheMemory": "RubyCache",
38 "Sequencer": "RubySequencer",
39 "DirectoryMemory": "RubyDirectoryMemory",
40 "MemoryControl": "RubyMemoryControl",
41 "DMASequencer": "DMASequencer"
44 class StateMachine(Symbol
):
45 def __init__(self
, symtab
, ident
, location
, pairs
, config_parameters
):
46 super(StateMachine
, self
).__init
__(symtab
, ident
, location
, pairs
)
48 self
.config_parameters
= config_parameters
49 for param
in config_parameters
:
51 var
= Var(symtab
, param
.name
, location
, param
.type_ast
.type,
52 "(*m_%s_ptr)" % param
.name
, {}, self
)
54 var
= Var(symtab
, param
.name
, location
, param
.type_ast
.type,
55 "m_%s" % param
.name
, {}, self
)
56 self
.symtab
.registerSym(param
.name
, var
)
58 self
.states
= orderdict()
59 self
.events
= orderdict()
60 self
.actions
= orderdict()
66 self
.message_buffer_names
= []
69 return "[StateMachine: %s]" % self
.ident
71 def addState(self
, state
):
72 assert self
.table
is None
73 self
.states
[state
.ident
] = state
75 def addEvent(self
, event
):
76 assert self
.table
is None
77 self
.events
[event
.ident
] = event
79 def addAction(self
, action
):
80 assert self
.table
is None
82 # Check for duplicate action
83 for other
in self
.actions
.itervalues():
84 if action
.ident
== other
.ident
:
85 action
.warning("Duplicate action definition: %s" % action
.ident
)
86 action
.error("Duplicate action definition: %s" % action
.ident
)
87 if action
.short
== other
.short
:
88 other
.warning("Duplicate action shorthand: %s" % other
.ident
)
89 other
.warning(" shorthand = %s" % other
.short
)
90 action
.warning("Duplicate action shorthand: %s" % action
.ident
)
91 action
.error(" shorthand = %s" % action
.short
)
93 self
.actions
[action
.ident
] = action
95 def addTransition(self
, trans
):
96 assert self
.table
is None
97 self
.transitions
.append(trans
)
99 def addInPort(self
, var
):
100 self
.in_ports
.append(var
)
102 def addFunc(self
, func
):
103 # register func in the symbol table
104 self
.symtab
.registerSym(str(func
), func
)
105 self
.functions
.append(func
)
107 def addObject(self
, obj
):
108 self
.objects
.append(obj
)
110 # Needs to be called before accessing the table
111 def buildTable(self
):
112 assert self
.table
is None
116 for trans
in self
.transitions
:
117 # Track which actions we touch so we know if we use them
118 # all -- really this should be done for all symbols as
119 # part of the symbol table, then only trigger it for
120 # Actions, States, Events, etc.
122 for action
in trans
.actions
:
125 index
= (trans
.state
, trans
.event
)
127 table
[index
].warning("Duplicate transition: %s" % table
[index
])
128 trans
.error("Duplicate transition: %s" % trans
)
131 # Look at all actions to make sure we used them all
132 for action
in self
.actions
.itervalues():
134 error_msg
= "Unused action: %s" % action
.ident
136 error_msg
+= ", " + action
.desc
137 action
.warning(error_msg
)
140 def writeCodeFiles(self
, path
):
141 self
.printControllerPython(path
)
142 self
.printControllerHH(path
)
143 self
.printControllerCC(path
)
144 self
.printCSwitch(path
)
145 self
.printCWakeup(path
)
146 self
.printProfilerCC(path
)
147 self
.printProfilerHH(path
)
149 for func
in self
.functions
:
150 func
.writeCodeFiles(path
)
152 def printControllerPython(self
, path
):
153 code
= self
.symtab
.codeFormatter()
155 py_ident
= "%s_Controller" % ident
156 c_ident
= "%s_Controller" % self
.ident
158 from m5.params import *
159 from m5.SimObject import SimObject
160 from Controller import RubyController
162 class $py_ident(RubyController):
166 for param
in self
.config_parameters
:
168 if param
.default
is not None:
169 dflt_str
= str(param
.default
) + ', '
170 if python_class_map
.has_key(param
.type_ast
.type.c_ident
):
171 python_type
= python_class_map
[param
.type_ast
.type.c_ident
]
172 code('${{param.name}} = Param.${{python_type}}(${dflt_str}"")')
174 self
.error("Unknown c++ to python class conversion for c++ " \
175 "type: '%s'. Please update the python_class_map " \
176 "in StateMachine.py", param
.type_ast
.type.c_ident
)
178 code
.write(path
, '%s.py' % py_ident
)
181 def printControllerHH(self
, path
):
182 '''Output the method declarations for the class declaration'''
183 code
= self
.symtab
.codeFormatter()
185 c_ident
= "%s_Controller" % self
.ident
187 self
.message_buffer_names
= []
190 /** \\file $c_ident.hh
192 * Auto generated C++ code started by $__file__:$__line__
193 * Created by slicc definition of Module "${{self.short}}"
196 #ifndef __${ident}_CONTROLLER_HH__
197 #define __${ident}_CONTROLLER_HH__
203 #include "params/$c_ident.hh"
205 #include "mem/ruby/common/Global.hh"
206 #include "mem/ruby/common/Consumer.hh"
207 #include "mem/ruby/slicc_interface/AbstractController.hh"
208 #include "mem/protocol/TransitionResult.hh"
209 #include "mem/protocol/Types.hh"
210 #include "mem/protocol/${ident}_Profiler.hh"
214 for var
in self
.objects
:
215 if var
.type.ident
not in seen_types
and not var
.type.isPrimitive
:
216 code('#include "mem/protocol/${{var.type.c_ident}}.hh"')
217 seen_types
.add(var
.type.ident
)
219 # for adding information to the protocol debug trace
221 extern std::stringstream ${ident}_transitionComment;
223 class $c_ident : public AbstractController
225 // the coherence checker needs to call isBlockExclusive() and isBlockShared()
226 // making the Chip a friend class is an easy way to do this for now
229 typedef ${c_ident}Params Params;
230 $c_ident(const Params *p);
231 static int getNumControllers();
233 MessageBuffer* getMandatoryQueue() const;
234 const int & getVersion() const;
235 const std::string toString() const;
236 const std::string getName() const;
237 const MachineType getMachineType() const;
238 void initNetworkPtr(Network* net_ptr) { m_net_ptr = net_ptr; }
239 void print(std::ostream& out) const;
240 void printConfig(std::ostream& out) const;
242 void printStats(std::ostream& out) const;
244 void blockOnQueue(Address addr, MessageBuffer* port);
245 void unblock(Address addr);
252 for param
in self
.config_parameters
:
254 code('${{param.type_ast.type}}* m_${{param.ident}}_ptr;')
256 code('${{param.type_ast.type}} m_${{param.ident}};')
259 int m_number_of_TBEs;
261 TransitionResult doTransition(${ident}_Event event,
262 ${ident}_State state,
263 const Address& addr);
265 TransitionResult doTransitionWorker(${ident}_Event event,
266 ${ident}_State state,
267 ${ident}_State& next_state,
268 const Address& addr);
271 int m_transitions_per_cycle;
273 int m_recycle_latency;
274 std::map<std::string, std::string> m_cfg;
277 MachineID m_machineID;
279 std::map<Address, MessageBuffer*> m_block_map;
280 ${ident}_Profiler s_profiler;
281 static int m_num_controllers;
283 // Internal functions
286 for func
in self
.functions
:
287 proto
= func
.prototype
295 for action
in self
.actions
.itervalues():
296 code('/** \\brief ${{action.desc}} */')
297 code('void ${{action.ident}}(const Address& addr);')
299 # the controller internal variables
304 for var
in self
.objects
:
305 th
= var
.get("template_hack", "")
306 code('${{var.type.c_ident}}$th* m_${{var.c_ident}}_ptr;')
308 if var
.type.ident
== "MessageBuffer":
309 self
.message_buffer_names
.append("m_%s_ptr" % var
.c_ident
)
313 code('#endif // __${ident}_CONTROLLER_H__')
314 code
.write(path
, '%s.hh' % c_ident
)
316 def printControllerCC(self
, path
):
317 '''Output the actions for performing the actions'''
319 code
= self
.symtab
.codeFormatter()
321 c_ident
= "%s_Controller" % self
.ident
324 /** \\file $c_ident.cc
326 * Auto generated C++ code started by $__file__:$__line__
327 * Created by slicc definition of Module "${{self.short}}"
333 #include "base/cprintf.hh"
334 #include "mem/protocol/${ident}_Controller.hh"
335 #include "mem/protocol/${ident}_State.hh"
336 #include "mem/protocol/${ident}_Event.hh"
337 #include "mem/protocol/Types.hh"
338 #include "mem/ruby/common/Global.hh"
339 #include "mem/ruby/slicc_interface/RubySlicc_includes.hh"
340 #include "mem/ruby/system/System.hh"
345 # include object classes
347 for var
in self
.objects
:
348 if var
.type.ident
not in seen_types
and not var
.type.isPrimitive
:
349 code('#include "mem/protocol/${{var.type.c_ident}}.hh"')
350 seen_types
.add(var
.type.ident
)
354 ${c_ident}Params::create()
356 return new $c_ident(this);
359 int $c_ident::m_num_controllers = 0;
361 // for adding information to the protocol debug trace
362 stringstream ${ident}_transitionComment;
363 #define APPEND_TRANSITION_COMMENT(str) (${ident}_transitionComment << str)
365 /** \\brief constructor */
366 $c_ident::$c_ident(const Params *p)
367 : AbstractController(p)
369 m_version = p->version;
370 m_transitions_per_cycle = p->transitions_per_cycle;
371 m_buffer_size = p->buffer_size;
372 m_recycle_latency = p->recycle_latency;
373 m_number_of_TBEs = p->number_of_TBEs;
374 m_is_blocking = false;
379 # After initializing the universal machine parameters, initialize the
380 # this machines config parameters. Also detemine if these configuration
381 # params include a sequencer. This information will be used later for
382 # contecting the sequencer back to the L1 cache controller.
384 contains_sequencer
= False
385 for param
in self
.config_parameters
:
386 if param
.name
== "sequencer" or param
.name
== "dma_sequencer":
387 contains_sequencer
= True
389 code('m_${{param.name}}_ptr = p->${{param.name}};')
391 code('m_${{param.name}} = p->${{param.name}};')
394 # For the l1 cache controller, add the special atomic support which
395 # includes passing the sequencer a pointer to the controller.
397 if self
.ident
== "L1Cache":
398 if not contains_sequencer
:
399 self
.error("The L1Cache controller must include the sequencer " \
400 "configuration parameter")
403 m_sequencer_ptr->setController(this);
406 # For the DMA controller, pass the sequencer a pointer to the
409 if self
.ident
== "DMA":
410 if not contains_sequencer
:
411 self
.error("The DMA controller must include the sequencer " \
412 "configuration parameter")
415 m_dma_sequencer_ptr->setController(this);
418 code('m_num_controllers++;')
419 for var
in self
.objects
:
420 if var
.ident
.find("mandatoryQueue") >= 0:
421 code('m_${{var.c_ident}}_ptr = new ${{var.type.c_ident}}();')
430 MachineType machine_type;
433 m_machineID.type = MachineType_${ident};
434 m_machineID.num = m_version;
436 // initialize objects
437 s_profiler.setVersion(m_version);
442 for var
in self
.objects
:
444 vid
= "m_%s_ptr" % var
.c_ident
445 if "network" not in var
:
446 # Not a network port object
447 if "primitive" in vtype
:
448 code('$vid = new ${{vtype.c_ident}};')
450 code('(*$vid) = ${{var["default"]}};')
455 code('$vid = ${{var["factory"]}};')
456 elif var
.ident
.find("mandatoryQueue") < 0:
457 th
= var
.get("template_hack", "")
458 expr
= "%s = new %s%s" % (vid
, vtype
.c_ident
, th
)
461 if "non_obj" not in vtype
and not vtype
.isEnumeration
:
462 if expr
.find("TBETable") >= 0:
463 args
= "m_number_of_TBEs"
465 args
= var
.get("constructor_hack", "")
467 code('$expr($args);')
469 code('assert($vid != NULL);')
472 code('*$vid = ${{var["default"]}}; // Object default')
473 elif "default" in vtype
:
474 comment
= "Type %s default" % vtype
.ident
475 code('*$vid = ${{vtype["default"]}}; // $comment')
478 if "ordered" in var
and "trigger_queue" not in var
:
480 code('$vid->setOrdering(${{var["ordered"]}});')
485 code('$vid->setRandomization(${{var["random"]}});')
488 if vtype
.isBuffer
and \
489 "rank" in var
and "trigger_queue" not in var
:
490 code('$vid->setPriority(${{var["rank"]}});')
492 # Network port object
493 network
= var
["network"]
494 ordered
= var
["ordered"]
495 vnet
= var
["virtual_network"]
497 assert var
.machine
is not None
499 machine_type = string_to_MachineType("${{var.machine.ident}}");
500 base = MachineType_base_number(machine_type);
501 $vid = m_net_ptr->get${network}NetQueue(m_version + base, $ordered, $vnet);
504 code('assert($vid != NULL);')
509 code('$vid->setOrdering(${{var["ordered"]}});')
514 code('$vid->setRandomization(${{var["random"]}})')
518 code('$vid->setPriority(${{var["rank"]}})')
523 if (m_buffer_size > 0) {
524 $vid->resize(m_buffer_size);
528 # set description (may be overriden later by port def)
530 $vid->setDescription("[Version " + to_string(m_version) + ", ${ident}, name=${{var.c_ident}}]");
534 # Set the queue consumers
535 code
.insert_newline()
536 for port
in self
.in_ports
:
537 code('${{port.code}}.setConsumer(this);')
539 # Set the queue descriptions
540 code
.insert_newline()
541 for port
in self
.in_ports
:
542 code('${{port.code}}.setDescription("[Version " + to_string(m_version) + ", $ident, $port]");')
544 # Initialize the transition profiling
545 code
.insert_newline()
546 for trans
in self
.transitions
:
547 # Figure out if we stall
549 for action
in trans
.actions
:
550 if action
.ident
== "z_stall":
553 # Only possible if it is not a 'z' case
555 state
= "%s_State_%s" % (self
.ident
, trans
.state
.ident
)
556 event
= "%s_Event_%s" % (self
.ident
, trans
.event
.ident
)
557 code('s_profiler.possibleTransition($state, $event);')
559 # added by SS to initialize recycle_latency of message buffers
560 for buf
in self
.message_buffer_names
:
561 code("$buf->setRecycleLatency(m_recycle_latency);")
566 has_mandatory_q
= False
567 for port
in self
.in_ports
:
568 if port
.code
.find("mandatoryQueue_ptr") >= 0:
569 has_mandatory_q
= True
572 mq_ident
= "m_%s_mandatoryQueue_ptr" % self
.ident
578 $c_ident::getNumControllers()
580 return m_num_controllers;
584 $c_ident::getMandatoryQueue() const
590 $c_ident::getVersion() const
596 $c_ident::toString() const
602 $c_ident::getName() const
608 $c_ident::getMachineType() const
610 return MachineType_${ident};
614 $c_ident::blockOnQueue(Address addr, MessageBuffer* port)
616 m_is_blocking = true;
617 m_block_map[addr] = port;
621 $c_ident::unblock(Address addr)
623 m_block_map.erase(addr);
624 if (m_block_map.size() == 0) {
625 m_is_blocking = false;
630 $c_ident::print(ostream& out) const
632 out << "[$c_ident " << m_version << "]";
636 $c_ident::printConfig(ostream& out) const
638 out << "$c_ident config: " << m_name << endl;
639 out << " version: " << m_version << endl;
640 map<string, string>::const_iterator it;
641 for (it = m_cfg.begin(); it != m_cfg.end(); it++)
642 out << " " << it->first << ": " << it->second << endl;
646 $c_ident::printStats(ostream& out) const
650 # Cache and Memory Controllers have specific profilers associated with
651 # them. Print out these stats before dumping state transition stats.
653 for param
in self
.config_parameters
:
654 if param
.type_ast
.type.ident
== "CacheMemory" or \
655 param
.type_ast
.type.ident
== "DirectoryMemory" or \
656 param
.type_ast
.type.ident
== "MemoryControl":
657 assert(param
.pointer
)
658 code(' m_${{param.ident}}_ptr->printStats(out);')
661 s_profiler.dumpStats(out);
664 void $c_ident::clearStats() {
667 # Cache and Memory Controllers have specific profilers associated with
668 # them. These stats must be cleared too.
670 for param
in self
.config_parameters
:
671 if param
.type_ast
.type.ident
== "CacheMemory" or \
672 param
.type_ast
.type.ident
== "MemoryControl":
673 assert(param
.pointer
)
674 code(' m_${{param.ident}}_ptr->clearStats();')
677 s_profiler.clearStats();
683 for action
in self
.actions
.itervalues():
684 if "c_code" not in action
:
688 /** \\brief ${{action.desc}} */
690 $c_ident::${{action.ident}}(const Address& addr)
692 DEBUG_MSG(GENERATED_COMP, HighPrio, "executing");
693 ${{action["c_code"]}}
697 code
.write(path
, "%s.cc" % c_ident
)
699 def printCWakeup(self
, path
):
700 '''Output the wakeup loop for the events'''
702 code
= self
.symtab
.codeFormatter()
706 // Auto generated C++ code started by $__file__:$__line__
707 // ${ident}: ${{self.short}}
709 #include "base/misc.hh"
710 #include "mem/ruby/common/Global.hh"
711 #include "mem/ruby/slicc_interface/RubySlicc_includes.hh"
712 #include "mem/protocol/${ident}_Controller.hh"
713 #include "mem/protocol/${ident}_State.hh"
714 #include "mem/protocol/${ident}_Event.hh"
715 #include "mem/protocol/Types.hh"
716 #include "mem/ruby/system/System.hh"
721 ${ident}_Controller::wakeup()
723 // DEBUG_EXPR(GENERATED_COMP, MedPrio, *this);
724 // DEBUG_EXPR(GENERATED_COMP, MedPrio, g_eventQueue_ptr->getTime());
728 // Some cases will put us into an infinite loop without this limit
729 assert(counter <= m_transitions_per_cycle);
730 if (counter == m_transitions_per_cycle) {
731 // Count how often we are fully utilized
732 g_system_ptr->getProfiler()->controllerBusy(m_machineID);
734 // Wakeup in another cycle and try again
735 g_eventQueue_ptr->scheduleEvent(this, 1);
745 for port
in self
.in_ports
:
747 code('// ${ident}InPort $port')
748 code('${{port["c_code_in_port"]}}')
756 break; // If we got this far, we have nothing left todo
758 // g_eventQueue_ptr->scheduleEvent(this, 1);
759 // DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
763 code
.write(path
, "%s_Wakeup.cc" % self
.ident
)
765 def printCSwitch(self
, path
):
766 '''Output switch statement for transition table'''
768 code
= self
.symtab
.codeFormatter()
772 // Auto generated C++ code started by $__file__:$__line__
773 // ${ident}: ${{self.short}}
775 #include "mem/ruby/common/Global.hh"
776 #include "mem/protocol/${ident}_Controller.hh"
777 #include "mem/protocol/${ident}_State.hh"
778 #include "mem/protocol/${ident}_Event.hh"
779 #include "mem/protocol/Types.hh"
780 #include "mem/ruby/system/System.hh"
782 #define HASH_FUN(state, event) ((int(state)*${ident}_Event_NUM)+int(event))
784 #define GET_TRANSITION_COMMENT() (${ident}_transitionComment.str())
785 #define CLEAR_TRANSITION_COMMENT() (${ident}_transitionComment.str(""))
788 ${ident}_Controller::doTransition(${ident}_Event event,
789 ${ident}_State state,
792 ${ident}_State next_state = state;
794 DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
795 DEBUG_MSG(GENERATED_COMP, MedPrio, *this);
796 DEBUG_EXPR(GENERATED_COMP, MedPrio, g_eventQueue_ptr->getTime());
797 DEBUG_EXPR(GENERATED_COMP, MedPrio,state);
798 DEBUG_EXPR(GENERATED_COMP, MedPrio,event);
799 DEBUG_EXPR(GENERATED_COMP, MedPrio,addr);
801 TransitionResult result =
802 doTransitionWorker(event, state, next_state, addr);
804 if (result == TransitionResult_Valid) {
805 DEBUG_EXPR(GENERATED_COMP, MedPrio, next_state);
806 DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
807 s_profiler.countTransition(state, event);
808 if (Debug::getProtocolTrace()) {
809 g_system_ptr->getProfiler()->profileTransition("${ident}",
811 ${ident}_State_to_string(state),
812 ${ident}_Event_to_string(event),
813 ${ident}_State_to_string(next_state),
814 GET_TRANSITION_COMMENT());
816 CLEAR_TRANSITION_COMMENT();
817 ${ident}_setState(addr, next_state);
819 } else if (result == TransitionResult_ResourceStall) {
820 if (Debug::getProtocolTrace()) {
821 g_system_ptr->getProfiler()->profileTransition("${ident}",
823 ${ident}_State_to_string(state),
824 ${ident}_Event_to_string(event),
825 ${ident}_State_to_string(next_state),
828 } else if (result == TransitionResult_ProtocolStall) {
829 DEBUG_MSG(GENERATED_COMP, HighPrio, "stalling");
830 DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
831 if (Debug::getProtocolTrace()) {
832 g_system_ptr->getProfiler()->profileTransition("${ident}",
834 ${ident}_State_to_string(state),
835 ${ident}_Event_to_string(event),
836 ${ident}_State_to_string(next_state),
845 ${ident}_Controller::doTransitionWorker(${ident}_Event event,
846 ${ident}_State state,
847 ${ident}_State& next_state,
850 switch(HASH_FUN(state, event)) {
853 # This map will allow suppress generating duplicate code
856 for trans
in self
.transitions
:
857 case_string
= "%s_State_%s, %s_Event_%s" % \
858 (self
.ident
, trans
.state
.ident
, self
.ident
, trans
.event
.ident
)
860 case
= self
.symtab
.codeFormatter()
861 # Only set next_state if it changes
862 if trans
.state
!= trans
.nextState
:
863 ns_ident
= trans
.nextState
.ident
864 case('next_state = ${ident}_State_${ns_ident};')
866 actions
= trans
.actions
868 # Check for resources
870 res
= trans
.resources
871 for key
,val
in res
.iteritems():
872 if key
.type.ident
!= "DNUCAStopTable":
874 if (!%s.areNSlotsAvailable(%s))
875 return TransitionResult_ResourceStall;
876 ''' % (key
.code
, val
)
877 case_sorter
.append(val
)
880 # Emit the code sequences in a sorted order. This makes the
881 # output deterministic (without this the output order can vary
882 # since Map's keys() on a vector of pointers is not deterministic
883 for c
in sorted(case_sorter
):
886 # Figure out if we stall
888 for action
in actions
:
889 if action
.ident
== "z_stall":
894 case('return TransitionResult_ProtocolStall;')
896 for action
in actions
:
897 case('${{action.ident}}(addr);')
898 case('return TransitionResult_Valid;')
902 # Look to see if this transition code is unique.
903 if case
not in cases
:
906 cases
[case
].append(case_string
)
908 # Walk through all of the unique code blocks and spit out the
909 # corresponding case statement elements
910 for case
,transitions
in cases
.iteritems():
911 # Iterative over all the multiple transitions that share
913 for trans
in transitions
:
914 code(' case HASH_FUN($trans):')
919 WARN_EXPR(m_version);
920 WARN_EXPR(g_eventQueue_ptr->getTime());
924 ERROR_MSG(\"Invalid transition\");
926 return TransitionResult_Valid;
929 code
.write(path
, "%s_Transitions.cc" % self
.ident
)
931 def printProfilerHH(self
, path
):
932 code
= self
.symtab
.codeFormatter()
936 // Auto generated C++ code started by $__file__:$__line__
937 // ${ident}: ${{self.short}}
939 #ifndef __${ident}_PROFILER_HH_
940 #define __${ident}_PROFILER_HH_
944 #include "mem/ruby/common/Global.hh"
945 #include "mem/protocol/${ident}_State.hh"
946 #include "mem/protocol/${ident}_Event.hh"
948 class ${ident}_Profiler
952 void setVersion(int version);
953 void countTransition(${ident}_State state, ${ident}_Event event);
954 void possibleTransition(${ident}_State state, ${ident}_Event event);
955 void dumpStats(std::ostream& out) const;
959 int m_counters[${ident}_State_NUM][${ident}_Event_NUM];
960 int m_event_counters[${ident}_Event_NUM];
961 bool m_possible[${ident}_State_NUM][${ident}_Event_NUM];
965 #endif // __${ident}_PROFILER_HH__
967 code
.write(path
, "%s_Profiler.hh" % self
.ident
)
969 def printProfilerCC(self
, path
):
970 code
= self
.symtab
.codeFormatter()
974 // Auto generated C++ code started by $__file__:$__line__
975 // ${ident}: ${{self.short}}
977 #include "mem/protocol/${ident}_Profiler.hh"
979 ${ident}_Profiler::${ident}_Profiler()
981 for (int state = 0; state < ${ident}_State_NUM; state++) {
982 for (int event = 0; event < ${ident}_Event_NUM; event++) {
983 m_possible[state][event] = false;
984 m_counters[state][event] = 0;
987 for (int event = 0; event < ${ident}_Event_NUM; event++) {
988 m_event_counters[event] = 0;
993 ${ident}_Profiler::setVersion(int version)
999 ${ident}_Profiler::clearStats()
1001 for (int state = 0; state < ${ident}_State_NUM; state++) {
1002 for (int event = 0; event < ${ident}_Event_NUM; event++) {
1003 m_counters[state][event] = 0;
1007 for (int event = 0; event < ${ident}_Event_NUM; event++) {
1008 m_event_counters[event] = 0;
1012 ${ident}_Profiler::countTransition(${ident}_State state, ${ident}_Event event)
1014 assert(m_possible[state][event]);
1015 m_counters[state][event]++;
1016 m_event_counters[event]++;
1019 ${ident}_Profiler::possibleTransition(${ident}_State state,
1020 ${ident}_Event event)
1022 m_possible[state][event] = true;
1026 ${ident}_Profiler::dumpStats(std::ostream& out) const
1028 using namespace std;
1030 out << " --- ${ident} " << m_version << " ---" << endl;
1031 out << " - Event Counts -" << endl;
1032 for (int event = 0; event < ${ident}_Event_NUM; event++) {
1033 int count = m_event_counters[event];
1034 out << (${ident}_Event) event << " " << count << endl;
1037 out << " - Transitions -" << endl;
1038 for (int state = 0; state < ${ident}_State_NUM; state++) {
1039 for (int event = 0; event < ${ident}_Event_NUM; event++) {
1040 if (m_possible[state][event]) {
1041 int count = m_counters[state][event];
1042 out << (${ident}_State) state << " "
1043 << (${ident}_Event) event << " " << count;
1054 code
.write(path
, "%s_Profiler.cc" % self
.ident
)
1056 # **************************
1057 # ******* HTML Files *******
1058 # **************************
1059 def frameRef(self
, click_href
, click_target
, over_href
, over_num
, text
):
1060 code
= self
.symtab
.codeFormatter(fix_newlines
=False)
1061 code("""<A href=\"$click_href\" target=\"$click_target\" onmouseover=\"
1062 if (parent.frames[$over_num].location != parent.location + '$over_href') {
1063 parent.frames[$over_num].location='$over_href'
1065 ${{html.formatShorthand(text)}}
1069 def writeHTMLFiles(self
, path
):
1070 # Create table with no row hilighted
1071 self
.printHTMLTransitions(path
, None)
1073 # Generate transition tables
1074 for state
in self
.states
.itervalues():
1075 self
.printHTMLTransitions(path
, state
)
1077 # Generate action descriptions
1078 for action
in self
.actions
.itervalues():
1079 name
= "%s_action_%s.html" % (self
.ident
, action
.ident
)
1080 code
= html
.createSymbol(action
, "Action")
1081 code
.write(path
, name
)
1083 # Generate state descriptions
1084 for state
in self
.states
.itervalues():
1085 name
= "%s_State_%s.html" % (self
.ident
, state
.ident
)
1086 code
= html
.createSymbol(state
, "State")
1087 code
.write(path
, name
)
1089 # Generate event descriptions
1090 for event
in self
.events
.itervalues():
1091 name
= "%s_Event_%s.html" % (self
.ident
, event
.ident
)
1092 code
= html
.createSymbol(event
, "Event")
1093 code
.write(path
, name
)
1095 def printHTMLTransitions(self
, path
, active_state
):
1096 code
= self
.symtab
.codeFormatter()
1100 <BODY link="blue" vlink="blue">
1102 <H1 align="center">${{html.formatShorthand(self.short)}}:
1105 for i
,machine
in enumerate(self
.symtab
.getAllType(StateMachine
)):
1114 code('$extra<A target="Table" href="${mid}_table.html">$mid</A>')
1125 for event
in self
.events
.itervalues():
1126 href
= "%s_Event_%s.html" % (self
.ident
, event
.ident
)
1127 ref
= self
.frameRef(href
, "Status", href
, "1", event
.short
)
1128 code('<TH bgcolor=white>$ref</TH>')
1132 for state
in self
.states
.itervalues():
1134 if state
== active_state
:
1139 click
= "%s_table_%s.html" % (self
.ident
, state
.ident
)
1140 over
= "%s_State_%s.html" % (self
.ident
, state
.ident
)
1141 text
= html
.formatShorthand(state
.short
)
1142 ref
= self
.frameRef(click
, "Table", over
, "1", state
.short
)
1145 <TH bgcolor=$color>$ref</TH>
1148 # -- One column for each event
1149 for event
in self
.events
.itervalues():
1150 trans
= self
.table
.get((state
,event
), None)
1152 # This is the no transition case
1153 if state
== active_state
:
1158 code('<TD bgcolor=$color> </TD>')
1161 next
= trans
.nextState
1162 stall_action
= False
1164 # -- Get the actions
1165 for action
in trans
.actions
:
1166 if action
.ident
== "z_stall" or \
1167 action
.ident
== "zz_recycleMandatoryQueue":
1170 # -- Print out "actions/next-state"
1172 if state
== active_state
:
1177 elif active_state
and next
.ident
== active_state
.ident
:
1179 elif state
== active_state
:
1184 code('<TD bgcolor=$color>')
1185 for action
in trans
.actions
:
1186 href
= "%s_action_%s.html" % (self
.ident
, action
.ident
)
1187 ref
= self
.frameRef(href
, "Status", href
, "1",
1193 click
= "%s_table_%s.html" % (self
.ident
, next
.ident
)
1194 over
= "%s_State_%s.html" % (self
.ident
, next
.ident
)
1195 ref
= self
.frameRef(click
, "Table", over
, "1", next
.short
)
1200 if state
== active_state
:
1205 click
= "%s_table_%s.html" % (self
.ident
, state
.ident
)
1206 over
= "%s_State_%s.html" % (self
.ident
, state
.ident
)
1207 ref
= self
.frameRef(click
, "Table", over
, "1", state
.short
)
1209 <TH bgcolor=$color>$ref</TH>
1218 for event
in self
.events
.itervalues():
1219 href
= "%s_Event_%s.html" % (self
.ident
, event
.ident
)
1220 ref
= self
.frameRef(href
, "Status", href
, "1", event
.short
)
1221 code('<TH bgcolor=white>$ref</TH>')
1230 name
= "%s_table_%s.html" % (self
.ident
, active_state
.ident
)
1232 name
= "%s_table.html" % self
.ident
1233 code
.write(path
, name
)
1235 __all__
= [ "StateMachine" ]