text: # Copyright (c) 2019 ARM Limited
  # All rights reserved.
  #
  # The license below extends only to copyright in the software and shall
  # not be construed as granting a license to any other intellectual
  # property including but not limited to intellectual property relating
  # to a hardware implementation of the functionality of the software
  # licensed hereunder.  You may use the software subject to the license
  # terms below provided that you ensure that this notice is replicated
  # unmodified and in its entirety in all distributions of the software,
  # modified or unmodified, in source code or in binary form.
  #
  # Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
  # Copyright (c) 2009 The Hewlett-Packard Development Company
  # Copyright (c) 2013 Advanced Micro Devices, Inc.
  # All rights reserved.
  #
  # Redistribution and use in source and binary forms, with or without
  # modification, are permitted provided that the following conditions are
  # met: redistributions of source code must retain the above copyright
  # notice, this list of conditions and the following disclaimer;
  # redistributions in binary form must reproduce the above copyright
  # notice, this list of conditions and the following disclaimer in the
  # documentation and/or other materials provided with the distribution;
  # neither the name of the copyright holders nor the names of its
  # contributors may be used to endorse or promote products derived from
  # this software without specific prior written permission.
  #
  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  
  from collections import OrderedDict
  
  from slicc.symbols.Symbol import Symbol
  from slicc.symbols.Var import Var
  import slicc.generate.html as html
  import re
  
  python_class_map = {
                      "int": "Int",
                      "NodeID": "Int",
                      "uint32_t" : "UInt32",
                      "std::string": "String",
                      "bool": "Bool",
                      "CacheMemory": "RubyCache",
                      "WireBuffer": "RubyWireBuffer",
                      "Sequencer": "RubySequencer",
                      "GPUCoalescer" : "RubyGPUCoalescer",
                      "VIPERCoalescer" : "VIPERCoalescer",
                      "DirectoryMemory": "RubyDirectoryMemory",
                      "PerfectCacheMemory": "RubyPerfectCacheMemory",
                      "MemoryControl": "MemoryControl",
                      "MessageBuffer": "MessageBuffer",
                      "DMASequencer": "DMASequencer",
                      "RubyPrefetcher":"RubyPrefetcher",
                      "Cycles":"Cycles",
                     }
  
  class StateMachine(Symbol):
      def __init__(self, symtab, ident, location, pairs, config_parameters):
          super(StateMachine, self).__init__(symtab, ident, location, pairs)
          self.table = None
  
          # Data members in the State Machine that have been declared before
          # the opening brace '{'  of the machine.  Note that these along with
          # the members in self.objects form the entire set of data members.
          self.config_parameters = config_parameters
  
          self.prefetchers = []
  
          for param in config_parameters:
              if param.pointer:
                  var = Var(symtab, param.ident, location, param.type_ast.type,
                            "(*m_%s_ptr)" % param.ident, {}, self)
              else:
                  var = Var(symtab, param.ident, location, param.type_ast.type,
                            "m_%s" % param.ident, {}, self)
  
              self.symtab.registerSym(param.ident, var)
  
              if str(param.type_ast.type) == "RubyPrefetcher":
                  self.prefetchers.append(var)
  
          self.states = OrderedDict()
          self.events = OrderedDict()
          self.actions = OrderedDict()
          self.request_types = OrderedDict()
          self.transitions = []
          self.in_ports = []
          self.functions = []
  
          # Data members in the State Machine that have been declared inside
          # the {} machine.  Note that these along with the config params
          # form the entire set of data members of the machine.
          self.objects = []
          self.TBEType   = None
          self.EntryType = None
          self.debug_flags = set()
          self.debug_flags.add('RubyGenerated')
          self.debug_flags.add('RubySlicc')
  
      def __repr__(self):
          return "[StateMachine: %s]" % self.ident
  
      def addState(self, state):
          assert self.table is None
          self.states[state.ident] = state
  
      def addEvent(self, event):
          assert self.table is None
          self.events[event.ident] = event
  
      def addAction(self, action):
          assert self.table is None
  
          # Check for duplicate action
          for other in self.actions.values():
              if action.ident == other.ident:
                  action.warning("Duplicate action definition: %s" % action.ident)
                  action.error("Duplicate action definition: %s" % action.ident)
              if action.short == other.short:
                  other.warning("Duplicate action shorthand: %s" % other.ident)
                  other.warning("    shorthand = %s" % other.short)
                  action.warning("Duplicate action shorthand: %s" % action.ident)
                  action.error("    shorthand = %s" % action.short)
  
          self.actions[action.ident] = action
  
      def addDebugFlag(self, flag):
          self.debug_flags.add(flag)
  
      def addRequestType(self, request_type):
          assert self.table is None
          self.request_types[request_type.ident] = request_type
  
      def addTransition(self, trans):
          assert self.table is None
          self.transitions.append(trans)
  
      def addInPort(self, var):
          self.in_ports.append(var)
  
      def addFunc(self, func):
          # register func in the symbol table
          self.symtab.registerSym(str(func), func)
          self.functions.append(func)
  
      def addObject(self, obj):
          self.symtab.registerSym(str(obj), obj)
          self.objects.append(obj)
  
      def addType(self, type):
          type_ident = '%s' % type.c_ident
  
          if type_ident == "%s_TBE" %self.ident:
              if self.TBEType != None:
                  self.error("Multiple Transaction Buffer types in a " \
                             "single machine.");
              self.TBEType = type
  
          elif "interface" in type and "AbstractCacheEntry" == type["interface"]:
              if "main" in type and "false" == type["main"].lower():
                  pass # this isn't the EntryType
              else:
                  if self.EntryType != None:
                      self.error("Multiple AbstractCacheEntry types in a " \
                                 "single machine.");
                  self.EntryType = type
  
      # Needs to be called before accessing the table
      def buildTable(self):
          assert self.table is None
  
          table = {}
  
          for trans in self.transitions:
              # Track which actions we touch so we know if we use them
              # all -- really this should be done for all symbols as
              # part of the symbol table, then only trigger it for
              # Actions, States, Events, etc.
  
              for action in trans.actions:
                  action.used = True
  
              index = (trans.state, trans.event)
              if index in table:
                  table[index].warning("Duplicate transition: %s" % table[index])
                  trans.error("Duplicate transition: %s" % trans)
              table[index] = trans
  
          # Look at all actions to make sure we used them all
          for action in self.actions.values():
              if not action.used:
                  error_msg = "Unused action: %s" % action.ident
                  if "desc" in action:
                      error_msg += ", "  + action.desc
                  action.warning(error_msg)
          self.table = table
  
      # determine the port->msg buffer mappings
      def getBufferMaps(self, ident):
          msg_bufs = []
          port_to_buf_map = {}
          in_msg_bufs = {}
          for port in self.in_ports:
              buf_name = "m_%s_ptr" % port.pairs["buffer_expr"].name
              msg_bufs.append(buf_name)
              port_to_buf_map[port] = msg_bufs.index(buf_name)
              if buf_name not in in_msg_bufs:
                  in_msg_bufs[buf_name] = [port]
              else:
                  in_msg_bufs[buf_name].append(port)
          return port_to_buf_map, in_msg_bufs, msg_bufs
  
      def writeCodeFiles(self, path, includes):
          self.printControllerPython(path)
          self.printControllerHH(path)
          self.printControllerCC(path, includes)
          self.printCSwitch(path)
          self.printCWakeup(path, includes)
  
      def printControllerPython(self, path):
          code = self.symtab.codeFormatter()
          ident = self.ident
  
          py_ident = "%s_Controller" % ident
          c_ident = "%s_Controller" % self.ident
  
          code('''
  from m5.params import *
  from m5.SimObject import SimObject
  from m5.objects.Controller import RubyController
  
  class $py_ident(RubyController):
      type = '$py_ident'
      cxx_header = 'mem/ruby/protocol/${c_ident}.hh'
  ''')
          code.indent()
          for param in self.config_parameters:
              dflt_str = ''
  
              if param.rvalue is not None:
                  dflt_str = str(param.rvalue.inline()) + ', '
  
              if param.type_ast.type.c_ident in python_class_map:
                  python_type = python_class_map[param.type_ast.type.c_ident]
                  code('${{param.ident}} = Param.${{python_type}}(${dflt_str}"")')
  
              else:
                  self.error("Unknown c++ to python class conversion for c++ " \
                             "type: '%s'. Please update the python_class_map " \
                             "in StateMachine.py", param.type_ast.type.c_ident)
  
          code.dedent()
          code.write(path, '%s.py' % py_ident)
  
  
      def printControllerHH(self, path):
          '''Output the method declarations for the class declaration'''
          code = self.symtab.codeFormatter()
          ident = self.ident
          c_ident = "%s_Controller" % self.ident
  
          code('''
  /** \\file $c_ident.hh
   *
   * Auto generated C++ code started by $__file__:$__line__
   * Created by slicc definition of Module "${{self.short}}"
   */
  
  #ifndef __${ident}_CONTROLLER_HH__
  #define __${ident}_CONTROLLER_HH__
  
  #include <iostream>
  #include <sstream>
  #include <string>
  
  #include "mem/ruby/common/Consumer.hh"
  #include "mem/ruby/protocol/TransitionResult.hh"
  #include "mem/ruby/protocol/Types.hh"
  #include "mem/ruby/slicc_interface/AbstractController.hh"
  #include "params/$c_ident.hh"
  
  ''')
  
          seen_types = set()
          for var in self.objects:
              if var.type.ident not in seen_types and not var.type.isPrimitive:
                  code('#include "mem/ruby/protocol/${{var.type.c_ident}}.hh"')
                  seen_types.add(var.type.ident)
  
          # for adding information to the protocol debug trace
          code('''
  extern std::stringstream ${ident}_transitionComment;
  
  class $c_ident : public AbstractController
  {
    public:
      typedef ${c_ident}Params Params;
      $c_ident(const Params *p);
      static int getNumControllers();
      void init();
  
      MessageBuffer *getMandatoryQueue() const;
      MessageBuffer *getMemReqQueue() const;
      MessageBuffer *getMemRespQueue() const;
      void initNetQueues();
  
      void print(std::ostream& out) const;
      void wakeup();
      void resetStats();
      void regStats();
      void collateStats();
  
      void recordCacheTrace(int cntrl, CacheRecorder* tr);
      Sequencer* getCPUSequencer() const;
      DMASequencer* getDMASequencer() const;
      GPUCoalescer* getGPUCoalescer() const;
  
      bool functionalReadBuffers(PacketPtr&);
      int functionalWriteBuffers(PacketPtr&);
  
      void countTransition(${ident}_State state, ${ident}_Event event);
      void possibleTransition(${ident}_State state, ${ident}_Event event);
      uint64_t getEventCount(${ident}_Event event);
      bool isPossible(${ident}_State state, ${ident}_Event event);
      uint64_t getTransitionCount(${ident}_State state, ${ident}_Event event);
  
  private:
  ''')
  
          code.indent()
          # added by SS
          for param in self.config_parameters:
              if param.pointer:
                  code('${{param.type_ast.type}}* m_${{param.ident}}_ptr;')
              else:
                  code('${{param.type_ast.type}} m_${{param.ident}};')
  
          code('''
  TransitionResult doTransition(${ident}_Event event,
  ''')
  
          if self.EntryType != None:
              code('''
                                ${{self.EntryType.c_ident}}* m_cache_entry_ptr,
  ''')
          if self.TBEType != None:
              code('''
                                ${{self.TBEType.c_ident}}* m_tbe_ptr,
  ''')
  
          code('''
                                Addr addr);
  
  TransitionResult doTransitionWorker(${ident}_Event event,
                                      ${ident}_State state,
                                      ${ident}_State& next_state,
  ''')
  
          if self.TBEType != None:
              code('''
                                      ${{self.TBEType.c_ident}}*& m_tbe_ptr,
  ''')
          if self.EntryType != None:
              code('''
                                      ${{self.EntryType.c_ident}}*& m_cache_entry_ptr,
  ''')
  
          code('''
                                      Addr addr);
  
  int m_counters[${ident}_State_NUM][${ident}_Event_NUM];
  int m_event_counters[${ident}_Event_NUM];
  bool m_possible[${ident}_State_NUM][${ident}_Event_NUM];
  
  static std::vector<Stats::Vector *> eventVec;
  static std::vector<std::vector<Stats::Vector *> > transVec;
  static int m_num_controllers;
  
  // Internal functions
  ''')
  
          for func in self.functions:
              proto = func.prototype
              if proto:
                  code('$proto')
  
          if self.EntryType != None:
              code('''
  
  // Set and Reset for cache_entry variable
  void set_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, AbstractCacheEntry* m_new_cache_entry);
  void unset_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr);
  ''')
  
          if self.TBEType != None:
              code('''
  
  // Set and Reset for tbe variable
  void set_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${ident}_TBE* m_new_tbe);
  void unset_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr);
  ''')
  
          # Prototype the actions that the controller can take
          code('''
  
  // Actions
  ''')
          if self.TBEType != None and self.EntryType != None:
              for action in self.actions.values():
                  code('/** \\brief ${{action.desc}} */')
                  code('void ${{action.ident}}(${{self.TBEType.c_ident}}*& '
                       'm_tbe_ptr, ${{self.EntryType.c_ident}}*& '
                       'm_cache_entry_ptr, Addr addr);')
          elif self.TBEType != None:
              for action in self.actions.values():
                  code('/** \\brief ${{action.desc}} */')
                  code('void ${{action.ident}}(${{self.TBEType.c_ident}}*& '
                       'm_tbe_ptr, Addr addr);')
          elif self.EntryType != None:
              for action in self.actions.values():
                  code('/** \\brief ${{action.desc}} */')
                  code('void ${{action.ident}}(${{self.EntryType.c_ident}}*& '
                       'm_cache_entry_ptr, Addr addr);')
          else:
              for action in self.actions.values():
                  code('/** \\brief ${{action.desc}} */')
                  code('void ${{action.ident}}(Addr addr);')
  
          # the controller internal variables
          code('''
  
  // Objects
  ''')
          for var in self.objects:
              th = var.get("template", "")
              code('${{var.type.c_ident}}$th* m_${{var.ident}}_ptr;')
  
          code.dedent()
          code('};')
          code('#endif // __${ident}_CONTROLLER_H__')
          code.write(path, '%s.hh' % c_ident)
  
      def printControllerCC(self, path, includes):
          '''Output the actions for performing the actions'''
  
          code = self.symtab.codeFormatter()
          ident = self.ident
          c_ident = "%s_Controller" % self.ident
  
          # Unfortunately, clang compilers will throw a "call to function ...
          # that is neither visible in the template definition nor found by
          # argument-dependent lookup" error if "mem/ruby/common/BoolVec.hh" is
          # included after "base/cprintf.hh". This is because "base/cprintf.hh"
          # utilizes a "<<" operator in "base/cprintf_formats.hh" that is
          # defined in "mem/ruby/common/BoolVec.hh". While GCC compilers permit
          # the operator definition after usage in this case, clang compilers do
          # not.
          #
          # The reason for this verbose solution below is due to the gem5
          # style-checker, which will complain if "mem/ruby/common/BoolVec.hh"
          # is included above "base/cprintf.hh" in this file, despite it being
          # necessary in this case. This is therefore a bit of a hack to keep
          # both clang and our style-checker happy.
          base_include = '''
  #include "base/compiler.hh"
  #include "base/cprintf.hh"
  
  '''
  
          boolvec_include = '''
  #include "mem/ruby/common/BoolVec.hh"
  
  '''
  
          code('''
  /** \\file $c_ident.cc
   *
   * Auto generated C++ code started by $__file__:$__line__
   * Created by slicc definition of Module "${{self.short}}"
   */
  
  #include <sys/types.h>
  #include <unistd.h>
  
  #include <cassert>
  #include <sstream>
  #include <string>
  #include <typeinfo>
  
  ''')
  
          code(boolvec_include)
          code(base_include)
  
          for f in self.debug_flags:
              code('#include "debug/${{f}}.hh"')
          code('''
  #include "mem/ruby/network/Network.hh"
  #include "mem/ruby/protocol/${ident}_Controller.hh"
  #include "mem/ruby/protocol/${ident}_Event.hh"
  #include "mem/ruby/protocol/${ident}_State.hh"
  #include "mem/ruby/protocol/Types.hh"
  #include "mem/ruby/system/RubySystem.hh"
  
  ''')
          for include_path in includes:
              code('#include "${{include_path}}"')
  
          code('''
  
  using namespace std;
  ''')
  
          # include object classes
          seen_types = set()
          for var in self.objects:
              if var.type.ident not in seen_types and not var.type.isPrimitive:
                  code('#include "mem/ruby/protocol/${{var.type.c_ident}}.hh"')
              seen_types.add(var.type.ident)
  
          num_in_ports = len(self.in_ports)
  
          code('''
  $c_ident *
  ${c_ident}Params::create()
  {
      return new $c_ident(this);
  }
  
  int $c_ident::m_num_controllers = 0;
  std::vector<Stats::Vector *>  $c_ident::eventVec;
  std::vector<std::vector<Stats::Vector *> >  $c_ident::transVec;
  
  // for adding information to the protocol debug trace
  stringstream ${ident}_transitionComment;
  
  #ifndef NDEBUG
  #define APPEND_TRANSITION_COMMENT(str) (${ident}_transitionComment << str)
  #else
  #define APPEND_TRANSITION_COMMENT(str) do {} while (0)
  #endif
  
  /** \\brief constructor */
  $c_ident::$c_ident(const Params *p)
      : AbstractController(p)
  {
      m_machineID.type = MachineType_${ident};
      m_machineID.num = m_version;
      m_num_controllers++;
  
      m_in_ports = $num_in_ports;
  ''')
          code.indent()
  
          #
          # After initializing the universal machine parameters, initialize the
          # this machines config parameters.  Also if these configuration params
          # include a sequencer, connect the it to the controller.
          #
          for param in self.config_parameters:
              if param.pointer:
                  code('m_${{param.ident}}_ptr = p->${{param.ident}};')
              else:
                  code('m_${{param.ident}} = p->${{param.ident}};')
  
              if re.compile("sequencer").search(param.ident) or \
                     param.type_ast.type.c_ident == "GPUCoalescer" or \
                     param.type_ast.type.c_ident == "VIPERCoalescer":
                  code('''
  if (m_${{param.ident}}_ptr != NULL) {
      m_${{param.ident}}_ptr->setController(this);
  }
  ''')
  
          code('''
  
  for (int state = 0; state < ${ident}_State_NUM; state++) {
      for (int event = 0; event < ${ident}_Event_NUM; event++) {
          m_possible[state][event] = false;
          m_counters[state][event] = 0;
      }
  }
  for (int event = 0; event < ${ident}_Event_NUM; event++) {
      m_event_counters[event] = 0;
  }
  ''')
          code.dedent()
          code('''
  }
  
  void
  $c_ident::initNetQueues()
  {
      MachineType machine_type = string_to_MachineType("${{self.ident}}");
      int base M5_VAR_USED = MachineType_base_number(machine_type);
  
  ''')
          code.indent()
  
          # set for maintaining the vnet, direction pairs already seen for this
          # machine.  This map helps in implementing the check for avoiding
          # multiple message buffers being mapped to the same vnet.
          vnet_dir_set = set()
  
          for var in self.config_parameters:
              vid = "m_%s_ptr" % var.ident
              if "network" in var:
                  vtype = var.type_ast.type
                  code('assert($vid != NULL);')
  
                  # Network port object
                  network = var["network"]
  
                  if "virtual_network" in var:
                      vnet = var["virtual_network"]
                      vnet_type = var["vnet_type"]
  
                      assert (vnet, network) not in vnet_dir_set
                      vnet_dir_set.add((vnet,network))
  
                      code('''
  m_net_ptr->set${network}NetQueue(m_version + base, $vid->getOrdered(), $vnet,
                                   "$vnet_type", $vid);
  ''')
                  # Set Priority
                  if "rank" in var:
                      code('$vid->setPriority(${{var["rank"]}})')
  
          code.dedent()
          code('''
  }
  
  void
  $c_ident::init()
  {
      // initialize objects
  ''')
  
          code.indent()
  
          for var in self.objects:
              vtype = var.type
              vid = "m_%s_ptr" % var.ident
              if "network" not in var:
                  # Not a network port object
                  if "primitive" in vtype:
                      code('$vid = new ${{vtype.c_ident}};')
                      if "default" in var:
                          code('(*$vid) = ${{var["default"]}};')
                  else:
                      # Normal Object
                      th = var.get("template", "")
                      expr = "%s  = new %s%s" % (vid, vtype.c_ident, th)
                      args = ""
                      if "non_obj" not in vtype and not vtype.isEnumeration:
                          args = var.get("constructor", "")
  
                      code('$expr($args);')
                      code('assert($vid != NULL);')
  
                      if "default" in var:
                          code('*$vid = ${{var["default"]}}; // Object default')
                      elif "default" in vtype:
                          comment = "Type %s default" % vtype.ident
                          code('*$vid = ${{vtype["default"]}}; // $comment')
  
          # Set the prefetchers
          code()
          for prefetcher in self.prefetchers:
              code('${{prefetcher.code}}.setController(this);')
  
          code()
          for port in self.in_ports:
              # Set the queue consumers
              code('${{port.code}}.setConsumer(this);')
  
          # Initialize the transition profiling
          code()
          for trans in self.transitions:
              # Figure out if we stall
              stall = False
              for action in trans.actions:
                  if action.ident == "z_stall":
                      stall = True
  
              # Only possible if it is not a 'z' case
              if not stall:
                  state = "%s_State_%s" % (self.ident, trans.state.ident)
                  event = "%s_Event_%s" % (self.ident, trans.event.ident)
                  code('possibleTransition($state, $event);')
  
          code.dedent()
          code('''
      AbstractController::init();
      resetStats();
  }
  ''')
  
          mq_ident = "NULL"
          for port in self.in_ports:
              if port.code.find("mandatoryQueue_ptr") >= 0:
                  mq_ident = "m_mandatoryQueue_ptr"
  
          memoutq_ident = "NULL"
          for param in self.config_parameters:
              if param.ident.find("requestToMemory") >= 0:
                  memoutq_ident = "m_requestToMemory_ptr"
  
          memq_ident = "NULL"
          for port in self.in_ports:
              if port.code.find("responseFromMemory_ptr") >= 0:
                  memq_ident = "m_responseFromMemory_ptr"
  
          seq_ident = "NULL"
          for param in self.config_parameters:
              if param.ident == "sequencer":
                  assert(param.pointer)
                  seq_ident = "m_%s_ptr" % param.ident
  
          dma_seq_ident = "NULL"
          for param in self.config_parameters:
              if param.ident == "dma_sequencer":
                  assert(param.pointer)
                  dma_seq_ident = "m_%s_ptr" % param.ident
  
          coal_ident = "NULL"
          for param in self.config_parameters:
              if param.ident == "coalescer":
                  assert(param.pointer)
                  coal_ident = "m_%s_ptr" % param.ident
  
          if seq_ident != "NULL":
              code('''
  Sequencer*
  $c_ident::getCPUSequencer() const
  {
      if (NULL != $seq_ident && $seq_ident->isCPUSequencer()) {
          return $seq_ident;
      } else {
          return NULL;
      }
  }
  ''')
          else:
              code('''
  
  Sequencer*
  $c_ident::getCPUSequencer() const
  {
      return NULL;
  }
  ''')
  
          if dma_seq_ident != "NULL":
              code('''
  DMASequencer*
  $c_ident::getDMASequencer() const
  {
      if (NULL != $dma_seq_ident) {
          return $dma_seq_ident;
      } else {
          return NULL;
      }
  }
  ''')
          else:
              code('''
  
  DMASequencer*
  $c_ident::getDMASequencer() const
  {
      return NULL;
  }
  ''')
  
          if coal_ident != "NULL":
              code('''
  GPUCoalescer*
  $c_ident::getGPUCoalescer() const
  {
      if (NULL != $coal_ident && !$coal_ident->isCPUSequencer()) {
          return $coal_ident;
      } else {
          return NULL;
      }
  }
  ''')
          else:
              code('''
  
  GPUCoalescer*
  $c_ident::getGPUCoalescer() const
  {
      return NULL;
  }
  ''')
  
          code('''
  
  void
  $c_ident::regStats()
  {
      AbstractController::regStats();
  
      if (m_version == 0) {
          for (${ident}_Event event = ${ident}_Event_FIRST;
               event < ${ident}_Event_NUM; ++event) {
              Stats::Vector *t = new Stats::Vector();
              t->init(m_num_controllers);
              t->name(params()->ruby_system->name() + ".${c_ident}." +
                  ${ident}_Event_to_string(event));
              t->flags(Stats::pdf | Stats::total | Stats::oneline |
                       Stats::nozero);
  
              eventVec.push_back(t);
          }
  
          for (${ident}_State state = ${ident}_State_FIRST;
               state < ${ident}_State_NUM; ++state) {
  
              transVec.push_back(std::vector<Stats::Vector *>());
  
              for (${ident}_Event event = ${ident}_Event_FIRST;
                   event < ${ident}_Event_NUM; ++event) {
  
                  Stats::Vector *t = new Stats::Vector();
                  t->init(m_num_controllers);
                  t->name(params()->ruby_system->name() + ".${c_ident}." +
                          ${ident}_State_to_string(state) +
                          "." + ${ident}_Event_to_string(event));
  
                  t->flags(Stats::pdf | Stats::total | Stats::oneline |
                           Stats::nozero);
                  transVec[state].push_back(t);
              }
          }
      }
  }
  
  void
  $c_ident::collateStats()
  {
      for (${ident}_Event event = ${ident}_Event_FIRST;
           event < ${ident}_Event_NUM; ++event) {
          for (unsigned int i = 0; i < m_num_controllers; ++i) {
              RubySystem *rs = params()->ruby_system;
              std::map<uint32_t, AbstractController *>::iterator it =
                       rs->m_abstract_controls[MachineType_${ident}].find(i);
              assert(it != rs->m_abstract_controls[MachineType_${ident}].end());
              (*eventVec[event])[i] =
                  (($c_ident *)(*it).second)->getEventCount(event);
          }
      }
  
      for (${ident}_State state = ${ident}_State_FIRST;
           state < ${ident}_State_NUM; ++state) {
  
          for (${ident}_Event event = ${ident}_Event_FIRST;
               event < ${ident}_Event_NUM; ++event) {
  
              for (unsigned int i = 0; i < m_num_controllers; ++i) {
                  RubySystem *rs = params()->ruby_system;
                  std::map<uint32_t, AbstractController *>::iterator it =
                           rs->m_abstract_controls[MachineType_${ident}].find(i);
                  assert(it != rs->m_abstract_controls[MachineType_${ident}].end());
                  (*transVec[state][event])[i] =
                      (($c_ident *)(*it).second)->getTransitionCount(state, event);
              }
          }
      }
  }
  
  void
  $c_ident::countTransition(${ident}_State state, ${ident}_Event event)
  {
      assert(m_possible[state][event]);
      m_counters[state][event]++;
      m_event_counters[event]++;
  }
  void
  $c_ident::possibleTransition(${ident}_State state,
                               ${ident}_Event event)
  {
      m_possible[state][event] = true;
  }
  
  uint64_t
  $c_ident::getEventCount(${ident}_Event event)
  {
      return m_event_counters[event];
  }
  
  bool
  $c_ident::isPossible(${ident}_State state, ${ident}_Event event)
  {
      return m_possible[state][event];
  }
  
  uint64_t
  $c_ident::getTransitionCount(${ident}_State state,
                               ${ident}_Event event)
  {
      return m_counters[state][event];
  }
  
  int
  $c_ident::getNumControllers()
  {
      return m_num_controllers;
  }
  
  MessageBuffer*
  $c_ident::getMandatoryQueue() const
  {
      return $mq_ident;
  }
  
  MessageBuffer*
  $c_ident::getMemReqQueue() const
  {
      return $memoutq_ident;
  }
  
  MessageBuffer*
  $c_ident::getMemRespQueue() const
  {
      return $memq_ident;
  }
  
  void
  $c_ident::print(ostream& out) const
  {
      out << "[$c_ident " << m_version << "]";
  }
  
  void $c_ident::resetStats()
  {
      for (int state = 0; state < ${ident}_State_NUM; state++) {
          for (int event = 0; event < ${ident}_Event_NUM; event++) {
              m_counters[state][event] = 0;
          }
      }
  
      for (int event = 0; event < ${ident}_Event_NUM; event++) {
          m_event_counters[event] = 0;
      }
  
      AbstractController::resetStats();
  }
  ''')
  
          if self.EntryType != None:
              code('''
  
  // Set and Reset for cache_entry variable
  void
  $c_ident::set_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, AbstractCacheEntry* m_new_cache_entry)
  {
    m_cache_entry_ptr = (${{self.EntryType.c_ident}}*)m_new_cache_entry;
  }
  
  void
  $c_ident::unset_cache_entry(${{self.EntryType.c_ident}}*& m_cache_entry_ptr)
  {
    m_cache_entry_ptr = 0;
  }
  ''')
  
          if self.TBEType != None:
              code('''
  
  // Set and Reset for tbe variable
  void
  $c_ident::set_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.TBEType.c_ident}}* m_new_tbe)
  {
    m_tbe_ptr = m_new_tbe;
  }
  
  void
  $c_ident::unset_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr)
  {
    m_tbe_ptr = NULL;
  }
  ''')
  
          code('''
  
  void
  $c_ident::recordCacheTrace(int cntrl, CacheRecorder* tr)
  {
  ''')
          #
          # Record cache contents for all associated caches.
          #
          code.indent()
          for param in self.config_parameters:
              if param.type_ast.type.ident == "CacheMemory":
                  assert(param.pointer)
                  code('m_${{param.ident}}_ptr->recordCacheContents(cntrl, tr);')
  
          code.dedent()
          code('''
  }
  
  // Actions
  ''')
          if self.TBEType != None and self.EntryType != None:
              for action in self.actions.values():
                  if "c_code" not in action:
                   continue
  
                  code('''
  /** \\brief ${{action.desc}} */
  void
  $c_ident::${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.EntryType.c_ident}}*& m_cache_entry_ptr, Addr addr)
  {
      DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
      try {
         ${{action["c_code"]}}
      } catch (const RejectException & e) {
         fatal("Error in action ${{ident}}:${{action.ident}}: "
               "executed a peek statement with the wrong message "
               "type specified. ");
      }
  }
  
  ''')
          elif self.TBEType != None:
              for action in self.actions.values():
                  if "c_code" not in action:
                   continue
  
                  code('''
  /** \\brief ${{action.desc}} */
  void
  $c_ident::${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, Addr addr)
  {
      DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
      ${{action["c_code"]}}
  }
  
  ''')
          elif self.EntryType != None:
              for action in self.actions.values():
                  if "c_code" not in action:
                   continue
  
                  code('''
  /** \\brief ${{action.desc}} */
  void
  $c_ident::${{action.ident}}(${{self.EntryType.c_ident}}*& m_cache_entry_ptr, Addr addr)
  {
      DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
      ${{action["c_code"]}}
  }
  
  ''')
          else:
              for action in self.actions.values():
                  if "c_code" not in action:
                   continue
  
                  code('''
  /** \\brief ${{action.desc}} */
  void
  $c_ident::${{action.ident}}(Addr addr)
  {
      DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
      ${{action["c_code"]}}
  }
  
  ''')
          for func in self.functions:
              code(func.generateCode())
  
          # Function for functional writes to messages buffered in the controller
          code('''
  int
  $c_ident::functionalWriteBuffers(PacketPtr& pkt)
  {
      int num_functional_writes = 0;
  ''')
          for var in self.objects:
              vtype = var.type
              if vtype.isBuffer:
                  vid = "m_%s_ptr" % var.ident
                  code('num_functional_writes += $vid->functionalWrite(pkt);')
  
          for var in self.config_parameters:
              vtype = var.type_ast.type
              if vtype.isBuffer:
                  vid = "m_%s_ptr" % var.ident
                  code('num_functional_writes += $vid->functionalWrite(pkt);')
  
          code('''
      return num_functional_writes;
  }
  ''')
  
          # Function for functional reads to messages buffered in the controller
          code('''
  bool
  $c_ident::functionalReadBuffers(PacketPtr& pkt)
  {
  ''')
          for var in self.objects:
              vtype = var.type
              if vtype.isBuffer:
                  vid = "m_%s_ptr" % var.ident
                  code('if ($vid->functionalRead(pkt)) return true;')
  
          for var in self.config_parameters:
              vtype = var.type_ast.type
              if vtype.isBuffer:
                  vid = "m_%s_ptr" % var.ident
                  code('if ($vid->functionalRead(pkt)) return true;')
  
          code('''
      return false;
  }
  ''')
  
          code.write(path, "%s.cc" % c_ident)
  
      def printCWakeup(self, path, includes):
          '''Output the wakeup loop for the events'''
  
          code = self.symtab.codeFormatter()
          ident = self.ident
  
          outputRequest_types = True
          if len(self.request_types) == 0:
              outputRequest_types = False
  
          code('''
  // Auto generated C++ code started by $__file__:$__line__
  // ${ident}: ${{self.short}}
  
  #include <sys/types.h>
  #include <unistd.h>
  
  #include <cassert>
  #include <typeinfo>
  
  #include "base/logging.hh"
  
  ''')
          for f in self.debug_flags:
              code('#include "debug/${{f}}.hh"')
          code('''
  #include "mem/ruby/protocol/${ident}_Controller.hh"
  #include "mem/ruby/protocol/${ident}_Event.hh"
  #include "mem/ruby/protocol/${ident}_State.hh"
  
  ''')
  
          if outputRequest_types:
              code('''#include "mem/ruby/protocol/${ident}_RequestType.hh"''')
  
          code('''
  #include "mem/ruby/protocol/Types.hh"
  #include "mem/ruby/system/RubySystem.hh"
  
  ''')
  
  
          for include_path in includes:
              code('#include "${{include_path}}"')
  
          port_to_buf_map, in_msg_bufs, msg_bufs = self.getBufferMaps(ident)
  
          code('''
  
  using namespace std;
  
  void
  ${ident}_Controller::wakeup()
  {
      if (getMemReqQueue() && getMemReqQueue()->isReady(clockEdge())) {
          serviceMemoryQueue();
      }
  
      int counter = 0;
      while (true) {
          unsigned char rejected[${{len(msg_bufs)}}];
          memset(rejected, 0, sizeof(unsigned char)*${{len(msg_bufs)}});
          // Some cases will put us into an infinite loop without this limit
          assert(counter <= m_transitions_per_cycle);
          if (counter == m_transitions_per_cycle) {
              // Count how often we are fully utilized
              m_fully_busy_cycles++;
  
              // Wakeup in another cycle and try again
              scheduleEvent(Cycles(1));
              break;
          }
  ''')
  
          code.indent()
          code.indent()
  
          # InPorts
          #
          for port in self.in_ports:
              code.indent()
              code('// ${ident}InPort $port')
              if "rank" in port.pairs:
                  code('m_cur_in_port = ${{port.pairs["rank"]}};')
              else:
                  code('m_cur_in_port = 0;')
              if port in port_to_buf_map:
                  code('try {')
                  code.indent()
              code('${{port["c_code_in_port"]}}')
  
              if port in port_to_buf_map:
                  code.dedent()
                  code('''
              } catch (const RejectException & e) {
                  rejected[${{port_to_buf_map[port]}}]++;
              }
  ''')
              code.dedent()
              code('')
  
          code.dedent()
          code.dedent()
          code('''
          // If we got this far, we have nothing left todo or something went
          // wrong''')
          for buf_name, ports in in_msg_bufs.items():
              if len(ports) > 1:
                  # only produce checks when a buffer is shared by multiple ports
                  code('''
          if (${{buf_name}}->isReady(clockEdge()) && rejected[${{port_to_buf_map[ports[0]]}}] == ${{len(ports)}})
          {
              // no port claimed the message on the top of this buffer
              panic("Runtime Error at Ruby Time: %d. "
                    "All ports rejected a message. "
                    "You are probably sending a message type to this controller "
                    "over a virtual network that do not define an in_port for "
                    "the incoming message type.\\n",
                    Cycles(1));
          }
  ''')
          code('''
          break;
      }
  }
  ''')
  
          code.write(path, "%s_Wakeup.cc" % self.ident)
  
      def printCSwitch(self, path):
          '''Output switch statement for transition table'''
  
          code = self.symtab.codeFormatter()
          ident = self.ident
  
          code('''
  // Auto generated C++ code started by $__file__:$__line__
  // ${ident}: ${{self.short}}
  
  #include <cassert>
  
  #include "base/logging.hh"
  #include "base/trace.hh"
  #include "debug/ProtocolTrace.hh"
  #include "debug/RubyGenerated.hh"
  #include "mem/ruby/protocol/${ident}_Controller.hh"
  #include "mem/ruby/protocol/${ident}_Event.hh"
  #include "mem/ruby/protocol/${ident}_State.hh"
  #include "mem/ruby/protocol/Types.hh"
  #include "mem/ruby/system/RubySystem.hh"
  
  #define HASH_FUN(state, event)  ((int(state)*${ident}_Event_NUM)+int(event))
  
  #define GET_TRANSITION_COMMENT() (${ident}_transitionComment.str())
  #define CLEAR_TRANSITION_COMMENT() (${ident}_transitionComment.str(""))
  
  TransitionResult
  ${ident}_Controller::doTransition(${ident}_Event event,
  ''')
          if self.EntryType != None:
              code('''
                                    ${{self.EntryType.c_ident}}* m_cache_entry_ptr,
  ''')
          if self.TBEType != None:
              code('''
                                    ${{self.TBEType.c_ident}}* m_tbe_ptr,
  ''')
          code('''
                                    Addr addr)
  {
  ''')
          code.indent()
  
          if self.TBEType != None and self.EntryType != None:
              code('${ident}_State state = getState(m_tbe_ptr, m_cache_entry_ptr, addr);')
          elif self.TBEType != None:
              code('${ident}_State state = getState(m_tbe_ptr, addr);')
          elif self.EntryType != None:
              code('${ident}_State state = getState(m_cache_entry_ptr, addr);')
          else:
              code('${ident}_State state = getState(addr);')
  
          code('''
  ${ident}_State next_state = state;
  
  DPRINTF(RubyGenerated, "%s, Time: %lld, state: %s, event: %s, addr: %#x\\n",
          *this, curCycle(), ${ident}_State_to_string(state),
          ${ident}_Event_to_string(event), addr);
  
  TransitionResult result =
  ''')
          if self.TBEType != None and self.EntryType != None:
              code('doTransitionWorker(event, state, next_state, m_tbe_ptr, m_cache_entry_ptr, addr);')
          elif self.TBEType != None:
              code('doTransitionWorker(event, state, next_state, m_tbe_ptr, addr);')
          elif self.EntryType != None:
              code('doTransitionWorker(event, state, next_state, m_cache_entry_ptr, addr);')
          else:
              code('doTransitionWorker(event, state, next_state, addr);')
  
          port_to_buf_map, in_msg_bufs, msg_bufs = self.getBufferMaps(ident)
  
          code('''
  
  if (result == TransitionResult_Valid) {
      DPRINTF(RubyGenerated, "next_state: %s\\n",
              ${ident}_State_to_string(next_state));
      countTransition(state, event);
  
      DPRINTFR(ProtocolTrace, "%15d %3s %10s%20s %6s>%-6s %#x %s\\n",
               curTick(), m_version, "${ident}",
               ${ident}_Event_to_string(event),
               ${ident}_State_to_string(state),
               ${ident}_State_to_string(next_state),
               printAddress(addr), GET_TRANSITION_COMMENT());
  
      CLEAR_TRANSITION_COMMENT();
  ''')
          if self.TBEType != None and self.EntryType != None:
              code('setState(m_tbe_ptr, m_cache_entry_ptr, addr, next_state);')
              code('setAccessPermission(m_cache_entry_ptr, addr, next_state);')
          elif self.TBEType != None:
              code('setState(m_tbe_ptr, addr, next_state);')
              code('setAccessPermission(addr, next_state);')
          elif self.EntryType != None:
              code('setState(m_cache_entry_ptr, addr, next_state);')
              code('setAccessPermission(m_cache_entry_ptr, addr, next_state);')
          else:
              code('setState(addr, next_state);')
              code('setAccessPermission(addr, next_state);')
  
          code('''
  } else if (result == TransitionResult_ResourceStall) {
      DPRINTFR(ProtocolTrace, "%15s %3s %10s%20s %6s>%-6s %#x %s\\n",
               curTick(), m_version, "${ident}",
               ${ident}_Event_to_string(event),
               ${ident}_State_to_string(state),
               ${ident}_State_to_string(next_state),
               printAddress(addr), "Resource Stall");
  } else if (result == TransitionResult_ProtocolStall) {
      DPRINTF(RubyGenerated, "stalling\\n");
      DPRINTFR(ProtocolTrace, "%15s %3s %10s%20s %6s>%-6s %#x %s\\n",
               curTick(), m_version, "${ident}",
               ${ident}_Event_to_string(event),
               ${ident}_State_to_string(state),
               ${ident}_State_to_string(next_state),
               printAddress(addr), "Protocol Stall");
  }
  
  return result;
  ''')
          code.dedent()
          code('''
  }
  
  TransitionResult
  ${ident}_Controller::doTransitionWorker(${ident}_Event event,
                                          ${ident}_State state,
                                          ${ident}_State& next_state,
  ''')
  
          if self.TBEType != None:
              code('''
                                          ${{self.TBEType.c_ident}}*& m_tbe_ptr,
  ''')
          if self.EntryType != None:
                    code('''
                                          ${{self.EntryType.c_ident}}*& m_cache_entry_ptr,
  ''')
          code('''
                                          Addr addr)
  {
      switch(HASH_FUN(state, event)) {
  ''')
  
          # This map will allow suppress generating duplicate code
          cases = OrderedDict()
  
          for trans in self.transitions:
              case_string = "%s_State_%s, %s_Event_%s" % \
                  (self.ident, trans.state.ident, self.ident, trans.event.ident)
  
              case = self.symtab.codeFormatter()
              # Only set next_state if it changes
              if trans.state != trans.nextState:
                  if trans.nextState.isWildcard():
                      # When * is encountered as an end state of a transition,
                      # the next state is determined by calling the
                      # machine-specific getNextState function. The next state
                      # is determined before any actions of the transition
                      # execute, and therefore the next state calculation cannot
                      # depend on any of the transitionactions.
                      case('next_state = getNextState(addr);')
                  else:
                      ns_ident = trans.nextState.ident
                      case('next_state = ${ident}_State_${ns_ident};')
  
              actions = trans.actions
              request_types = trans.request_types
  
              # Check for resources
              case_sorter = []
              res = trans.resources
              for key,val in res.items():
                  val = '''
  if (!%s.areNSlotsAvailable(%s, clockEdge()))
      return TransitionResult_ResourceStall;
  ''' % (key.code, val)
                  case_sorter.append(val)
  
              # Check all of the request_types for resource constraints
              for request_type in request_types:
                  val = '''
  if (!checkResourceAvailable(%s_RequestType_%s, addr)) {
      return TransitionResult_ResourceStall;
  }
  ''' % (self.ident, request_type.ident)
                  case_sorter.append(val)
  
              # Emit the code sequences in a sorted order.  This makes the
              # output deterministic (without this the output order can vary
              # since Map's keys() on a vector of pointers is not deterministic
              for c in sorted(case_sorter):
                  case("$c")
  
              # Record access types for this transition
              for request_type in request_types:
                  case('recordRequestType(${ident}_RequestType_${{request_type.ident}}, addr);')
  
              # Figure out if we stall
              stall = False
              for action in actions:
                  if action.ident == "z_stall":
                      stall = True
                      break
  
              if stall:
                  case('return TransitionResult_ProtocolStall;')
              else:
                  if self.TBEType != None and self.EntryType != None:
                      for action in actions:
                          case('${{action.ident}}(m_tbe_ptr, m_cache_entry_ptr, addr);')
                  elif self.TBEType != None:
                      for action in actions:
                          case('${{action.ident}}(m_tbe_ptr, addr);')
                  elif self.EntryType != None:
                      for action in actions:
                          case('${{action.ident}}(m_cache_entry_ptr, addr);')
                  else:
                      for action in actions:
                          case('${{action.ident}}(addr);')
                  case('return TransitionResult_Valid;')
  
              case = str(case)
  
              # Look to see if this transition code is unique.
              if case not in cases:
                  cases[case] = []
  
              cases[case].append(case_string)
  
          # Walk through all of the unique code blocks and spit out the
          # corresponding case statement elements
          for case,transitions in cases.items():
              # Iterative over all the multiple transitions that share
              # the same code
              for trans in transitions:
                  code('  case HASH_FUN($trans):')
              code('    $case\n')
  
          code('''
        default:
          panic("Invalid transition\\n"
                "%s time: %d addr: %#x event: %s state: %s\\n",
                name(), curCycle(), addr, event, state);
      }
  
      return TransitionResult_Valid;
  }
  ''')
          code.write(path, "%s_Transitions.cc" % self.ident)
  
  
      # **************************
      # ******* HTML Files *******
      # **************************
      def frameRef(self, click_href, click_target, over_href, over_num, text):
          code = self.symtab.codeFormatter(fix_newlines=False)
          code("""<A href=\"$click_href\" target=\"$click_target\" onmouseover=\"
      if (parent.frames[$over_num].location != parent.location + '$over_href') {
          parent.frames[$over_num].location='$over_href'
      }\">
      ${{html.formatShorthand(text)}}
      </A>""")
          return str(code)
  
      def writeHTMLFiles(self, path):
          # Create table with no row hilighted
          self.printHTMLTransitions(path, None)
  
          # Generate transition tables
          for state in self.states.values():
              self.printHTMLTransitions(path, state)
  
          # Generate action descriptions
          for action in self.actions.values():
              name = "%s_action_%s.html" % (self.ident, action.ident)
              code = html.createSymbol(action, "Action")
              code.write(path, name)
  
          # Generate state descriptions
          for state in self.states.values():
              name = "%s_State_%s.html" % (self.ident, state.ident)
              code = html.createSymbol(state, "State")
              code.write(path, name)
  
          # Generate event descriptions
          for event in self.events.values():
              name = "%s_Event_%s.html" % (self.ident, event.ident)
              code = html.createSymbol(event, "Event")
              code.write(path, name)
  
      def printHTMLTransitions(self, path, active_state):
          code = self.symtab.codeFormatter()
  
          code('''
  <HTML>
  <BODY link="blue" vlink="blue">
  
  <H1 align="center">${{html.formatShorthand(self.short)}}:
  ''')
          code.indent()
          for i,machine in enumerate(self.symtab.getAllType(StateMachine)):
              mid = machine.ident
              if i != 0:
                  extra = " - "
              else:
                  extra = ""
              if machine == self:
                  code('$extra$mid')
              else:
                  code('$extra<A target="Table" href="${mid}_table.html">$mid</A>')
          code.dedent()
  
          code("""
  </H1>
  
  <TABLE border=1>
  <TR>
    <TH> </TH>
  """)
  
          for event in self.events.values():
              href = "%s_Event_%s.html" % (self.ident, event.ident)
              ref = self.frameRef(href, "Status", href, "1", event.short)
              code('<TH bgcolor=white>$ref</TH>')
  
          code('</TR>')
          # -- Body of table
          for state in self.states.values():
              # -- Each row
              if state == active_state:
                  color = "yellow"
              else:
                  color = "white"
  
              click = "%s_table_%s.html" % (self.ident, state.ident)
              over = "%s_State_%s.html" % (self.ident, state.ident)
              text = html.formatShorthand(state.short)
              ref = self.frameRef(click, "Table", over, "1", state.short)
              code('''
  <TR>
    <TH bgcolor=$color>$ref</TH>
  ''')
  
              # -- One column for each event
              for event in self.events.values():
                  trans = self.table.get((state,event), None)
                  if trans is None:
                      # This is the no transition case
                      if state == active_state:
                          color = "#C0C000"
                      else:
                          color = "lightgrey"
  
                      code('<TD bgcolor=$color>&nbsp;</TD>')
                      continue
  
                  next = trans.nextState
                  stall_action = False
  
                  # -- Get the actions
                  for action in trans.actions:
                      if action.ident == "z_stall" or \
                         action.ident == "zz_recycleMandatoryQueue":
                          stall_action = True
  
                  # -- Print out "actions/next-state"
                  if stall_action:
                      if state == active_state:
                          color = "#C0C000"
                      else:
                          color = "lightgrey"
  
                  elif active_state and next.ident == active_state.ident:
                      color = "aqua"
                  elif state == active_state:
                      color = "yellow"
                  else:
                      color = "white"
  
                  code('<TD bgcolor=$color>')
                  for action in trans.actions:
                      href = "%s_action_%s.html" % (self.ident, action.ident)
                      ref = self.frameRef(href, "Status", href, "1",
                                          action.short)
                      code('  $ref')
                  if next != state:
                      if trans.actions:
                          code('/')
                      click = "%s_table_%s.html" % (self.ident, next.ident)
                      over = "%s_State_%s.html" % (self.ident, next.ident)
                      ref = self.frameRef(click, "Table", over, "1", next.short)
                      code("$ref")
                  code("</TD>")
  
              # -- Each row
              if state == active_state:
                  color = "yellow"
              else:
                  color = "white"
  
              click = "%s_table_%s.html" % (self.ident, state.ident)
              over = "%s_State_%s.html" % (self.ident, state.ident)
              ref = self.frameRef(click, "Table", over, "1", state.short)
              code('''
    <TH bgcolor=$color>$ref</TH>
  </TR>
  ''')
          code('''
  <!- Column footer->
  <TR>
    <TH> </TH>
  ''')
  
          for event in self.events.values():
              href = "%s_Event_%s.html" % (self.ident, event.ident)
              ref = self.frameRef(href, "Status", href, "1", event.short)
              code('<TH bgcolor=white>$ref</TH>')
          code('''
  </TR>
  </TABLE>
  </BODY></HTML>
  ''')
  
  
          if active_state:
              name = "%s_table_%s.html" % (self.ident, active_state.ident)
          else:
              name = "%s_table.html" % self.ident
          code.write(path, name)
  
  __all__ = [ "StateMachine" ]
  