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
)
148 self
.printProfileDumperCC(path
)
149 self
.printProfileDumperHH(path
)
151 for func
in self
.functions
:
152 func
.writeCodeFiles(path
)
154 def printControllerPython(self
, path
):
155 code
= self
.symtab
.codeFormatter()
157 py_ident
= "%s_Controller" % ident
158 c_ident
= "%s_Controller" % self
.ident
160 from m5.params import *
161 from m5.SimObject import SimObject
162 from Controller import RubyController
164 class $py_ident(RubyController):
168 for param
in self
.config_parameters
:
170 if param
.default
is not None:
171 dflt_str
= str(param
.default
) + ', '
172 if python_class_map
.has_key(param
.type_ast
.type.c_ident
):
173 python_type
= python_class_map
[param
.type_ast
.type.c_ident
]
174 code('${{param.name}} = Param.${{python_type}}(${dflt_str}"")')
176 self
.error("Unknown c++ to python class conversion for c++ " \
177 "type: '%s'. Please update the python_class_map " \
178 "in StateMachine.py", param
.type_ast
.type.c_ident
)
180 code
.write(path
, '%s.py' % py_ident
)
183 def printControllerHH(self
, path
):
184 '''Output the method declarations for the class declaration'''
185 code
= self
.symtab
.codeFormatter()
187 c_ident
= "%s_Controller" % self
.ident
189 self
.message_buffer_names
= []
192 /** \\file $c_ident.hh
194 * Auto generated C++ code started by $__file__:$__line__
195 * Created by slicc definition of Module "${{self.short}}"
198 #ifndef __${ident}_CONTROLLER_HH__
199 #define __${ident}_CONTROLLER_HH__
205 #include "params/$c_ident.hh"
207 #include "mem/ruby/common/Global.hh"
208 #include "mem/ruby/common/Consumer.hh"
209 #include "mem/ruby/slicc_interface/AbstractController.hh"
210 #include "mem/protocol/TransitionResult.hh"
211 #include "mem/protocol/Types.hh"
212 #include "mem/protocol/${ident}_Profiler.hh"
213 #include "mem/protocol/${ident}_ProfileDumper.hh"
217 for var
in self
.objects
:
218 if var
.type.ident
not in seen_types
and not var
.type.isPrimitive
:
219 code('#include "mem/protocol/${{var.type.c_ident}}.hh"')
220 seen_types
.add(var
.type.ident
)
222 # for adding information to the protocol debug trace
224 extern std::stringstream ${ident}_transitionComment;
226 class $c_ident : public AbstractController
228 // the coherence checker needs to call isBlockExclusive() and isBlockShared()
229 // making the Chip a friend class is an easy way to do this for now
232 typedef ${c_ident}Params Params;
233 $c_ident(const Params *p);
234 static int getNumControllers();
236 MessageBuffer* getMandatoryQueue() const;
237 const int & getVersion() const;
238 const std::string toString() const;
239 const std::string getName() const;
240 const MachineType getMachineType() const;
241 void initNetworkPtr(Network* net_ptr) { m_net_ptr = net_ptr; }
242 void print(std::ostream& out) const;
243 void printConfig(std::ostream& out) const;
245 void printStats(std::ostream& out) const;
247 void blockOnQueue(Address addr, MessageBuffer* port);
248 void unblock(Address addr);
255 for param
in self
.config_parameters
:
257 code('${{param.type_ast.type}}* m_${{param.ident}}_ptr;')
259 code('${{param.type_ast.type}} m_${{param.ident}};')
262 int m_number_of_TBEs;
264 TransitionResult doTransition(${ident}_Event event,
265 ${ident}_State state,
266 const Address& addr);
268 TransitionResult doTransitionWorker(${ident}_Event event,
269 ${ident}_State state,
270 ${ident}_State& next_state,
271 const Address& addr);
274 int m_transitions_per_cycle;
276 int m_recycle_latency;
277 std::map<std::string, std::string> m_cfg;
280 MachineID m_machineID;
282 std::map<Address, MessageBuffer*> m_block_map;
283 static ${ident}_ProfileDumper s_profileDumper;
284 ${ident}_Profiler m_profiler;
285 static int m_num_controllers;
287 // Internal functions
290 for func
in self
.functions
:
291 proto
= func
.prototype
299 for action
in self
.actions
.itervalues():
300 code('/** \\brief ${{action.desc}} */')
301 code('void ${{action.ident}}(const Address& addr);')
303 # the controller internal variables
308 for var
in self
.objects
:
309 th
= var
.get("template_hack", "")
310 code('${{var.type.c_ident}}$th* m_${{var.c_ident}}_ptr;')
312 if var
.type.ident
== "MessageBuffer":
313 self
.message_buffer_names
.append("m_%s_ptr" % var
.c_ident
)
317 code('#endif // __${ident}_CONTROLLER_H__')
318 code
.write(path
, '%s.hh' % c_ident
)
320 def printControllerCC(self
, path
):
321 '''Output the actions for performing the actions'''
323 code
= self
.symtab
.codeFormatter()
325 c_ident
= "%s_Controller" % self
.ident
328 /** \\file $c_ident.cc
330 * Auto generated C++ code started by $__file__:$__line__
331 * Created by slicc definition of Module "${{self.short}}"
337 #include "base/cprintf.hh"
338 #include "mem/protocol/${ident}_Controller.hh"
339 #include "mem/protocol/${ident}_State.hh"
340 #include "mem/protocol/${ident}_Event.hh"
341 #include "mem/protocol/Types.hh"
342 #include "mem/ruby/common/Global.hh"
343 #include "mem/ruby/slicc_interface/RubySlicc_includes.hh"
344 #include "mem/ruby/system/System.hh"
349 # include object classes
351 for var
in self
.objects
:
352 if var
.type.ident
not in seen_types
and not var
.type.isPrimitive
:
353 code('#include "mem/protocol/${{var.type.c_ident}}.hh"')
354 seen_types
.add(var
.type.ident
)
358 ${c_ident}Params::create()
360 return new $c_ident(this);
363 int $c_ident::m_num_controllers = 0;
364 ${ident}_ProfileDumper $c_ident::s_profileDumper;
366 // for adding information to the protocol debug trace
367 stringstream ${ident}_transitionComment;
368 #define APPEND_TRANSITION_COMMENT(str) (${ident}_transitionComment << str)
370 /** \\brief constructor */
371 $c_ident::$c_ident(const Params *p)
372 : AbstractController(p)
374 m_version = p->version;
375 m_transitions_per_cycle = p->transitions_per_cycle;
376 m_buffer_size = p->buffer_size;
377 m_recycle_latency = p->recycle_latency;
378 m_number_of_TBEs = p->number_of_TBEs;
379 m_is_blocking = false;
384 # After initializing the universal machine parameters, initialize the
385 # this machines config parameters. Also detemine if these configuration
386 # params include a sequencer. This information will be used later for
387 # contecting the sequencer back to the L1 cache controller.
389 contains_sequencer
= False
390 for param
in self
.config_parameters
:
391 if param
.name
== "sequencer" or param
.name
== "dma_sequencer":
392 contains_sequencer
= True
394 code('m_${{param.name}}_ptr = p->${{param.name}};')
396 code('m_${{param.name}} = p->${{param.name}};')
399 # For the l1 cache controller, add the special atomic support which
400 # includes passing the sequencer a pointer to the controller.
402 if self
.ident
== "L1Cache":
403 if not contains_sequencer
:
404 self
.error("The L1Cache controller must include the sequencer " \
405 "configuration parameter")
408 m_sequencer_ptr->setController(this);
411 # For the DMA controller, pass the sequencer a pointer to the
414 if self
.ident
== "DMA":
415 if not contains_sequencer
:
416 self
.error("The DMA controller must include the sequencer " \
417 "configuration parameter")
420 m_dma_sequencer_ptr->setController(this);
423 code('m_num_controllers++;')
424 for var
in self
.objects
:
425 if var
.ident
.find("mandatoryQueue") >= 0:
426 code('m_${{var.c_ident}}_ptr = new ${{var.type.c_ident}}();')
435 MachineType machine_type;
438 m_machineID.type = MachineType_${ident};
439 m_machineID.num = m_version;
441 // initialize objects
442 m_profiler.setVersion(m_version);
443 s_profileDumper.registerProfiler(&m_profiler);
448 for var
in self
.objects
:
450 vid
= "m_%s_ptr" % var
.c_ident
451 if "network" not in var
:
452 # Not a network port object
453 if "primitive" in vtype
:
454 code('$vid = new ${{vtype.c_ident}};')
456 code('(*$vid) = ${{var["default"]}};')
461 code('$vid = ${{var["factory"]}};')
462 elif var
.ident
.find("mandatoryQueue") < 0:
463 th
= var
.get("template_hack", "")
464 expr
= "%s = new %s%s" % (vid
, vtype
.c_ident
, th
)
467 if "non_obj" not in vtype
and not vtype
.isEnumeration
:
468 if expr
.find("TBETable") >= 0:
469 args
= "m_number_of_TBEs"
471 args
= var
.get("constructor_hack", "")
473 code('$expr($args);')
475 code('assert($vid != NULL);')
478 code('*$vid = ${{var["default"]}}; // Object default')
479 elif "default" in vtype
:
480 comment
= "Type %s default" % vtype
.ident
481 code('*$vid = ${{vtype["default"]}}; // $comment')
484 if "ordered" in var
and "trigger_queue" not in var
:
486 code('$vid->setOrdering(${{var["ordered"]}});')
491 code('$vid->setRandomization(${{var["random"]}});')
494 if vtype
.isBuffer
and \
495 "rank" in var
and "trigger_queue" not in var
:
496 code('$vid->setPriority(${{var["rank"]}});')
498 # Network port object
499 network
= var
["network"]
500 ordered
= var
["ordered"]
501 vnet
= var
["virtual_network"]
503 assert var
.machine
is not None
505 machine_type = string_to_MachineType("${{var.machine.ident}}");
506 base = MachineType_base_number(machine_type);
507 $vid = m_net_ptr->get${network}NetQueue(m_version + base, $ordered, $vnet);
510 code('assert($vid != NULL);')
515 code('$vid->setOrdering(${{var["ordered"]}});')
520 code('$vid->setRandomization(${{var["random"]}})')
524 code('$vid->setPriority(${{var["rank"]}})')
529 if (m_buffer_size > 0) {
530 $vid->resize(m_buffer_size);
534 # set description (may be overriden later by port def)
536 $vid->setDescription("[Version " + to_string(m_version) + ", ${ident}, name=${{var.c_ident}}]");
540 # Set the queue consumers
541 code
.insert_newline()
542 for port
in self
.in_ports
:
543 code('${{port.code}}.setConsumer(this);')
545 # Set the queue descriptions
546 code
.insert_newline()
547 for port
in self
.in_ports
:
548 code('${{port.code}}.setDescription("[Version " + to_string(m_version) + ", $ident, $port]");')
550 # Initialize the transition profiling
551 code
.insert_newline()
552 for trans
in self
.transitions
:
553 # Figure out if we stall
555 for action
in trans
.actions
:
556 if action
.ident
== "z_stall":
559 # Only possible if it is not a 'z' case
561 state
= "%s_State_%s" % (self
.ident
, trans
.state
.ident
)
562 event
= "%s_Event_%s" % (self
.ident
, trans
.event
.ident
)
563 code('m_profiler.possibleTransition($state, $event);')
565 # added by SS to initialize recycle_latency of message buffers
566 for buf
in self
.message_buffer_names
:
567 code("$buf->setRecycleLatency(m_recycle_latency);")
572 has_mandatory_q
= False
573 for port
in self
.in_ports
:
574 if port
.code
.find("mandatoryQueue_ptr") >= 0:
575 has_mandatory_q
= True
578 mq_ident
= "m_%s_mandatoryQueue_ptr" % self
.ident
584 $c_ident::getNumControllers()
586 return m_num_controllers;
590 $c_ident::getMandatoryQueue() const
596 $c_ident::getVersion() const
602 $c_ident::toString() const
608 $c_ident::getName() const
614 $c_ident::getMachineType() const
616 return MachineType_${ident};
620 $c_ident::blockOnQueue(Address addr, MessageBuffer* port)
622 m_is_blocking = true;
623 m_block_map[addr] = port;
627 $c_ident::unblock(Address addr)
629 m_block_map.erase(addr);
630 if (m_block_map.size() == 0) {
631 m_is_blocking = false;
636 $c_ident::print(ostream& out) const
638 out << "[$c_ident " << m_version << "]";
642 $c_ident::printConfig(ostream& out) const
644 out << "$c_ident config: " << m_name << endl;
645 out << " version: " << m_version << endl;
646 map<string, string>::const_iterator it;
647 for (it = m_cfg.begin(); it != m_cfg.end(); it++)
648 out << " " << it->first << ": " << it->second << endl;
652 $c_ident::printStats(ostream& out) const
656 # Cache and Memory Controllers have specific profilers associated with
657 # them. Print out these stats before dumping state transition stats.
659 for param
in self
.config_parameters
:
660 if param
.type_ast
.type.ident
== "CacheMemory" or \
661 param
.type_ast
.type.ident
== "DirectoryMemory" or \
662 param
.type_ast
.type.ident
== "MemoryControl":
663 assert(param
.pointer
)
664 code(' m_${{param.ident}}_ptr->printStats(out);')
667 if (m_version == 0) {
668 s_profileDumper.dumpStats(out);
672 void $c_ident::clearStats() {
675 # Cache and Memory Controllers have specific profilers associated with
676 # them. These stats must be cleared too.
678 for param
in self
.config_parameters
:
679 if param
.type_ast
.type.ident
== "CacheMemory" or \
680 param
.type_ast
.type.ident
== "MemoryControl":
681 assert(param
.pointer
)
682 code(' m_${{param.ident}}_ptr->clearStats();')
685 m_profiler.clearStats();
691 for action
in self
.actions
.itervalues():
692 if "c_code" not in action
:
696 /** \\brief ${{action.desc}} */
698 $c_ident::${{action.ident}}(const Address& addr)
700 DEBUG_MSG(GENERATED_COMP, HighPrio, "executing");
701 ${{action["c_code"]}}
705 code
.write(path
, "%s.cc" % c_ident
)
707 def printCWakeup(self
, path
):
708 '''Output the wakeup loop for the events'''
710 code
= self
.symtab
.codeFormatter()
714 // Auto generated C++ code started by $__file__:$__line__
715 // ${ident}: ${{self.short}}
717 #include "base/misc.hh"
718 #include "mem/ruby/common/Global.hh"
719 #include "mem/ruby/slicc_interface/RubySlicc_includes.hh"
720 #include "mem/protocol/${ident}_Controller.hh"
721 #include "mem/protocol/${ident}_State.hh"
722 #include "mem/protocol/${ident}_Event.hh"
723 #include "mem/protocol/Types.hh"
724 #include "mem/ruby/system/System.hh"
729 ${ident}_Controller::wakeup()
731 // DEBUG_EXPR(GENERATED_COMP, MedPrio, *this);
732 // DEBUG_EXPR(GENERATED_COMP, MedPrio, g_eventQueue_ptr->getTime());
736 // Some cases will put us into an infinite loop without this limit
737 assert(counter <= m_transitions_per_cycle);
738 if (counter == m_transitions_per_cycle) {
739 // Count how often we are fully utilized
740 g_system_ptr->getProfiler()->controllerBusy(m_machineID);
742 // Wakeup in another cycle and try again
743 g_eventQueue_ptr->scheduleEvent(this, 1);
753 for port
in self
.in_ports
:
755 code('// ${ident}InPort $port')
756 code('${{port["c_code_in_port"]}}')
764 break; // If we got this far, we have nothing left todo
766 // g_eventQueue_ptr->scheduleEvent(this, 1);
767 // DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
771 code
.write(path
, "%s_Wakeup.cc" % self
.ident
)
773 def printCSwitch(self
, path
):
774 '''Output switch statement for transition table'''
776 code
= self
.symtab
.codeFormatter()
780 // Auto generated C++ code started by $__file__:$__line__
781 // ${ident}: ${{self.short}}
783 #include "mem/ruby/common/Global.hh"
784 #include "mem/protocol/${ident}_Controller.hh"
785 #include "mem/protocol/${ident}_State.hh"
786 #include "mem/protocol/${ident}_Event.hh"
787 #include "mem/protocol/Types.hh"
788 #include "mem/ruby/system/System.hh"
790 #define HASH_FUN(state, event) ((int(state)*${ident}_Event_NUM)+int(event))
792 #define GET_TRANSITION_COMMENT() (${ident}_transitionComment.str())
793 #define CLEAR_TRANSITION_COMMENT() (${ident}_transitionComment.str(""))
796 ${ident}_Controller::doTransition(${ident}_Event event,
797 ${ident}_State state,
800 ${ident}_State next_state = state;
802 DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
803 DEBUG_MSG(GENERATED_COMP, MedPrio, *this);
804 DEBUG_EXPR(GENERATED_COMP, MedPrio, g_eventQueue_ptr->getTime());
805 DEBUG_EXPR(GENERATED_COMP, MedPrio,state);
806 DEBUG_EXPR(GENERATED_COMP, MedPrio,event);
807 DEBUG_EXPR(GENERATED_COMP, MedPrio,addr);
809 TransitionResult result =
810 doTransitionWorker(event, state, next_state, addr);
812 if (result == TransitionResult_Valid) {
813 DEBUG_EXPR(GENERATED_COMP, MedPrio, next_state);
814 DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
815 m_profiler.countTransition(state, event);
816 if (Debug::getProtocolTrace()) {
817 g_system_ptr->getProfiler()->profileTransition("${ident}",
819 ${ident}_State_to_string(state),
820 ${ident}_Event_to_string(event),
821 ${ident}_State_to_string(next_state),
822 GET_TRANSITION_COMMENT());
824 CLEAR_TRANSITION_COMMENT();
825 ${ident}_setState(addr, next_state);
827 } else if (result == TransitionResult_ResourceStall) {
828 if (Debug::getProtocolTrace()) {
829 g_system_ptr->getProfiler()->profileTransition("${ident}",
831 ${ident}_State_to_string(state),
832 ${ident}_Event_to_string(event),
833 ${ident}_State_to_string(next_state),
836 } else if (result == TransitionResult_ProtocolStall) {
837 DEBUG_MSG(GENERATED_COMP, HighPrio, "stalling");
838 DEBUG_NEWLINE(GENERATED_COMP, MedPrio);
839 if (Debug::getProtocolTrace()) {
840 g_system_ptr->getProfiler()->profileTransition("${ident}",
842 ${ident}_State_to_string(state),
843 ${ident}_Event_to_string(event),
844 ${ident}_State_to_string(next_state),
853 ${ident}_Controller::doTransitionWorker(${ident}_Event event,
854 ${ident}_State state,
855 ${ident}_State& next_state,
858 switch(HASH_FUN(state, event)) {
861 # This map will allow suppress generating duplicate code
864 for trans
in self
.transitions
:
865 case_string
= "%s_State_%s, %s_Event_%s" % \
866 (self
.ident
, trans
.state
.ident
, self
.ident
, trans
.event
.ident
)
868 case
= self
.symtab
.codeFormatter()
869 # Only set next_state if it changes
870 if trans
.state
!= trans
.nextState
:
871 ns_ident
= trans
.nextState
.ident
872 case('next_state = ${ident}_State_${ns_ident};')
874 actions
= trans
.actions
876 # Check for resources
878 res
= trans
.resources
879 for key
,val
in res
.iteritems():
880 if key
.type.ident
!= "DNUCAStopTable":
882 if (!%s.areNSlotsAvailable(%s))
883 return TransitionResult_ResourceStall;
884 ''' % (key
.code
, val
)
885 case_sorter
.append(val
)
888 # Emit the code sequences in a sorted order. This makes the
889 # output deterministic (without this the output order can vary
890 # since Map's keys() on a vector of pointers is not deterministic
891 for c
in sorted(case_sorter
):
894 # Figure out if we stall
896 for action
in actions
:
897 if action
.ident
== "z_stall":
902 case('return TransitionResult_ProtocolStall;')
904 for action
in actions
:
905 case('${{action.ident}}(addr);')
906 case('return TransitionResult_Valid;')
910 # Look to see if this transition code is unique.
911 if case
not in cases
:
914 cases
[case
].append(case_string
)
916 # Walk through all of the unique code blocks and spit out the
917 # corresponding case statement elements
918 for case
,transitions
in cases
.iteritems():
919 # Iterative over all the multiple transitions that share
921 for trans
in transitions
:
922 code(' case HASH_FUN($trans):')
927 WARN_EXPR(m_version);
928 WARN_EXPR(g_eventQueue_ptr->getTime());
932 ERROR_MSG(\"Invalid transition\");
934 return TransitionResult_Valid;
937 code
.write(path
, "%s_Transitions.cc" % self
.ident
)
939 def printProfileDumperHH(self
, path
):
940 code
= self
.symtab
.codeFormatter()
944 // Auto generated C++ code started by $__file__:$__line__
945 // ${ident}: ${{self.short}}
947 #ifndef __${ident}_PROFILE_DUMPER_HH__
948 #define __${ident}_PROFILE_DUMPER_HH__
953 #include "${ident}_Profiler.hh"
954 #include "${ident}_Event.hh"
956 typedef std::vector<${ident}_Profiler *> ${ident}_profilers;
958 class ${ident}_ProfileDumper
961 ${ident}_ProfileDumper();
962 void registerProfiler(${ident}_Profiler* profiler);
963 void dumpStats(std::ostream& out) const;
966 ${ident}_profilers m_profilers;
969 #endif // __${ident}_PROFILE_DUMPER_HH__
971 code
.write(path
, "%s_ProfileDumper.hh" % self
.ident
)
973 def printProfileDumperCC(self
, path
):
974 code
= self
.symtab
.codeFormatter()
978 // Auto generated C++ code started by $__file__:$__line__
979 // ${ident}: ${{self.short}}
981 #include "mem/protocol/${ident}_ProfileDumper.hh"
983 ${ident}_ProfileDumper::${ident}_ProfileDumper()
988 ${ident}_ProfileDumper::registerProfiler(${ident}_Profiler* profiler)
990 m_profilers.push_back(profiler);
994 ${ident}_ProfileDumper::dumpStats(std::ostream& out) const
996 out << " --- ${ident} ---\\n";
997 out << " - Event Counts -\\n";
998 for (${ident}_Event event = ${ident}_Event_FIRST;
999 event < ${ident}_Event_NUM;
1001 out << (${ident}_Event) event << " [";
1003 for (int i = 0; i < m_profilers.size(); i++) {
1004 out << m_profilers[i]->getEventCount(event) << " ";
1005 total += m_profilers[i]->getEventCount(event);
1007 out << "] " << total << "\\n";
1010 out << " - Transitions -\\n";
1011 for (${ident}_State state = ${ident}_State_FIRST;
1012 state < ${ident}_State_NUM;
1014 for (${ident}_Event event = ${ident}_Event_FIRST;
1015 event < ${ident}_Event_NUM;
1017 if (m_profilers[0]->isPossible(state, event)) {
1018 out << (${ident}_State) state << " "
1019 << (${ident}_Event) event << " [";
1021 for (int i = 0; i < m_profilers.size(); i++) {
1022 out << m_profilers[i]->getTransitionCount(state, event) << " ";
1023 total += m_profilers[i]->getTransitionCount(state, event);
1025 out << "] " << total << "\\n";
1032 code
.write(path
, "%s_ProfileDumper.cc" % self
.ident
)
1034 def printProfilerHH(self
, path
):
1035 code
= self
.symtab
.codeFormatter()
1039 // Auto generated C++ code started by $__file__:$__line__
1040 // ${ident}: ${{self.short}}
1042 #ifndef __${ident}_PROFILER_HH__
1043 #define __${ident}_PROFILER_HH__
1047 #include "mem/ruby/common/Global.hh"
1048 #include "mem/protocol/${ident}_State.hh"
1049 #include "mem/protocol/${ident}_Event.hh"
1051 class ${ident}_Profiler
1054 ${ident}_Profiler();
1055 void setVersion(int version);
1056 void countTransition(${ident}_State state, ${ident}_Event event);
1057 void possibleTransition(${ident}_State state, ${ident}_Event event);
1058 uint64 getEventCount(${ident}_Event event);
1059 bool isPossible(${ident}_State state, ${ident}_Event event);
1060 uint64 getTransitionCount(${ident}_State state, ${ident}_Event event);
1064 int m_counters[${ident}_State_NUM][${ident}_Event_NUM];
1065 int m_event_counters[${ident}_Event_NUM];
1066 bool m_possible[${ident}_State_NUM][${ident}_Event_NUM];
1070 #endif // __${ident}_PROFILER_HH__
1072 code
.write(path
, "%s_Profiler.hh" % self
.ident
)
1074 def printProfilerCC(self
, path
):
1075 code
= self
.symtab
.codeFormatter()
1079 // Auto generated C++ code started by $__file__:$__line__
1080 // ${ident}: ${{self.short}}
1082 #include "mem/protocol/${ident}_Profiler.hh"
1084 ${ident}_Profiler::${ident}_Profiler()
1086 for (int state = 0; state < ${ident}_State_NUM; state++) {
1087 for (int event = 0; event < ${ident}_Event_NUM; event++) {
1088 m_possible[state][event] = false;
1089 m_counters[state][event] = 0;
1092 for (int event = 0; event < ${ident}_Event_NUM; event++) {
1093 m_event_counters[event] = 0;
1098 ${ident}_Profiler::setVersion(int version)
1100 m_version = version;
1104 ${ident}_Profiler::clearStats()
1106 for (int state = 0; state < ${ident}_State_NUM; state++) {
1107 for (int event = 0; event < ${ident}_Event_NUM; event++) {
1108 m_counters[state][event] = 0;
1112 for (int event = 0; event < ${ident}_Event_NUM; event++) {
1113 m_event_counters[event] = 0;
1117 ${ident}_Profiler::countTransition(${ident}_State state, ${ident}_Event event)
1119 assert(m_possible[state][event]);
1120 m_counters[state][event]++;
1121 m_event_counters[event]++;
1124 ${ident}_Profiler::possibleTransition(${ident}_State state,
1125 ${ident}_Event event)
1127 m_possible[state][event] = true;
1131 ${ident}_Profiler::getEventCount(${ident}_Event event)
1133 return m_event_counters[event];
1137 ${ident}_Profiler::isPossible(${ident}_State state, ${ident}_Event event)
1139 return m_possible[state][event];
1143 ${ident}_Profiler::getTransitionCount(${ident}_State state,
1144 ${ident}_Event event)
1146 return m_counters[state][event];
1150 code
.write(path
, "%s_Profiler.cc" % self
.ident
)
1152 # **************************
1153 # ******* HTML Files *******
1154 # **************************
1155 def frameRef(self
, click_href
, click_target
, over_href
, over_num
, text
):
1156 code
= self
.symtab
.codeFormatter(fix_newlines
=False)
1157 code("""<A href=\"$click_href\" target=\"$click_target\" onmouseover=\"
1158 if (parent.frames[$over_num].location != parent.location + '$over_href') {
1159 parent.frames[$over_num].location='$over_href'
1161 ${{html.formatShorthand(text)}}
1165 def writeHTMLFiles(self
, path
):
1166 # Create table with no row hilighted
1167 self
.printHTMLTransitions(path
, None)
1169 # Generate transition tables
1170 for state
in self
.states
.itervalues():
1171 self
.printHTMLTransitions(path
, state
)
1173 # Generate action descriptions
1174 for action
in self
.actions
.itervalues():
1175 name
= "%s_action_%s.html" % (self
.ident
, action
.ident
)
1176 code
= html
.createSymbol(action
, "Action")
1177 code
.write(path
, name
)
1179 # Generate state descriptions
1180 for state
in self
.states
.itervalues():
1181 name
= "%s_State_%s.html" % (self
.ident
, state
.ident
)
1182 code
= html
.createSymbol(state
, "State")
1183 code
.write(path
, name
)
1185 # Generate event descriptions
1186 for event
in self
.events
.itervalues():
1187 name
= "%s_Event_%s.html" % (self
.ident
, event
.ident
)
1188 code
= html
.createSymbol(event
, "Event")
1189 code
.write(path
, name
)
1191 def printHTMLTransitions(self
, path
, active_state
):
1192 code
= self
.symtab
.codeFormatter()
1196 <BODY link="blue" vlink="blue">
1198 <H1 align="center">${{html.formatShorthand(self.short)}}:
1201 for i
,machine
in enumerate(self
.symtab
.getAllType(StateMachine
)):
1210 code('$extra<A target="Table" href="${mid}_table.html">$mid</A>')
1221 for event
in self
.events
.itervalues():
1222 href
= "%s_Event_%s.html" % (self
.ident
, event
.ident
)
1223 ref
= self
.frameRef(href
, "Status", href
, "1", event
.short
)
1224 code('<TH bgcolor=white>$ref</TH>')
1228 for state
in self
.states
.itervalues():
1230 if state
== active_state
:
1235 click
= "%s_table_%s.html" % (self
.ident
, state
.ident
)
1236 over
= "%s_State_%s.html" % (self
.ident
, state
.ident
)
1237 text
= html
.formatShorthand(state
.short
)
1238 ref
= self
.frameRef(click
, "Table", over
, "1", state
.short
)
1241 <TH bgcolor=$color>$ref</TH>
1244 # -- One column for each event
1245 for event
in self
.events
.itervalues():
1246 trans
= self
.table
.get((state
,event
), None)
1248 # This is the no transition case
1249 if state
== active_state
:
1254 code('<TD bgcolor=$color> </TD>')
1257 next
= trans
.nextState
1258 stall_action
= False
1260 # -- Get the actions
1261 for action
in trans
.actions
:
1262 if action
.ident
== "z_stall" or \
1263 action
.ident
== "zz_recycleMandatoryQueue":
1266 # -- Print out "actions/next-state"
1268 if state
== active_state
:
1273 elif active_state
and next
.ident
== active_state
.ident
:
1275 elif state
== active_state
:
1280 code('<TD bgcolor=$color>')
1281 for action
in trans
.actions
:
1282 href
= "%s_action_%s.html" % (self
.ident
, action
.ident
)
1283 ref
= self
.frameRef(href
, "Status", href
, "1",
1289 click
= "%s_table_%s.html" % (self
.ident
, next
.ident
)
1290 over
= "%s_State_%s.html" % (self
.ident
, next
.ident
)
1291 ref
= self
.frameRef(click
, "Table", over
, "1", next
.short
)
1296 if state
== active_state
:
1301 click
= "%s_table_%s.html" % (self
.ident
, state
.ident
)
1302 over
= "%s_State_%s.html" % (self
.ident
, state
.ident
)
1303 ref
= self
.frameRef(click
, "Table", over
, "1", state
.short
)
1305 <TH bgcolor=$color>$ref</TH>
1314 for event
in self
.events
.itervalues():
1315 href
= "%s_Event_%s.html" % (self
.ident
, event
.ident
)
1316 ref
= self
.frameRef(href
, "Status", href
, "1", event
.short
)
1317 code('<TH bgcolor=white>$ref</TH>')
1326 name
= "%s_table_%s.html" % (self
.ident
, active_state
.ident
)
1328 name
= "%s_table.html" % self
.ident
1329 code
.write(path
, name
)
1331 __all__
= [ "StateMachine" ]