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 stallBuffer(MessageBuffer* buf, Address addr);
242 void wakeUpBuffers(Address addr);
243 void initNetworkPtr(Network* net_ptr) { m_net_ptr = net_ptr; }
244 void print(std::ostream& out) const;
245 void printConfig(std::ostream& out) const;
247 void printStats(std::ostream& out) const;
249 void blockOnQueue(Address addr, MessageBuffer* port);
250 void unblock(Address addr);
257 for param
in self
.config_parameters
:
259 code('${{param.type_ast.type}}* m_${{param.ident}}_ptr;')
261 code('${{param.type_ast.type}} m_${{param.ident}};')
264 int m_number_of_TBEs;
266 TransitionResult doTransition(${ident}_Event event,
267 ${ident}_State state,
268 const Address& addr);
270 TransitionResult doTransitionWorker(${ident}_Event event,
271 ${ident}_State state,
272 ${ident}_State& next_state,
273 const Address& addr);
276 int m_transitions_per_cycle;
278 int m_recycle_latency;
279 std::map<std::string, std::string> m_cfg;
282 MachineID m_machineID;
284 std::map<Address, MessageBuffer*> m_block_map;
285 typedef std::vector<MessageBuffer*> MsgVecType;
286 typedef m5::hash_map< Address, MsgVecType* > WaitingBufType;
287 WaitingBufType m_waiting_buffers;
288 int m_max_in_port_rank;
289 int m_cur_in_port_rank;
290 static ${ident}_ProfileDumper s_profileDumper;
291 ${ident}_Profiler m_profiler;
292 static int m_num_controllers;
294 // Internal functions
297 for func
in self
.functions
:
298 proto
= func
.prototype
306 for action
in self
.actions
.itervalues():
307 code('/** \\brief ${{action.desc}} */')
308 code('void ${{action.ident}}(const Address& addr);')
310 # the controller internal variables
315 for var
in self
.objects
:
316 th
= var
.get("template_hack", "")
317 code('${{var.type.c_ident}}$th* m_${{var.c_ident}}_ptr;')
319 if var
.type.ident
== "MessageBuffer":
320 self
.message_buffer_names
.append("m_%s_ptr" % var
.c_ident
)
324 code('#endif // __${ident}_CONTROLLER_H__')
325 code
.write(path
, '%s.hh' % c_ident
)
327 def printControllerCC(self
, path
):
328 '''Output the actions for performing the actions'''
330 code
= self
.symtab
.codeFormatter()
332 c_ident
= "%s_Controller" % self
.ident
335 /** \\file $c_ident.cc
337 * Auto generated C++ code started by $__file__:$__line__
338 * Created by slicc definition of Module "${{self.short}}"
345 #include "base/cprintf.hh"
346 #include "mem/protocol/${ident}_Controller.hh"
347 #include "mem/protocol/${ident}_State.hh"
348 #include "mem/protocol/${ident}_Event.hh"
349 #include "mem/protocol/Types.hh"
350 #include "mem/ruby/common/Global.hh"
351 #include "mem/ruby/slicc_interface/RubySlicc_includes.hh"
352 #include "mem/ruby/system/System.hh"
357 # include object classes
359 for var
in self
.objects
:
360 if var
.type.ident
not in seen_types
and not var
.type.isPrimitive
:
361 code('#include "mem/protocol/${{var.type.c_ident}}.hh"')
362 seen_types
.add(var
.type.ident
)
366 ${c_ident}Params::create()
368 return new $c_ident(this);
371 int $c_ident::m_num_controllers = 0;
372 ${ident}_ProfileDumper $c_ident::s_profileDumper;
374 // for adding information to the protocol debug trace
375 stringstream ${ident}_transitionComment;
376 #define APPEND_TRANSITION_COMMENT(str) (${ident}_transitionComment << str)
378 /** \\brief constructor */
379 $c_ident::$c_ident(const Params *p)
380 : AbstractController(p)
382 m_version = p->version;
383 m_transitions_per_cycle = p->transitions_per_cycle;
384 m_buffer_size = p->buffer_size;
385 m_recycle_latency = p->recycle_latency;
386 m_number_of_TBEs = p->number_of_TBEs;
387 m_is_blocking = false;
390 # max_port_rank is used to size vectors and thus should be one plus the
393 max_port_rank
= self
.in_ports
[0].pairs
["max_port_rank"] + 1
394 code(' m_max_in_port_rank = $max_port_rank;')
398 # After initializing the universal machine parameters, initialize the
399 # this machines config parameters. Also detemine if these configuration
400 # params include a sequencer. This information will be used later for
401 # contecting the sequencer back to the L1 cache controller.
403 contains_sequencer
= False
404 for param
in self
.config_parameters
:
405 if param
.name
== "sequencer" or param
.name
== "dma_sequencer":
406 contains_sequencer
= True
408 code('m_${{param.name}}_ptr = p->${{param.name}};')
410 code('m_${{param.name}} = p->${{param.name}};')
413 # For the l1 cache controller, add the special atomic support which
414 # includes passing the sequencer a pointer to the controller.
416 if self
.ident
== "L1Cache":
417 if not contains_sequencer
:
418 self
.error("The L1Cache controller must include the sequencer " \
419 "configuration parameter")
422 m_sequencer_ptr->setController(this);
425 # For the DMA controller, pass the sequencer a pointer to the
428 if self
.ident
== "DMA":
429 if not contains_sequencer
:
430 self
.error("The DMA controller must include the sequencer " \
431 "configuration parameter")
434 m_dma_sequencer_ptr->setController(this);
437 code('m_num_controllers++;')
438 for var
in self
.objects
:
439 if var
.ident
.find("mandatoryQueue") >= 0:
440 code('m_${{var.c_ident}}_ptr = new ${{var.type.c_ident}}();')
449 MachineType machine_type;
452 m_machineID.type = MachineType_${ident};
453 m_machineID.num = m_version;
455 // initialize objects
456 m_profiler.setVersion(m_version);
457 s_profileDumper.registerProfiler(&m_profiler);
462 for var
in self
.objects
:
464 vid
= "m_%s_ptr" % var
.c_ident
465 if "network" not in var
:
466 # Not a network port object
467 if "primitive" in vtype
:
468 code('$vid = new ${{vtype.c_ident}};')
470 code('(*$vid) = ${{var["default"]}};')
475 code('$vid = ${{var["factory"]}};')
476 elif var
.ident
.find("mandatoryQueue") < 0:
477 th
= var
.get("template_hack", "")
478 expr
= "%s = new %s%s" % (vid
, vtype
.c_ident
, th
)
481 if "non_obj" not in vtype
and not vtype
.isEnumeration
:
482 if expr
.find("TBETable") >= 0:
483 args
= "m_number_of_TBEs"
485 args
= var
.get("constructor_hack", "")
487 code('$expr($args);')
489 code('assert($vid != NULL);')
492 code('*$vid = ${{var["default"]}}; // Object default')
493 elif "default" in vtype
:
494 comment
= "Type %s default" % vtype
.ident
495 code('*$vid = ${{vtype["default"]}}; // $comment')
498 if "ordered" in var
and "trigger_queue" not in var
:
500 code('$vid->setOrdering(${{var["ordered"]}});')
505 code('$vid->setRandomization(${{var["random"]}});')
508 if vtype
.isBuffer
and \
509 "rank" in var
and "trigger_queue" not in var
:
510 code('$vid->setPriority(${{var["rank"]}});')
513 # Network port object
514 network
= var
["network"]
515 ordered
= var
["ordered"]
516 vnet
= var
["virtual_network"]
518 assert var
.machine
is not None
520 machine_type = string_to_MachineType("${{var.machine.ident}}");
521 base = MachineType_base_number(machine_type);
522 $vid = m_net_ptr->get${network}NetQueue(m_version + base, $ordered, $vnet);
525 code('assert($vid != NULL);')
530 code('$vid->setOrdering(${{var["ordered"]}});')
535 code('$vid->setRandomization(${{var["random"]}})')
539 code('$vid->setPriority(${{var["rank"]}})')
544 if (m_buffer_size > 0) {
545 $vid->resize(m_buffer_size);
549 # set description (may be overriden later by port def)
551 $vid->setDescription("[Version " + to_string(m_version) + ", ${ident}, name=${{var.c_ident}}]");
556 if "recycle_latency" in var
:
557 code('$vid->setRecycleLatency(${{var["recycle_latency"]}});')
559 code('$vid->setRecycleLatency(m_recycle_latency);')
562 # Set the queue consumers
564 for port
in self
.in_ports
:
565 code('${{port.code}}.setConsumer(this);')
567 # Set the queue descriptions
569 for port
in self
.in_ports
:
570 code('${{port.code}}.setDescription("[Version " + to_string(m_version) + ", $ident, $port]");')
572 # Initialize the transition profiling
574 for trans
in self
.transitions
:
575 # Figure out if we stall
577 for action
in trans
.actions
:
578 if action
.ident
== "z_stall":
581 # Only possible if it is not a 'z' case
583 state
= "%s_State_%s" % (self
.ident
, trans
.state
.ident
)
584 event
= "%s_Event_%s" % (self
.ident
, trans
.event
.ident
)
585 code('m_profiler.possibleTransition($state, $event);')
590 has_mandatory_q
= False
591 for port
in self
.in_ports
:
592 if port
.code
.find("mandatoryQueue_ptr") >= 0:
593 has_mandatory_q
= True
596 mq_ident
= "m_%s_mandatoryQueue_ptr" % self
.ident
602 $c_ident::getNumControllers()
604 return m_num_controllers;
608 $c_ident::getMandatoryQueue() const
614 $c_ident::getVersion() const
620 $c_ident::toString() const
626 $c_ident::getName() const
632 $c_ident::getMachineType() const
634 return MachineType_${ident};
638 $c_ident::stallBuffer(MessageBuffer* buf, Address addr)
640 if (m_waiting_buffers.count(addr) == 0) {
641 MsgVecType* msgVec = new MsgVecType;
642 msgVec->resize(m_max_in_port_rank, NULL);
643 m_waiting_buffers[addr] = msgVec;
645 (*(m_waiting_buffers[addr]))[m_cur_in_port_rank] = buf;
649 $c_ident::wakeUpBuffers(Address addr)
652 // Wake up all possible lower rank (i.e. lower priority) buffers that could
653 // be waiting on this message.
655 for (int in_port_rank = m_cur_in_port_rank - 1;
658 if ((*(m_waiting_buffers[addr]))[in_port_rank] != NULL) {
659 (*(m_waiting_buffers[addr]))[in_port_rank]->reanalyzeMessages(addr);
662 delete m_waiting_buffers[addr];
663 m_waiting_buffers.erase(addr);
667 $c_ident::blockOnQueue(Address addr, MessageBuffer* port)
669 m_is_blocking = true;
670 m_block_map[addr] = port;
674 $c_ident::unblock(Address addr)
676 m_block_map.erase(addr);
677 if (m_block_map.size() == 0) {
678 m_is_blocking = false;
683 $c_ident::print(ostream& out) const
685 out << "[$c_ident " << m_version << "]";
689 $c_ident::printConfig(ostream& out) const
691 out << "$c_ident config: " << m_name << endl;
692 out << " version: " << m_version << endl;
693 map<string, string>::const_iterator it;
694 for (it = m_cfg.begin(); it != m_cfg.end(); it++)
695 out << " " << it->first << ": " << it->second << endl;
699 $c_ident::printStats(ostream& out) const
703 # Cache and Memory Controllers have specific profilers associated with
704 # them. Print out these stats before dumping state transition stats.
706 for param
in self
.config_parameters
:
707 if param
.type_ast
.type.ident
== "CacheMemory" or \
708 param
.type_ast
.type.ident
== "DirectoryMemory" or \
709 param
.type_ast
.type.ident
== "MemoryControl":
710 assert(param
.pointer
)
711 code(' m_${{param.ident}}_ptr->printStats(out);')
714 if (m_version == 0) {
715 s_profileDumper.dumpStats(out);
719 void $c_ident::clearStats() {
722 # Cache and Memory Controllers have specific profilers associated with
723 # them. These stats must be cleared too.
725 for param
in self
.config_parameters
:
726 if param
.type_ast
.type.ident
== "CacheMemory" or \
727 param
.type_ast
.type.ident
== "MemoryControl":
728 assert(param
.pointer
)
729 code(' m_${{param.ident}}_ptr->clearStats();')
732 m_profiler.clearStats();
738 for action
in self
.actions
.itervalues():
739 if "c_code" not in action
:
743 /** \\brief ${{action.desc}} */
745 $c_ident::${{action.ident}}(const Address& addr)
747 DPRINTF(RubyGenerated, "executing\\n");
748 ${{action["c_code"]}}
752 code
.write(path
, "%s.cc" % c_ident
)
754 def printCWakeup(self
, path
):
755 '''Output the wakeup loop for the events'''
757 code
= self
.symtab
.codeFormatter()
761 // Auto generated C++ code started by $__file__:$__line__
762 // ${ident}: ${{self.short}}
766 #include "base/misc.hh"
767 #include "mem/ruby/common/Global.hh"
768 #include "mem/ruby/slicc_interface/RubySlicc_includes.hh"
769 #include "mem/protocol/${ident}_Controller.hh"
770 #include "mem/protocol/${ident}_State.hh"
771 #include "mem/protocol/${ident}_Event.hh"
772 #include "mem/protocol/Types.hh"
773 #include "mem/ruby/system/System.hh"
778 ${ident}_Controller::wakeup()
780 // DEBUG_EXPR(GENERATED_COMP, MedPrio, *this);
781 // DEBUG_EXPR(GENERATED_COMP, MedPrio, g_eventQueue_ptr->getTime());
785 // Some cases will put us into an infinite loop without this limit
786 assert(counter <= m_transitions_per_cycle);
787 if (counter == m_transitions_per_cycle) {
788 // Count how often we are fully utilized
789 g_system_ptr->getProfiler()->controllerBusy(m_machineID);
791 // Wakeup in another cycle and try again
792 g_eventQueue_ptr->scheduleEvent(this, 1);
802 for port
in self
.in_ports
:
804 code('// ${ident}InPort $port')
805 if port
.pairs
.has_key("rank"):
806 code('m_cur_in_port_rank = ${{port.pairs["rank"]}};')
808 code('m_cur_in_port_rank = 0;')
809 code('${{port["c_code_in_port"]}}')
817 break; // If we got this far, we have nothing left todo
819 // g_eventQueue_ptr->scheduleEvent(this, 1);
823 code
.write(path
, "%s_Wakeup.cc" % self
.ident
)
825 def printCSwitch(self
, path
):
826 '''Output switch statement for transition table'''
828 code
= self
.symtab
.codeFormatter()
832 // Auto generated C++ code started by $__file__:$__line__
833 // ${ident}: ${{self.short}}
837 #include "base/misc.hh"
838 #include "base/trace.hh"
839 #include "mem/ruby/common/Global.hh"
840 #include "mem/protocol/${ident}_Controller.hh"
841 #include "mem/protocol/${ident}_State.hh"
842 #include "mem/protocol/${ident}_Event.hh"
843 #include "mem/protocol/Types.hh"
844 #include "mem/ruby/system/System.hh"
846 #define HASH_FUN(state, event) ((int(state)*${ident}_Event_NUM)+int(event))
848 #define GET_TRANSITION_COMMENT() (${ident}_transitionComment.str())
849 #define CLEAR_TRANSITION_COMMENT() (${ident}_transitionComment.str(""))
852 ${ident}_Controller::doTransition(${ident}_Event event,
853 ${ident}_State state,
856 ${ident}_State next_state = state;
858 DPRINTF(RubyGenerated, "%s, Time: %lld, state: %s, event: %s, addr: %s\\n",
860 g_eventQueue_ptr->getTime(),
861 ${ident}_State_to_string(state),
862 ${ident}_Event_to_string(event),
865 TransitionResult result =
866 doTransitionWorker(event, state, next_state, addr);
868 if (result == TransitionResult_Valid) {
869 DPRINTF(RubyGenerated, "next_state: %s\\n",
870 ${ident}_State_to_string(next_state));
871 m_profiler.countTransition(state, event);
872 DPRINTFR(ProtocolTrace, "%7d %3s %10s%20s %6s>%-6s %s %s\\n",
873 g_eventQueue_ptr->getTime(), m_version, "${ident}",
874 ${ident}_Event_to_string(event),
875 ${ident}_State_to_string(state),
876 ${ident}_State_to_string(next_state),
877 addr, GET_TRANSITION_COMMENT());
879 CLEAR_TRANSITION_COMMENT();
880 ${ident}_setState(addr, next_state);
881 } else if (result == TransitionResult_ResourceStall) {
882 DPRINTFR(ProtocolTrace, "%7s %3s %10s%20s %6s>%-6s %s %s\\n",
883 g_eventQueue_ptr->getTime(), m_version, "${ident}",
884 ${ident}_Event_to_string(event),
885 ${ident}_State_to_string(state),
886 ${ident}_State_to_string(next_state),
887 addr, "Resource Stall");
888 } else if (result == TransitionResult_ProtocolStall) {
889 DPRINTF(RubyGenerated, "stalling\\n");
890 DPRINTFR(ProtocolTrace, "%7s %3s %10s%20s %6s>%-6s %s %s\\n",
891 g_eventQueue_ptr->getTime(), m_version, "${ident}",
892 ${ident}_Event_to_string(event),
893 ${ident}_State_to_string(state),
894 ${ident}_State_to_string(next_state),
895 addr, "Protocol Stall");
902 ${ident}_Controller::doTransitionWorker(${ident}_Event event,
903 ${ident}_State state,
904 ${ident}_State& next_state,
907 switch(HASH_FUN(state, event)) {
910 # This map will allow suppress generating duplicate code
913 for trans
in self
.transitions
:
914 case_string
= "%s_State_%s, %s_Event_%s" % \
915 (self
.ident
, trans
.state
.ident
, self
.ident
, trans
.event
.ident
)
917 case
= self
.symtab
.codeFormatter()
918 # Only set next_state if it changes
919 if trans
.state
!= trans
.nextState
:
920 ns_ident
= trans
.nextState
.ident
921 case('next_state = ${ident}_State_${ns_ident};')
923 actions
= trans
.actions
925 # Check for resources
927 res
= trans
.resources
928 for key
,val
in res
.iteritems():
929 if key
.type.ident
!= "DNUCAStopTable":
931 if (!%s.areNSlotsAvailable(%s))
932 return TransitionResult_ResourceStall;
933 ''' % (key
.code
, val
)
934 case_sorter
.append(val
)
937 # Emit the code sequences in a sorted order. This makes the
938 # output deterministic (without this the output order can vary
939 # since Map's keys() on a vector of pointers is not deterministic
940 for c
in sorted(case_sorter
):
943 # Figure out if we stall
945 for action
in actions
:
946 if action
.ident
== "z_stall":
951 case('return TransitionResult_ProtocolStall;')
953 for action
in actions
:
954 case('${{action.ident}}(addr);')
955 case('return TransitionResult_Valid;')
959 # Look to see if this transition code is unique.
960 if case
not in cases
:
963 cases
[case
].append(case_string
)
965 # Walk through all of the unique code blocks and spit out the
966 # corresponding case statement elements
967 for case
,transitions
in cases
.iteritems():
968 # Iterative over all the multiple transitions that share
970 for trans
in transitions
:
971 code(' case HASH_FUN($trans):')
976 fatal("Invalid transition\\n"
977 "version: %d time: %d addr: %s event: %s state: %s\\n",
978 m_version, g_eventQueue_ptr->getTime(), addr, event, state);
980 return TransitionResult_Valid;
983 code
.write(path
, "%s_Transitions.cc" % self
.ident
)
985 def printProfileDumperHH(self
, path
):
986 code
= self
.symtab
.codeFormatter()
990 // Auto generated C++ code started by $__file__:$__line__
991 // ${ident}: ${{self.short}}
993 #ifndef __${ident}_PROFILE_DUMPER_HH__
994 #define __${ident}_PROFILE_DUMPER_HH__
1000 #include "${ident}_Profiler.hh"
1001 #include "${ident}_Event.hh"
1003 typedef std::vector<${ident}_Profiler *> ${ident}_profilers;
1005 class ${ident}_ProfileDumper
1008 ${ident}_ProfileDumper();
1009 void registerProfiler(${ident}_Profiler* profiler);
1010 void dumpStats(std::ostream& out) const;
1013 ${ident}_profilers m_profilers;
1016 #endif // __${ident}_PROFILE_DUMPER_HH__
1018 code
.write(path
, "%s_ProfileDumper.hh" % self
.ident
)
1020 def printProfileDumperCC(self
, path
):
1021 code
= self
.symtab
.codeFormatter()
1025 // Auto generated C++ code started by $__file__:$__line__
1026 // ${ident}: ${{self.short}}
1028 #include "mem/protocol/${ident}_ProfileDumper.hh"
1030 ${ident}_ProfileDumper::${ident}_ProfileDumper()
1035 ${ident}_ProfileDumper::registerProfiler(${ident}_Profiler* profiler)
1037 m_profilers.push_back(profiler);
1041 ${ident}_ProfileDumper::dumpStats(std::ostream& out) const
1043 out << " --- ${ident} ---\\n";
1044 out << " - Event Counts -\\n";
1045 for (${ident}_Event event = ${ident}_Event_FIRST;
1046 event < ${ident}_Event_NUM;
1048 out << (${ident}_Event) event << " [";
1050 for (int i = 0; i < m_profilers.size(); i++) {
1051 out << m_profilers[i]->getEventCount(event) << " ";
1052 total += m_profilers[i]->getEventCount(event);
1054 out << "] " << total << "\\n";
1057 out << " - Transitions -\\n";
1058 for (${ident}_State state = ${ident}_State_FIRST;
1059 state < ${ident}_State_NUM;
1061 for (${ident}_Event event = ${ident}_Event_FIRST;
1062 event < ${ident}_Event_NUM;
1064 if (m_profilers[0]->isPossible(state, event)) {
1065 out << (${ident}_State) state << " "
1066 << (${ident}_Event) event << " [";
1068 for (int i = 0; i < m_profilers.size(); i++) {
1069 out << m_profilers[i]->getTransitionCount(state, event) << " ";
1070 total += m_profilers[i]->getTransitionCount(state, event);
1072 out << "] " << total << "\\n";
1079 code
.write(path
, "%s_ProfileDumper.cc" % self
.ident
)
1081 def printProfilerHH(self
, path
):
1082 code
= self
.symtab
.codeFormatter()
1086 // Auto generated C++ code started by $__file__:$__line__
1087 // ${ident}: ${{self.short}}
1089 #ifndef __${ident}_PROFILER_HH__
1090 #define __${ident}_PROFILER_HH__
1095 #include "mem/ruby/common/Global.hh"
1096 #include "mem/protocol/${ident}_State.hh"
1097 #include "mem/protocol/${ident}_Event.hh"
1099 class ${ident}_Profiler
1102 ${ident}_Profiler();
1103 void setVersion(int version);
1104 void countTransition(${ident}_State state, ${ident}_Event event);
1105 void possibleTransition(${ident}_State state, ${ident}_Event event);
1106 uint64 getEventCount(${ident}_Event event);
1107 bool isPossible(${ident}_State state, ${ident}_Event event);
1108 uint64 getTransitionCount(${ident}_State state, ${ident}_Event event);
1112 int m_counters[${ident}_State_NUM][${ident}_Event_NUM];
1113 int m_event_counters[${ident}_Event_NUM];
1114 bool m_possible[${ident}_State_NUM][${ident}_Event_NUM];
1118 #endif // __${ident}_PROFILER_HH__
1120 code
.write(path
, "%s_Profiler.hh" % self
.ident
)
1122 def printProfilerCC(self
, path
):
1123 code
= self
.symtab
.codeFormatter()
1127 // Auto generated C++ code started by $__file__:$__line__
1128 // ${ident}: ${{self.short}}
1132 #include "mem/protocol/${ident}_Profiler.hh"
1134 ${ident}_Profiler::${ident}_Profiler()
1136 for (int state = 0; state < ${ident}_State_NUM; state++) {
1137 for (int event = 0; event < ${ident}_Event_NUM; event++) {
1138 m_possible[state][event] = false;
1139 m_counters[state][event] = 0;
1142 for (int event = 0; event < ${ident}_Event_NUM; event++) {
1143 m_event_counters[event] = 0;
1148 ${ident}_Profiler::setVersion(int version)
1150 m_version = version;
1154 ${ident}_Profiler::clearStats()
1156 for (int state = 0; state < ${ident}_State_NUM; state++) {
1157 for (int event = 0; event < ${ident}_Event_NUM; event++) {
1158 m_counters[state][event] = 0;
1162 for (int event = 0; event < ${ident}_Event_NUM; event++) {
1163 m_event_counters[event] = 0;
1167 ${ident}_Profiler::countTransition(${ident}_State state, ${ident}_Event event)
1169 assert(m_possible[state][event]);
1170 m_counters[state][event]++;
1171 m_event_counters[event]++;
1174 ${ident}_Profiler::possibleTransition(${ident}_State state,
1175 ${ident}_Event event)
1177 m_possible[state][event] = true;
1181 ${ident}_Profiler::getEventCount(${ident}_Event event)
1183 return m_event_counters[event];
1187 ${ident}_Profiler::isPossible(${ident}_State state, ${ident}_Event event)
1189 return m_possible[state][event];
1193 ${ident}_Profiler::getTransitionCount(${ident}_State state,
1194 ${ident}_Event event)
1196 return m_counters[state][event];
1200 code
.write(path
, "%s_Profiler.cc" % self
.ident
)
1202 # **************************
1203 # ******* HTML Files *******
1204 # **************************
1205 def frameRef(self
, click_href
, click_target
, over_href
, over_num
, text
):
1206 code
= self
.symtab
.codeFormatter(fix_newlines
=False)
1207 code("""<A href=\"$click_href\" target=\"$click_target\" onmouseover=\"
1208 if (parent.frames[$over_num].location != parent.location + '$over_href') {
1209 parent.frames[$over_num].location='$over_href'
1211 ${{html.formatShorthand(text)}}
1215 def writeHTMLFiles(self
, path
):
1216 # Create table with no row hilighted
1217 self
.printHTMLTransitions(path
, None)
1219 # Generate transition tables
1220 for state
in self
.states
.itervalues():
1221 self
.printHTMLTransitions(path
, state
)
1223 # Generate action descriptions
1224 for action
in self
.actions
.itervalues():
1225 name
= "%s_action_%s.html" % (self
.ident
, action
.ident
)
1226 code
= html
.createSymbol(action
, "Action")
1227 code
.write(path
, name
)
1229 # Generate state descriptions
1230 for state
in self
.states
.itervalues():
1231 name
= "%s_State_%s.html" % (self
.ident
, state
.ident
)
1232 code
= html
.createSymbol(state
, "State")
1233 code
.write(path
, name
)
1235 # Generate event descriptions
1236 for event
in self
.events
.itervalues():
1237 name
= "%s_Event_%s.html" % (self
.ident
, event
.ident
)
1238 code
= html
.createSymbol(event
, "Event")
1239 code
.write(path
, name
)
1241 def printHTMLTransitions(self
, path
, active_state
):
1242 code
= self
.symtab
.codeFormatter()
1246 <BODY link="blue" vlink="blue">
1248 <H1 align="center">${{html.formatShorthand(self.short)}}:
1251 for i
,machine
in enumerate(self
.symtab
.getAllType(StateMachine
)):
1260 code('$extra<A target="Table" href="${mid}_table.html">$mid</A>')
1271 for event
in self
.events
.itervalues():
1272 href
= "%s_Event_%s.html" % (self
.ident
, event
.ident
)
1273 ref
= self
.frameRef(href
, "Status", href
, "1", event
.short
)
1274 code('<TH bgcolor=white>$ref</TH>')
1278 for state
in self
.states
.itervalues():
1280 if state
== active_state
:
1285 click
= "%s_table_%s.html" % (self
.ident
, state
.ident
)
1286 over
= "%s_State_%s.html" % (self
.ident
, state
.ident
)
1287 text
= html
.formatShorthand(state
.short
)
1288 ref
= self
.frameRef(click
, "Table", over
, "1", state
.short
)
1291 <TH bgcolor=$color>$ref</TH>
1294 # -- One column for each event
1295 for event
in self
.events
.itervalues():
1296 trans
= self
.table
.get((state
,event
), None)
1298 # This is the no transition case
1299 if state
== active_state
:
1304 code('<TD bgcolor=$color> </TD>')
1307 next
= trans
.nextState
1308 stall_action
= False
1310 # -- Get the actions
1311 for action
in trans
.actions
:
1312 if action
.ident
== "z_stall" or \
1313 action
.ident
== "zz_recycleMandatoryQueue":
1316 # -- Print out "actions/next-state"
1318 if state
== active_state
:
1323 elif active_state
and next
.ident
== active_state
.ident
:
1325 elif state
== active_state
:
1330 code('<TD bgcolor=$color>')
1331 for action
in trans
.actions
:
1332 href
= "%s_action_%s.html" % (self
.ident
, action
.ident
)
1333 ref
= self
.frameRef(href
, "Status", href
, "1",
1339 click
= "%s_table_%s.html" % (self
.ident
, next
.ident
)
1340 over
= "%s_State_%s.html" % (self
.ident
, next
.ident
)
1341 ref
= self
.frameRef(click
, "Table", over
, "1", next
.short
)
1346 if state
== active_state
:
1351 click
= "%s_table_%s.html" % (self
.ident
, state
.ident
)
1352 over
= "%s_State_%s.html" % (self
.ident
, state
.ident
)
1353 ref
= self
.frameRef(click
, "Table", over
, "1", state
.short
)
1355 <TH bgcolor=$color>$ref</TH>
1364 for event
in self
.events
.itervalues():
1365 href
= "%s_Event_%s.html" % (self
.ident
, event
.ident
)
1366 ref
= self
.frameRef(href
, "Status", href
, "1", event
.short
)
1367 code('<TH bgcolor=white>$ref</TH>')
1376 name
= "%s_table_%s.html" % (self
.ident
, active_state
.ident
)
1378 name
= "%s_table.html" % self
.ident
1379 code
.write(path
, name
)
1381 __all__
= [ "StateMachine" ]