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
35 python_class_map
= {"int": "Int",
36 "std::string": "String",
38 "CacheMemory": "RubyCache",
39 "WireBuffer": "RubyWireBuffer",
40 "Sequencer": "RubySequencer",
41 "DirectoryMemory": "RubyDirectoryMemory",
42 "MemoryControl": "MemoryControl",
43 "DMASequencer": "DMASequencer"
46 class StateMachine(Symbol
):
47 def __init__(self
, symtab
, ident
, location
, pairs
, config_parameters
):
48 super(StateMachine
, self
).__init
__(symtab
, ident
, location
, pairs
)
50 self
.config_parameters
= config_parameters
52 for param
in config_parameters
:
54 var
= Var(symtab
, param
.name
, location
, param
.type_ast
.type,
55 "(*m_%s_ptr)" % param
.name
, {}, self
)
57 var
= Var(symtab
, param
.name
, location
, param
.type_ast
.type,
58 "m_%s" % param
.name
, {}, self
)
59 self
.symtab
.registerSym(param
.name
, var
)
61 self
.states
= orderdict()
62 self
.events
= orderdict()
63 self
.actions
= orderdict()
64 self
.request_types
= orderdict()
72 self
.message_buffer_names
= []
75 return "[StateMachine: %s]" % self
.ident
77 def addState(self
, state
):
78 assert self
.table
is None
79 self
.states
[state
.ident
] = state
81 def addEvent(self
, event
):
82 assert self
.table
is None
83 self
.events
[event
.ident
] = event
85 def addAction(self
, action
):
86 assert self
.table
is None
88 # Check for duplicate action
89 for other
in self
.actions
.itervalues():
90 if action
.ident
== other
.ident
:
91 action
.warning("Duplicate action definition: %s" % action
.ident
)
92 action
.error("Duplicate action definition: %s" % action
.ident
)
93 if action
.short
== other
.short
:
94 other
.warning("Duplicate action shorthand: %s" % other
.ident
)
95 other
.warning(" shorthand = %s" % other
.short
)
96 action
.warning("Duplicate action shorthand: %s" % action
.ident
)
97 action
.error(" shorthand = %s" % action
.short
)
99 self
.actions
[action
.ident
] = action
101 def addRequestType(self
, request_type
):
102 assert self
.table
is None
103 self
.request_types
[request_type
.ident
] = request_type
105 def addTransition(self
, trans
):
106 assert self
.table
is None
107 self
.transitions
.append(trans
)
109 def addInPort(self
, var
):
110 self
.in_ports
.append(var
)
112 def addFunc(self
, func
):
113 # register func in the symbol table
114 self
.symtab
.registerSym(str(func
), func
)
115 self
.functions
.append(func
)
117 def addObject(self
, obj
):
118 self
.objects
.append(obj
)
120 def addType(self
, type):
121 type_ident
= '%s' % type.c_ident
123 if type_ident
== "%s_TBE" %self
.ident
:
124 if self
.TBEType
!= None:
125 self
.error("Multiple Transaction Buffer types in a " \
129 elif "interface" in type and "AbstractCacheEntry" == type["interface"]:
130 if self
.EntryType
!= None:
131 self
.error("Multiple AbstractCacheEntry types in a " \
133 self
.EntryType
= type
135 # Needs to be called before accessing the table
136 def buildTable(self
):
137 assert self
.table
is None
141 for trans
in self
.transitions
:
142 # Track which actions we touch so we know if we use them
143 # all -- really this should be done for all symbols as
144 # part of the symbol table, then only trigger it for
145 # Actions, States, Events, etc.
147 for action
in trans
.actions
:
150 index
= (trans
.state
, trans
.event
)
152 table
[index
].warning("Duplicate transition: %s" % table
[index
])
153 trans
.error("Duplicate transition: %s" % trans
)
156 # Look at all actions to make sure we used them all
157 for action
in self
.actions
.itervalues():
159 error_msg
= "Unused action: %s" % action
.ident
161 error_msg
+= ", " + action
.desc
162 action
.warning(error_msg
)
165 def writeCodeFiles(self
, path
, includes
):
166 self
.printControllerPython(path
)
167 self
.printControllerHH(path
)
168 self
.printControllerCC(path
, includes
)
169 self
.printCSwitch(path
)
170 self
.printCWakeup(path
, includes
)
171 self
.printProfilerCC(path
)
172 self
.printProfilerHH(path
)
173 self
.printProfileDumperCC(path
)
174 self
.printProfileDumperHH(path
)
176 def printControllerPython(self
, path
):
177 code
= self
.symtab
.codeFormatter()
179 py_ident
= "%s_Controller" % ident
180 c_ident
= "%s_Controller" % self
.ident
182 from m5.params import *
183 from m5.SimObject import SimObject
184 from Controller import RubyController
186 class $py_ident(RubyController):
188 cxx_header = 'mem/protocol/${c_ident}.hh'
191 for param
in self
.config_parameters
:
193 if param
.default
is not None:
194 dflt_str
= str(param
.default
) + ', '
195 if python_class_map
.has_key(param
.type_ast
.type.c_ident
):
196 python_type
= python_class_map
[param
.type_ast
.type.c_ident
]
197 code('${{param.name}} = Param.${{python_type}}(${dflt_str}"")')
199 self
.error("Unknown c++ to python class conversion for c++ " \
200 "type: '%s'. Please update the python_class_map " \
201 "in StateMachine.py", param
.type_ast
.type.c_ident
)
203 code
.write(path
, '%s.py' % py_ident
)
206 def printControllerHH(self
, path
):
207 '''Output the method declarations for the class declaration'''
208 code
= self
.symtab
.codeFormatter()
210 c_ident
= "%s_Controller" % self
.ident
212 self
.message_buffer_names
= []
215 /** \\file $c_ident.hh
217 * Auto generated C++ code started by $__file__:$__line__
218 * Created by slicc definition of Module "${{self.short}}"
221 #ifndef __${ident}_CONTROLLER_HH__
222 #define __${ident}_CONTROLLER_HH__
228 #include "mem/protocol/${ident}_ProfileDumper.hh"
229 #include "mem/protocol/${ident}_Profiler.hh"
230 #include "mem/protocol/TransitionResult.hh"
231 #include "mem/protocol/Types.hh"
232 #include "mem/ruby/common/Consumer.hh"
233 #include "mem/ruby/common/Global.hh"
234 #include "mem/ruby/slicc_interface/AbstractController.hh"
235 #include "params/$c_ident.hh"
239 for var
in self
.objects
:
240 if var
.type.ident
not in seen_types
and not var
.type.isPrimitive
:
241 code('#include "mem/protocol/${{var.type.c_ident}}.hh"')
242 seen_types
.add(var
.type.ident
)
244 # for adding information to the protocol debug trace
246 extern std::stringstream ${ident}_transitionComment;
248 class $c_ident : public AbstractController
251 typedef ${c_ident}Params Params;
252 $c_ident(const Params *p);
253 static int getNumControllers();
255 MessageBuffer* getMandatoryQueue() const;
256 const int & getVersion() const;
257 const std::string toString() const;
258 const std::string getName() const;
259 void stallBuffer(MessageBuffer* buf, Address addr);
260 void wakeUpBuffers(Address addr);
261 void wakeUpAllBuffers();
262 void initNetworkPtr(Network* net_ptr) { m_net_ptr = net_ptr; }
263 void print(std::ostream& out) const;
264 void printConfig(std::ostream& out) const;
266 void printStats(std::ostream& out) const;
268 void blockOnQueue(Address addr, MessageBuffer* port);
269 void unblock(Address addr);
270 void recordCacheTrace(int cntrl, CacheRecorder* tr);
271 Sequencer* getSequencer() const;
273 bool functionalReadBuffers(PacketPtr&);
274 uint32_t functionalWriteBuffers(PacketPtr&);
281 for param
in self
.config_parameters
:
283 code('${{param.type_ast.type}}* m_${{param.ident}}_ptr;')
285 code('${{param.type_ast.type}} m_${{param.ident}};')
288 int m_number_of_TBEs;
290 TransitionResult doTransition(${ident}_Event event,
293 if self
.EntryType
!= None:
295 ${{self.EntryType.c_ident}}* m_cache_entry_ptr,
297 if self
.TBEType
!= None:
299 ${{self.TBEType.c_ident}}* m_tbe_ptr,
303 const Address& addr);
305 TransitionResult doTransitionWorker(${ident}_Event event,
306 ${ident}_State state,
307 ${ident}_State& next_state,
310 if self
.TBEType
!= None:
312 ${{self.TBEType.c_ident}}*& m_tbe_ptr,
314 if self
.EntryType
!= None:
316 ${{self.EntryType.c_ident}}*& m_cache_entry_ptr,
320 const Address& addr);
323 int m_transitions_per_cycle;
325 int m_recycle_latency;
326 std::map<std::string, std::string> m_cfg;
329 MachineID m_machineID;
331 std::map<Address, MessageBuffer*> m_block_map;
332 typedef std::vector<MessageBuffer*> MsgVecType;
333 typedef std::map< Address, MsgVecType* > WaitingBufType;
334 WaitingBufType m_waiting_buffers;
335 int m_max_in_port_rank;
336 int m_cur_in_port_rank;
337 static ${ident}_ProfileDumper s_profileDumper;
338 ${ident}_Profiler m_profiler;
339 static int m_num_controllers;
341 // Internal functions
344 for func
in self
.functions
:
345 proto
= func
.prototype
349 if self
.EntryType
!= None:
352 // Set and Reset for cache_entry variable
353 void set_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, AbstractCacheEntry* m_new_cache_entry);
354 void unset_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr);
357 if self
.TBEType
!= None:
360 // Set and Reset for tbe variable
361 void set_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${ident}_TBE* m_new_tbe);
362 void unset_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr);
369 if self
.TBEType
!= None and self
.EntryType
!= None:
370 for action
in self
.actions
.itervalues():
371 code('/** \\brief ${{action.desc}} */')
372 code('void ${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr);')
373 elif self
.TBEType
!= None:
374 for action
in self
.actions
.itervalues():
375 code('/** \\brief ${{action.desc}} */')
376 code('void ${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, const Address& addr);')
377 elif self
.EntryType
!= None:
378 for action
in self
.actions
.itervalues():
379 code('/** \\brief ${{action.desc}} */')
380 code('void ${{action.ident}}(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr);')
382 for action
in self
.actions
.itervalues():
383 code('/** \\brief ${{action.desc}} */')
384 code('void ${{action.ident}}(const Address& addr);')
386 # the controller internal variables
391 for var
in self
.objects
:
392 th
= var
.get("template", "")
393 code('${{var.type.c_ident}}$th* m_${{var.c_ident}}_ptr;')
395 if var
.type.ident
== "MessageBuffer":
396 self
.message_buffer_names
.append("m_%s_ptr" % var
.c_ident
)
400 code('#endif // __${ident}_CONTROLLER_H__')
401 code
.write(path
, '%s.hh' % c_ident
)
403 def printControllerCC(self
, path
, includes
):
404 '''Output the actions for performing the actions'''
406 code
= self
.symtab
.codeFormatter()
408 c_ident
= "%s_Controller" % self
.ident
411 /** \\file $c_ident.cc
413 * Auto generated C++ code started by $__file__:$__line__
414 * Created by slicc definition of Module "${{self.short}}"
417 #include <sys/types.h>
424 #include "base/compiler.hh"
425 #include "base/cprintf.hh"
426 #include "debug/RubyGenerated.hh"
427 #include "debug/RubySlicc.hh"
428 #include "mem/protocol/${ident}_Controller.hh"
429 #include "mem/protocol/${ident}_Event.hh"
430 #include "mem/protocol/${ident}_State.hh"
431 #include "mem/protocol/Types.hh"
432 #include "mem/ruby/common/Global.hh"
433 #include "mem/ruby/system/System.hh"
435 for include_path
in includes
:
436 code('#include "${{include_path}}"')
443 # include object classes
445 for var
in self
.objects
:
446 if var
.type.ident
not in seen_types
and not var
.type.isPrimitive
:
447 code('#include "mem/protocol/${{var.type.c_ident}}.hh"')
448 seen_types
.add(var
.type.ident
)
452 ${c_ident}Params::create()
454 return new $c_ident(this);
457 int $c_ident::m_num_controllers = 0;
458 ${ident}_ProfileDumper $c_ident::s_profileDumper;
460 // for adding information to the protocol debug trace
461 stringstream ${ident}_transitionComment;
462 #define APPEND_TRANSITION_COMMENT(str) (${ident}_transitionComment << str)
464 /** \\brief constructor */
465 $c_ident::$c_ident(const Params *p)
466 : AbstractController(p)
468 m_version = p->version;
469 m_transitions_per_cycle = p->transitions_per_cycle;
470 m_buffer_size = p->buffer_size;
471 m_recycle_latency = p->recycle_latency;
472 m_number_of_TBEs = p->number_of_TBEs;
473 m_is_blocking = false;
477 # max_port_rank is used to size vectors and thus should be one plus the
480 max_port_rank
= self
.in_ports
[0].pairs
["max_port_rank"] + 1
481 code(' m_max_in_port_rank = $max_port_rank;')
485 # After initializing the universal machine parameters, initialize the
486 # this machines config parameters. Also detemine if these configuration
487 # params include a sequencer. This information will be used later for
488 # contecting the sequencer back to the L1 cache controller.
490 contains_dma_sequencer
= False
492 for param
in self
.config_parameters
:
493 if param
.name
== "dma_sequencer":
494 contains_dma_sequencer
= True
495 elif re
.compile("sequencer").search(param
.name
):
496 sequencers
.append(param
.name
)
498 code('m_${{param.name}}_ptr = p->${{param.name}};')
500 code('m_${{param.name}} = p->${{param.name}};')
503 # For the l1 cache controller, add the special atomic support which
504 # includes passing the sequencer a pointer to the controller.
506 if self
.ident
== "L1Cache":
508 self
.error("The L1Cache controller must include the sequencer " \
509 "configuration parameter")
511 for seq
in sequencers
:
513 m_${{seq}}_ptr->setController(this);
517 for seq
in sequencers
:
519 m_${{seq}}_ptr->setController(this);
523 # For the DMA controller, pass the sequencer a pointer to the
526 if self
.ident
== "DMA":
527 if not contains_dma_sequencer
:
528 self
.error("The DMA controller must include the sequencer " \
529 "configuration parameter")
532 m_dma_sequencer_ptr->setController(this);
535 code('m_num_controllers++;')
536 for var
in self
.objects
:
537 if var
.ident
.find("mandatoryQueue") >= 0:
538 code('m_${{var.c_ident}}_ptr = new ${{var.type.c_ident}}();')
547 MachineType machine_type;
550 m_machineID.type = MachineType_${ident};
551 m_machineID.num = m_version;
553 // initialize objects
554 m_profiler.setVersion(m_version);
555 s_profileDumper.registerProfiler(&m_profiler);
560 for var
in self
.objects
:
562 vid
= "m_%s_ptr" % var
.c_ident
563 if "network" not in var
:
564 # Not a network port object
565 if "primitive" in vtype
:
566 code('$vid = new ${{vtype.c_ident}};')
568 code('(*$vid) = ${{var["default"]}};')
573 code('$vid = ${{var["factory"]}};')
574 elif var
.ident
.find("mandatoryQueue") < 0:
575 th
= var
.get("template", "")
576 expr
= "%s = new %s%s" % (vid
, vtype
.c_ident
, th
)
579 if "non_obj" not in vtype
and not vtype
.isEnumeration
:
580 if expr
.find("TBETable") >= 0:
581 args
= "m_number_of_TBEs"
583 args
= var
.get("constructor_hack", "")
585 code('$expr($args);')
587 code('assert($vid != NULL);')
590 code('*$vid = ${{var["default"]}}; // Object default')
591 elif "default" in vtype
:
592 comment
= "Type %s default" % vtype
.ident
593 code('*$vid = ${{vtype["default"]}}; // $comment')
596 if "ordered" in var
and "trigger_queue" not in var
:
598 code('$vid->setOrdering(${{var["ordered"]}});')
603 code('$vid->setRandomization(${{var["random"]}});')
606 if vtype
.isBuffer
and \
607 "rank" in var
and "trigger_queue" not in var
:
608 code('$vid->setPriority(${{var["rank"]}});')
611 # Network port object
612 network
= var
["network"]
613 ordered
= var
["ordered"]
614 vnet
= var
["virtual_network"]
615 vnet_type
= var
["vnet_type"]
617 assert var
.machine
is not None
619 machine_type = string_to_MachineType("${{var.machine.ident}}");
620 base = MachineType_base_number(machine_type);
621 $vid = m_net_ptr->get${network}NetQueue(m_version + base, $ordered, $vnet, "$vnet_type");
624 code('assert($vid != NULL);')
629 code('$vid->setOrdering(${{var["ordered"]}});')
634 code('$vid->setRandomization(${{var["random"]}});')
638 code('$vid->setPriority(${{var["rank"]}})')
643 if (m_buffer_size > 0) {
644 $vid->resize(m_buffer_size);
648 # set description (may be overriden later by port def)
650 $vid->setDescription("[Version " + to_string(m_version) + ", ${ident}, name=${{var.c_ident}}]");
655 if "recycle_latency" in var
:
656 code('$vid->setRecycleLatency(${{var["recycle_latency"]}});')
658 code('$vid->setRecycleLatency(m_recycle_latency);')
661 # Set the queue consumers
663 for port
in self
.in_ports
:
664 code('${{port.code}}.setConsumer(this);')
666 # Set the queue descriptions
668 for port
in self
.in_ports
:
669 code('${{port.code}}.setDescription("[Version " + to_string(m_version) + ", $ident, $port]");')
671 # Initialize the transition profiling
673 for trans
in self
.transitions
:
674 # Figure out if we stall
676 for action
in trans
.actions
:
677 if action
.ident
== "z_stall":
680 # Only possible if it is not a 'z' case
682 state
= "%s_State_%s" % (self
.ident
, trans
.state
.ident
)
683 event
= "%s_Event_%s" % (self
.ident
, trans
.event
.ident
)
684 code('m_profiler.possibleTransition($state, $event);')
689 has_mandatory_q
= False
690 for port
in self
.in_ports
:
691 if port
.code
.find("mandatoryQueue_ptr") >= 0:
692 has_mandatory_q
= True
695 mq_ident
= "m_%s_mandatoryQueue_ptr" % self
.ident
700 for param
in self
.config_parameters
:
701 if param
.name
== "sequencer":
702 assert(param
.pointer
)
703 seq_ident
= "m_%s_ptr" % param
.name
707 $c_ident::getNumControllers()
709 return m_num_controllers;
713 $c_ident::getMandatoryQueue() const
719 $c_ident::getSequencer() const
725 $c_ident::getVersion() const
731 $c_ident::toString() const
737 $c_ident::getName() const
743 $c_ident::stallBuffer(MessageBuffer* buf, Address addr)
745 if (m_waiting_buffers.count(addr) == 0) {
746 MsgVecType* msgVec = new MsgVecType;
747 msgVec->resize(m_max_in_port_rank, NULL);
748 m_waiting_buffers[addr] = msgVec;
750 (*(m_waiting_buffers[addr]))[m_cur_in_port_rank] = buf;
754 $c_ident::wakeUpBuffers(Address addr)
756 if (m_waiting_buffers.count(addr) > 0) {
758 // Wake up all possible lower rank (i.e. lower priority) buffers that could
759 // be waiting on this message.
761 for (int in_port_rank = m_cur_in_port_rank - 1;
764 if ((*(m_waiting_buffers[addr]))[in_port_rank] != NULL) {
765 (*(m_waiting_buffers[addr]))[in_port_rank]->reanalyzeMessages(addr);
768 delete m_waiting_buffers[addr];
769 m_waiting_buffers.erase(addr);
774 $c_ident::wakeUpAllBuffers()
777 // Wake up all possible buffers that could be waiting on any message.
780 std::vector<MsgVecType*> wokeUpMsgVecs;
782 if(m_waiting_buffers.size() > 0) {
783 for (WaitingBufType::iterator buf_iter = m_waiting_buffers.begin();
784 buf_iter != m_waiting_buffers.end();
786 for (MsgVecType::iterator vec_iter = buf_iter->second->begin();
787 vec_iter != buf_iter->second->end();
789 if (*vec_iter != NULL) {
790 (*vec_iter)->reanalyzeAllMessages();
793 wokeUpMsgVecs.push_back(buf_iter->second);
796 for (std::vector<MsgVecType*>::iterator wb_iter = wokeUpMsgVecs.begin();
797 wb_iter != wokeUpMsgVecs.end();
802 m_waiting_buffers.clear();
807 $c_ident::blockOnQueue(Address addr, MessageBuffer* port)
809 m_is_blocking = true;
810 m_block_map[addr] = port;
814 $c_ident::unblock(Address addr)
816 m_block_map.erase(addr);
817 if (m_block_map.size() == 0) {
818 m_is_blocking = false;
823 $c_ident::print(ostream& out) const
825 out << "[$c_ident " << m_version << "]";
829 $c_ident::printConfig(ostream& out) const
831 out << "$c_ident config: " << m_name << endl;
832 out << " version: " << m_version << endl;
833 map<string, string>::const_iterator it;
834 for (it = m_cfg.begin(); it != m_cfg.end(); it++)
835 out << " " << it->first << ": " << it->second << endl;
839 $c_ident::printStats(ostream& out) const
843 # Cache and Memory Controllers have specific profilers associated with
844 # them. Print out these stats before dumping state transition stats.
846 for param
in self
.config_parameters
:
847 if param
.type_ast
.type.ident
== "CacheMemory" or \
848 param
.type_ast
.type.ident
== "DirectoryMemory" or \
849 param
.type_ast
.type.ident
== "MemoryControl":
850 assert(param
.pointer
)
851 code(' m_${{param.ident}}_ptr->printStats(out);')
854 if (m_version == 0) {
855 s_profileDumper.dumpStats(out);
859 void $c_ident::clearStats() {
862 # Cache and Memory Controllers have specific profilers associated with
863 # them. These stats must be cleared too.
865 for param
in self
.config_parameters
:
866 if param
.type_ast
.type.ident
== "CacheMemory" or \
867 param
.type_ast
.type.ident
== "MemoryControl":
868 assert(param
.pointer
)
869 code(' m_${{param.ident}}_ptr->clearStats();')
872 m_profiler.clearStats();
876 if self
.EntryType
!= None:
879 // Set and Reset for cache_entry variable
881 $c_ident::set_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, AbstractCacheEntry* m_new_cache_entry)
883 m_cache_entry_ptr = (${{self.EntryType.c_ident}}*)m_new_cache_entry;
887 $c_ident::unset_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr)
889 m_cache_entry_ptr = 0;
893 if self
.TBEType
!= None:
896 // Set and Reset for tbe variable
898 $c_ident::set_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.TBEType.c_ident}}* m_new_tbe)
900 m_tbe_ptr = m_new_tbe;
904 $c_ident::unset_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr)
913 $c_ident::recordCacheTrace(int cntrl, CacheRecorder* tr)
917 # Record cache contents for all associated caches.
920 for param
in self
.config_parameters
:
921 if param
.type_ast
.type.ident
== "CacheMemory":
922 assert(param
.pointer
)
923 code('m_${{param.ident}}_ptr->recordCacheContents(cntrl, tr);')
931 if self
.TBEType
!= None and self
.EntryType
!= None:
932 for action
in self
.actions
.itervalues():
933 if "c_code" not in action
:
937 /** \\brief ${{action.desc}} */
939 $c_ident::${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr)
941 DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
942 ${{action["c_code"]}}
946 elif self
.TBEType
!= None:
947 for action
in self
.actions
.itervalues():
948 if "c_code" not in action
:
952 /** \\brief ${{action.desc}} */
954 $c_ident::${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, const Address& addr)
956 DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
957 ${{action["c_code"]}}
961 elif self
.EntryType
!= None:
962 for action
in self
.actions
.itervalues():
963 if "c_code" not in action
:
967 /** \\brief ${{action.desc}} */
969 $c_ident::${{action.ident}}(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr)
971 DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
972 ${{action["c_code"]}}
977 for action
in self
.actions
.itervalues():
978 if "c_code" not in action
:
982 /** \\brief ${{action.desc}} */
984 $c_ident::${{action.ident}}(const Address& addr)
986 DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
987 ${{action["c_code"]}}
991 for func
in self
.functions
:
992 code(func
.generateCode())
994 # Function for functional reads from messages buffered in the controller
997 $c_ident::functionalReadBuffers(PacketPtr& pkt)
1000 for var
in self
.objects
:
1003 vid
= "m_%s_ptr" % var
.c_ident
1004 code('if ($vid->functionalRead(pkt)) { return true; }')
1010 # Function for functional writes to messages buffered in the controller
1013 $c_ident::functionalWriteBuffers(PacketPtr& pkt)
1015 uint32_t num_functional_writes = 0;
1017 for var
in self
.objects
:
1020 vid
= "m_%s_ptr" % var
.c_ident
1021 code('num_functional_writes += $vid->functionalWrite(pkt);')
1023 return num_functional_writes;
1027 code
.write(path
, "%s.cc" % c_ident
)
1029 def printCWakeup(self
, path
, includes
):
1030 '''Output the wakeup loop for the events'''
1032 code
= self
.symtab
.codeFormatter()
1035 outputRequest_types
= True
1036 if len(self
.request_types
) == 0:
1037 outputRequest_types
= False
1040 // Auto generated C++ code started by $__file__:$__line__
1041 // ${ident}: ${{self.short}}
1043 #include <sys/types.h>
1048 #include "base/misc.hh"
1049 #include "debug/RubySlicc.hh"
1050 #include "mem/protocol/${ident}_Controller.hh"
1051 #include "mem/protocol/${ident}_Event.hh"
1052 #include "mem/protocol/${ident}_State.hh"
1055 if outputRequest_types
:
1056 code('''#include "mem/protocol/${ident}_RequestType.hh"''')
1059 #include "mem/protocol/Types.hh"
1060 #include "mem/ruby/common/Global.hh"
1061 #include "mem/ruby/system/System.hh"
1065 for include_path
in includes
:
1066 code('#include "${{include_path}}"')
1070 using namespace std;
1073 ${ident}_Controller::wakeup()
1077 // Some cases will put us into an infinite loop without this limit
1078 assert(counter <= m_transitions_per_cycle);
1079 if (counter == m_transitions_per_cycle) {
1080 // Count how often we are fully utilized
1081 g_system_ptr->getProfiler()->controllerBusy(m_machineID);
1083 // Wakeup in another cycle and try again
1094 for port
in self
.in_ports
:
1096 code('// ${ident}InPort $port')
1097 if port
.pairs
.has_key("rank"):
1098 code('m_cur_in_port_rank = ${{port.pairs["rank"]}};')
1100 code('m_cur_in_port_rank = 0;')
1101 code('${{port["c_code_in_port"]}}')
1109 break; // If we got this far, we have nothing left todo
1114 code
.write(path
, "%s_Wakeup.cc" % self
.ident
)
1116 def printCSwitch(self
, path
):
1117 '''Output switch statement for transition table'''
1119 code
= self
.symtab
.codeFormatter()
1123 // Auto generated C++ code started by $__file__:$__line__
1124 // ${ident}: ${{self.short}}
1128 #include "base/misc.hh"
1129 #include "base/trace.hh"
1130 #include "debug/ProtocolTrace.hh"
1131 #include "debug/RubyGenerated.hh"
1132 #include "mem/protocol/${ident}_Controller.hh"
1133 #include "mem/protocol/${ident}_Event.hh"
1134 #include "mem/protocol/${ident}_State.hh"
1135 #include "mem/protocol/Types.hh"
1136 #include "mem/ruby/common/Global.hh"
1137 #include "mem/ruby/system/System.hh"
1139 #define HASH_FUN(state, event) ((int(state)*${ident}_Event_NUM)+int(event))
1141 #define GET_TRANSITION_COMMENT() (${ident}_transitionComment.str())
1142 #define CLEAR_TRANSITION_COMMENT() (${ident}_transitionComment.str(""))
1145 ${ident}_Controller::doTransition(${ident}_Event event,
1147 if self
.EntryType
!= None:
1149 ${{self.EntryType.c_ident}}* m_cache_entry_ptr,
1151 if self
.TBEType
!= None:
1153 ${{self.TBEType.c_ident}}* m_tbe_ptr,
1156 const Address &addr)
1159 if self
.TBEType
!= None and self
.EntryType
!= None:
1160 code('${ident}_State state = getState(m_tbe_ptr, m_cache_entry_ptr, addr);')
1161 elif self
.TBEType
!= None:
1162 code('${ident}_State state = getState(m_tbe_ptr, addr);')
1163 elif self
.EntryType
!= None:
1164 code('${ident}_State state = getState(m_cache_entry_ptr, addr);')
1166 code('${ident}_State state = getState(addr);')
1169 ${ident}_State next_state = state;
1171 DPRINTF(RubyGenerated, "%s, Time: %lld, state: %s, event: %s, addr: %s\\n",
1172 *this, g_system_ptr->getTime(), ${ident}_State_to_string(state),
1173 ${ident}_Event_to_string(event), addr);
1175 TransitionResult result =
1177 if self
.TBEType
!= None and self
.EntryType
!= None:
1178 code('doTransitionWorker(event, state, next_state, m_tbe_ptr, m_cache_entry_ptr, addr);')
1179 elif self
.TBEType
!= None:
1180 code('doTransitionWorker(event, state, next_state, m_tbe_ptr, addr);')
1181 elif self
.EntryType
!= None:
1182 code('doTransitionWorker(event, state, next_state, m_cache_entry_ptr, addr);')
1184 code('doTransitionWorker(event, state, next_state, addr);')
1187 if (result == TransitionResult_Valid) {
1188 DPRINTF(RubyGenerated, "next_state: %s\\n",
1189 ${ident}_State_to_string(next_state));
1190 m_profiler.countTransition(state, event);
1191 DPRINTFR(ProtocolTrace, "%15d %3s %10s%20s %6s>%-6s %s %s\\n",
1192 curTick(), m_version, "${ident}",
1193 ${ident}_Event_to_string(event),
1194 ${ident}_State_to_string(state),
1195 ${ident}_State_to_string(next_state),
1196 addr, GET_TRANSITION_COMMENT());
1198 CLEAR_TRANSITION_COMMENT();
1200 if self
.TBEType
!= None and self
.EntryType
!= None:
1201 code('setState(m_tbe_ptr, m_cache_entry_ptr, addr, next_state);')
1202 code('setAccessPermission(m_cache_entry_ptr, addr, next_state);')
1203 elif self
.TBEType
!= None:
1204 code('setState(m_tbe_ptr, addr, next_state);')
1205 code('setAccessPermission(addr, next_state);')
1206 elif self
.EntryType
!= None:
1207 code('setState(m_cache_entry_ptr, addr, next_state);')
1208 code('setAccessPermission(m_cache_entry_ptr, addr, next_state);')
1210 code('setState(addr, next_state);')
1211 code('setAccessPermission(addr, next_state);')
1214 } else if (result == TransitionResult_ResourceStall) {
1215 DPRINTFR(ProtocolTrace, "%15s %3s %10s%20s %6s>%-6s %s %s\\n",
1216 curTick(), m_version, "${ident}",
1217 ${ident}_Event_to_string(event),
1218 ${ident}_State_to_string(state),
1219 ${ident}_State_to_string(next_state),
1220 addr, "Resource Stall");
1221 } else if (result == TransitionResult_ProtocolStall) {
1222 DPRINTF(RubyGenerated, "stalling\\n");
1223 DPRINTFR(ProtocolTrace, "%15s %3s %10s%20s %6s>%-6s %s %s\\n",
1224 curTick(), m_version, "${ident}",
1225 ${ident}_Event_to_string(event),
1226 ${ident}_State_to_string(state),
1227 ${ident}_State_to_string(next_state),
1228 addr, "Protocol Stall");
1235 ${ident}_Controller::doTransitionWorker(${ident}_Event event,
1236 ${ident}_State state,
1237 ${ident}_State& next_state,
1240 if self
.TBEType
!= None:
1242 ${{self.TBEType.c_ident}}*& m_tbe_ptr,
1244 if self
.EntryType
!= None:
1246 ${{self.EntryType.c_ident}}*& m_cache_entry_ptr,
1249 const Address& addr)
1251 switch(HASH_FUN(state, event)) {
1254 # This map will allow suppress generating duplicate code
1257 for trans
in self
.transitions
:
1258 case_string
= "%s_State_%s, %s_Event_%s" % \
1259 (self
.ident
, trans
.state
.ident
, self
.ident
, trans
.event
.ident
)
1261 case
= self
.symtab
.codeFormatter()
1262 # Only set next_state if it changes
1263 if trans
.state
!= trans
.nextState
:
1264 ns_ident
= trans
.nextState
.ident
1265 case('next_state = ${ident}_State_${ns_ident};')
1267 actions
= trans
.actions
1268 request_types
= trans
.request_types
1270 # Check for resources
1272 res
= trans
.resources
1273 for key
,val
in res
.iteritems():
1274 if key
.type.ident
!= "DNUCAStopTable":
1276 if (!%s.areNSlotsAvailable(%s))
1277 return TransitionResult_ResourceStall;
1278 ''' % (key
.code
, val
)
1279 case_sorter
.append(val
)
1281 # Check all of the request_types for resource constraints
1282 for request_type
in request_types
:
1284 if (!checkResourceAvailable(%s_RequestType_%s, addr)) {
1285 return TransitionResult_ResourceStall;
1287 ''' % (self
.ident
, request_type
.ident
)
1288 case_sorter
.append(val
)
1290 # Emit the code sequences in a sorted order. This makes the
1291 # output deterministic (without this the output order can vary
1292 # since Map's keys() on a vector of pointers is not deterministic
1293 for c
in sorted(case_sorter
):
1296 # Record access types for this transition
1297 for request_type
in request_types
:
1298 case('recordRequestType(${ident}_RequestType_${{request_type.ident}}, addr);')
1300 # Figure out if we stall
1302 for action
in actions
:
1303 if action
.ident
== "z_stall":
1308 case('return TransitionResult_ProtocolStall;')
1310 if self
.TBEType
!= None and self
.EntryType
!= None:
1311 for action
in actions
:
1312 case('${{action.ident}}(m_tbe_ptr, m_cache_entry_ptr, addr);')
1313 elif self
.TBEType
!= None:
1314 for action
in actions
:
1315 case('${{action.ident}}(m_tbe_ptr, addr);')
1316 elif self
.EntryType
!= None:
1317 for action
in actions
:
1318 case('${{action.ident}}(m_cache_entry_ptr, addr);')
1320 for action
in actions
:
1321 case('${{action.ident}}(addr);')
1322 case('return TransitionResult_Valid;')
1326 # Look to see if this transition code is unique.
1327 if case
not in cases
:
1330 cases
[case
].append(case_string
)
1332 # Walk through all of the unique code blocks and spit out the
1333 # corresponding case statement elements
1334 for case
,transitions
in cases
.iteritems():
1335 # Iterative over all the multiple transitions that share
1337 for trans
in transitions
:
1338 code(' case HASH_FUN($trans):')
1343 fatal("Invalid transition\\n"
1344 "%s time: %d addr: %s event: %s state: %s\\n",
1345 name(), g_system_ptr->getTime(), addr, event, state);
1347 return TransitionResult_Valid;
1350 code
.write(path
, "%s_Transitions.cc" % self
.ident
)
1352 def printProfileDumperHH(self
, path
):
1353 code
= self
.symtab
.codeFormatter()
1357 // Auto generated C++ code started by $__file__:$__line__
1358 // ${ident}: ${{self.short}}
1360 #ifndef __${ident}_PROFILE_DUMPER_HH__
1361 #define __${ident}_PROFILE_DUMPER_HH__
1367 #include "${ident}_Event.hh"
1368 #include "${ident}_Profiler.hh"
1370 typedef std::vector<${ident}_Profiler *> ${ident}_profilers;
1372 class ${ident}_ProfileDumper
1375 ${ident}_ProfileDumper();
1376 void registerProfiler(${ident}_Profiler* profiler);
1377 void dumpStats(std::ostream& out) const;
1380 ${ident}_profilers m_profilers;
1383 #endif // __${ident}_PROFILE_DUMPER_HH__
1385 code
.write(path
, "%s_ProfileDumper.hh" % self
.ident
)
1387 def printProfileDumperCC(self
, path
):
1388 code
= self
.symtab
.codeFormatter()
1392 // Auto generated C++ code started by $__file__:$__line__
1393 // ${ident}: ${{self.short}}
1395 #include "mem/protocol/${ident}_ProfileDumper.hh"
1397 ${ident}_ProfileDumper::${ident}_ProfileDumper()
1402 ${ident}_ProfileDumper::registerProfiler(${ident}_Profiler* profiler)
1404 m_profilers.push_back(profiler);
1408 ${ident}_ProfileDumper::dumpStats(std::ostream& out) const
1410 out << " --- ${ident} ---\\n";
1411 out << " - Event Counts -\\n";
1412 for (${ident}_Event event = ${ident}_Event_FIRST;
1413 event < ${ident}_Event_NUM;
1415 out << (${ident}_Event) event << " [";
1417 for (int i = 0; i < m_profilers.size(); i++) {
1418 out << m_profilers[i]->getEventCount(event) << " ";
1419 total += m_profilers[i]->getEventCount(event);
1421 out << "] " << total << "\\n";
1424 out << " - Transitions -\\n";
1425 for (${ident}_State state = ${ident}_State_FIRST;
1426 state < ${ident}_State_NUM;
1428 for (${ident}_Event event = ${ident}_Event_FIRST;
1429 event < ${ident}_Event_NUM;
1431 if (m_profilers[0]->isPossible(state, event)) {
1432 out << (${ident}_State) state << " "
1433 << (${ident}_Event) event << " [";
1435 for (int i = 0; i < m_profilers.size(); i++) {
1436 out << m_profilers[i]->getTransitionCount(state, event) << " ";
1437 total += m_profilers[i]->getTransitionCount(state, event);
1439 out << "] " << total << "\\n";
1446 code
.write(path
, "%s_ProfileDumper.cc" % self
.ident
)
1448 def printProfilerHH(self
, path
):
1449 code
= self
.symtab
.codeFormatter()
1453 // Auto generated C++ code started by $__file__:$__line__
1454 // ${ident}: ${{self.short}}
1456 #ifndef __${ident}_PROFILER_HH__
1457 #define __${ident}_PROFILER_HH__
1462 #include "mem/protocol/${ident}_Event.hh"
1463 #include "mem/protocol/${ident}_State.hh"
1464 #include "mem/ruby/common/TypeDefines.hh"
1466 class ${ident}_Profiler
1469 ${ident}_Profiler();
1470 void setVersion(int version);
1471 void countTransition(${ident}_State state, ${ident}_Event event);
1472 void possibleTransition(${ident}_State state, ${ident}_Event event);
1473 uint64 getEventCount(${ident}_Event event);
1474 bool isPossible(${ident}_State state, ${ident}_Event event);
1475 uint64 getTransitionCount(${ident}_State state, ${ident}_Event event);
1479 int m_counters[${ident}_State_NUM][${ident}_Event_NUM];
1480 int m_event_counters[${ident}_Event_NUM];
1481 bool m_possible[${ident}_State_NUM][${ident}_Event_NUM];
1485 #endif // __${ident}_PROFILER_HH__
1487 code
.write(path
, "%s_Profiler.hh" % self
.ident
)
1489 def printProfilerCC(self
, path
):
1490 code
= self
.symtab
.codeFormatter()
1494 // Auto generated C++ code started by $__file__:$__line__
1495 // ${ident}: ${{self.short}}
1499 #include "mem/protocol/${ident}_Profiler.hh"
1501 ${ident}_Profiler::${ident}_Profiler()
1503 for (int state = 0; state < ${ident}_State_NUM; state++) {
1504 for (int event = 0; event < ${ident}_Event_NUM; event++) {
1505 m_possible[state][event] = false;
1506 m_counters[state][event] = 0;
1509 for (int event = 0; event < ${ident}_Event_NUM; event++) {
1510 m_event_counters[event] = 0;
1515 ${ident}_Profiler::setVersion(int version)
1517 m_version = version;
1521 ${ident}_Profiler::clearStats()
1523 for (int state = 0; state < ${ident}_State_NUM; state++) {
1524 for (int event = 0; event < ${ident}_Event_NUM; event++) {
1525 m_counters[state][event] = 0;
1529 for (int event = 0; event < ${ident}_Event_NUM; event++) {
1530 m_event_counters[event] = 0;
1534 ${ident}_Profiler::countTransition(${ident}_State state, ${ident}_Event event)
1536 assert(m_possible[state][event]);
1537 m_counters[state][event]++;
1538 m_event_counters[event]++;
1541 ${ident}_Profiler::possibleTransition(${ident}_State state,
1542 ${ident}_Event event)
1544 m_possible[state][event] = true;
1548 ${ident}_Profiler::getEventCount(${ident}_Event event)
1550 return m_event_counters[event];
1554 ${ident}_Profiler::isPossible(${ident}_State state, ${ident}_Event event)
1556 return m_possible[state][event];
1560 ${ident}_Profiler::getTransitionCount(${ident}_State state,
1561 ${ident}_Event event)
1563 return m_counters[state][event];
1567 code
.write(path
, "%s_Profiler.cc" % self
.ident
)
1569 # **************************
1570 # ******* HTML Files *******
1571 # **************************
1572 def frameRef(self
, click_href
, click_target
, over_href
, over_num
, text
):
1573 code
= self
.symtab
.codeFormatter(fix_newlines
=False)
1574 code("""<A href=\"$click_href\" target=\"$click_target\" onmouseover=\"
1575 if (parent.frames[$over_num].location != parent.location + '$over_href') {
1576 parent.frames[$over_num].location='$over_href'
1578 ${{html.formatShorthand(text)}}
1582 def writeHTMLFiles(self
, path
):
1583 # Create table with no row hilighted
1584 self
.printHTMLTransitions(path
, None)
1586 # Generate transition tables
1587 for state
in self
.states
.itervalues():
1588 self
.printHTMLTransitions(path
, state
)
1590 # Generate action descriptions
1591 for action
in self
.actions
.itervalues():
1592 name
= "%s_action_%s.html" % (self
.ident
, action
.ident
)
1593 code
= html
.createSymbol(action
, "Action")
1594 code
.write(path
, name
)
1596 # Generate state descriptions
1597 for state
in self
.states
.itervalues():
1598 name
= "%s_State_%s.html" % (self
.ident
, state
.ident
)
1599 code
= html
.createSymbol(state
, "State")
1600 code
.write(path
, name
)
1602 # Generate event descriptions
1603 for event
in self
.events
.itervalues():
1604 name
= "%s_Event_%s.html" % (self
.ident
, event
.ident
)
1605 code
= html
.createSymbol(event
, "Event")
1606 code
.write(path
, name
)
1608 def printHTMLTransitions(self
, path
, active_state
):
1609 code
= self
.symtab
.codeFormatter()
1613 <BODY link="blue" vlink="blue">
1615 <H1 align="center">${{html.formatShorthand(self.short)}}:
1618 for i
,machine
in enumerate(self
.symtab
.getAllType(StateMachine
)):
1627 code('$extra<A target="Table" href="${mid}_table.html">$mid</A>')
1638 for event
in self
.events
.itervalues():
1639 href
= "%s_Event_%s.html" % (self
.ident
, event
.ident
)
1640 ref
= self
.frameRef(href
, "Status", href
, "1", event
.short
)
1641 code('<TH bgcolor=white>$ref</TH>')
1645 for state
in self
.states
.itervalues():
1647 if state
== active_state
:
1652 click
= "%s_table_%s.html" % (self
.ident
, state
.ident
)
1653 over
= "%s_State_%s.html" % (self
.ident
, state
.ident
)
1654 text
= html
.formatShorthand(state
.short
)
1655 ref
= self
.frameRef(click
, "Table", over
, "1", state
.short
)
1658 <TH bgcolor=$color>$ref</TH>
1661 # -- One column for each event
1662 for event
in self
.events
.itervalues():
1663 trans
= self
.table
.get((state
,event
), None)
1665 # This is the no transition case
1666 if state
== active_state
:
1671 code('<TD bgcolor=$color> </TD>')
1674 next
= trans
.nextState
1675 stall_action
= False
1677 # -- Get the actions
1678 for action
in trans
.actions
:
1679 if action
.ident
== "z_stall" or \
1680 action
.ident
== "zz_recycleMandatoryQueue":
1683 # -- Print out "actions/next-state"
1685 if state
== active_state
:
1690 elif active_state
and next
.ident
== active_state
.ident
:
1692 elif state
== active_state
:
1697 code('<TD bgcolor=$color>')
1698 for action
in trans
.actions
:
1699 href
= "%s_action_%s.html" % (self
.ident
, action
.ident
)
1700 ref
= self
.frameRef(href
, "Status", href
, "1",
1706 click
= "%s_table_%s.html" % (self
.ident
, next
.ident
)
1707 over
= "%s_State_%s.html" % (self
.ident
, next
.ident
)
1708 ref
= self
.frameRef(click
, "Table", over
, "1", next
.short
)
1713 if state
== active_state
:
1718 click
= "%s_table_%s.html" % (self
.ident
, state
.ident
)
1719 over
= "%s_State_%s.html" % (self
.ident
, state
.ident
)
1720 ref
= self
.frameRef(click
, "Table", over
, "1", state
.short
)
1722 <TH bgcolor=$color>$ref</TH>
1731 for event
in self
.events
.itervalues():
1732 href
= "%s_Event_%s.html" % (self
.ident
, event
.ident
)
1733 ref
= self
.frameRef(href
, "Status", href
, "1", event
.short
)
1734 code('<TH bgcolor=white>$ref</TH>')
1743 name
= "%s_table_%s.html" % (self
.ident
, active_state
.ident
)
1745 name
= "%s_table.html" % self
.ident
1746 code
.write(path
, name
)
1748 __all__
= [ "StateMachine" ]