text: #-------------------------------------------------------------------------------
  # signalvisitor.py
  #
  # Signal definition visitor
  #
  # Copyright (C) 2013, Shinya Takamaeda-Yamazaki
  # License: Apache 2.0
  #-------------------------------------------------------------------------------
  from __future__ import absolute_import
  from __future__ import print_function
  import sys
  import os
  
  from pyverilog.vparser.ast import *
  import pyverilog.utils.util as util
  import pyverilog.utils.verror as verror
  from pyverilog.utils.scope import ScopeLabel, ScopeChain
  from pyverilog.dataflow.dataflow import *
  from pyverilog.dataflow.visit import *
  from pyverilog.dataflow.optimizer import VerilogOptimizer
  import pyverilog.dataflow.reorder as reorder
  
  class SignalVisitor(NodeVisitor):
      def __init__(self, moduleinfotable, top):
          self.moduleinfotable = moduleinfotable
          self.top = top
          self.frames = FrameTable()
          self.labels = Labels()
          self.optimizer = VerilogOptimizer({}, {})
  
          # set the top frame of top module
          self.stackInstanceFrame(top, top)
  
      ################################################################################
      def getFrameTable(self):
          return self.frames
  
      ################################################################################
      def start_visit(self):
          return self.visit(self.moduleinfotable.getDefinition(self.top))
  
      def visit_Input(self, node):
          self.frames.addSignal(node)
  
      def visit_Output(self, node):
          self.frames.addSignal(node)
  
      def visit_Inout(self, node):
          self.frames.addSignal(node)
  
      def visit_Reg(self, node):
          self.frames.addSignal(node)
  
      def visit_Wire(self, node):
          self.frames.addSignal(node)
  
      def visit_Supply(self, node):
          self.frames.addSignal(node)
  
      def visit_RegArray(self, node):
          self.frames.addSignal(node)
  
      def visit_WireArray(self, node):
          self.frames.addSignal(node)
  
      def visit_Tri(self, node):
          self.frames.addSignal(node)
  
      def visit_Integer(self, node):
          self.frames.addSignal(node)
  
      def visit_Parameter(self, node):
          self.frames.addConst(node)
          name = self.frames.getCurrent() + ScopeLabel(node.name, 'signal')
          if not self.hasConstant(name):
              value = self.optimize(self.getTree(node.value, self.frames.getCurrent()))
              self.setConstant(name, value)
  
      def visit_Localparam(self, node):
          self.frames.addConst(node)
          name = self.frames.getCurrent() + ScopeLabel(node.name, 'signal')
          if not self.hasConstant(name):
              value = self.optimize(self.getTree(node.value, self.frames.getCurrent()))
              self.setConstant(name, value)
  
      def visit_Genvar(self, node):
          self.frames.addConst(node)
          name = self.frames.getCurrent() + ScopeLabel(node.name, 'signal')
          if not self.hasConstant(name):
              value = DFEvalValue(0)
              self.setConstant(name, value)
  
      def visit_Function(self, node):
          self.frames.addFunction(node)
          self.frames.setFunctionDef()
          self.generic_visit(node)
          self.frames.unsetFunctionDef()
  
      def visit_Task(self, node):
          self.frames.addTask(node)
          self.frames.setTaskDef()
          self.generic_visit(node)
          self.frames.unsetTaskDef()
  
      def visit_Initial(self, node):
          pass
          #label = self.labels.get( self.frames.getLabelKey('initial') )
          #current = self.frames.addFrame(ScopeLabel(label, 'initial'),
          #                               generate=self.frames.isGenerate(),
          #                               initial=True)
          #self.generic_visit(node)
          #self.frames.setCurrent(current)
  
      def visit_InstanceList(self, node):
          for i in node.instances:
              self.visit(i)
  
      def visit_Instance(self, node):
          if node.array: return self._visit_Instance_array(node)
          nodename = node.name
          return self._visit_Instance_body(node, nodename)
  
      def _visit_Instance_array(self, node):
          if node.name == '':
              raise verror.FormatError("Module %s requires an instance name" % node.module)
  
          current = self.frames.getCurrent()
          msb = self.optimize(self.getTree(node.array.msb, current)).value
          lsb = self.optimize(self.getTree(node.array.lsb, current)).value
  
          for i in range(lsb, msb+1):
              nodename = node.name + '_' + str(i)
              self._visit_Instance_body(node, nodename)
  
      def _visit_Instance_body(self, node, nodename):
          if node.module in primitives: return self._visit_Instance_primitive(node)
  
          if nodename == '':
              raise verror.FormatError("Module %s requires an instance name" % node.module)
  
          current = self.stackInstanceFrame(nodename, node.module)
  
          self.setInstanceSimpleConstantTerms()
  
          scope = self.frames.getCurrent()
  
          paramnames = self.moduleinfotable.getParamNames(node.module)
          for paramnames_i, param in enumerate(node.parameterlist):
              paramname = paramnames[paramnames_i] if param.paramname is None else param.paramname
              if paramname not in paramnames:
                  raise verror.FormatError("No such parameter: %s in %s" %
                                           (paramname, nodename))
              value = self.optimize(self.getTree(param.argname, current))
              name, definition = self.searchConstantDefinition(scope, paramname)
              self.setConstant(name, value)
  
          self.setInstanceConstants()
          self.setInstanceConstantTerms()
  
          self.visit(self.moduleinfotable.getDefinition(node.module))
          self.frames.setCurrent(current)
  
      def _visit_Instance_primitive(self, node):
          pass
  
      def visit_Always(self, node):
          label = self.labels.get( self.frames.getLabelKey('always') )
          current = self.frames.addFrame(ScopeLabel(label, 'always'),
                                         generate=self.frames.isGenerate(),
                                         always=True)
          self.generic_visit(node)
          self.frames.setCurrent(current)
  
      def visit_IfStatement(self, node):
  
          if (self.frames.isGenerate() and
              not self.frames.isAlways() and not self.frames.isInitial() and
              not self.frames.isFunctioncall() and not self.frames.isTaskcall() and
              not self.frames.isFunctiondef() and not self.frames.isTaskdef()):
              # generate-if statement
              current = self.frames.getCurrent()
              tree = self.getTree(node.cond, current)
              rslt = self.optimize(tree)
              if not isinstance(rslt, DFEvalValue):
                  raise verror.FormatError("Can not resolve generate-if condition")
              if rslt.value > 0:
                  label = self._if_true(node)
              else:
                  label = self.labels.get( self.frames.getLabelKey('if') )
                  self._if_false(node, label)
              return
  
          label = self._if_true(node)
          self._if_false(node, label)
  
      def _toELSE(self, label):
          return label + '_ELSE'
  
      def _if_true(self, node):
          if node.true_statement is None: return None
          label = self.labels.get( self.frames.getLabelKey('if') )
          current = self.frames.addFrame(ScopeLabel(label, 'if'),
                                         frametype='ifthen',
                                         condition=node.cond,
                                         functioncall=self.frames.isFunctioncall(),
                                         taskcall=self.frames.isTaskcall(),
                                         generate=self.frames.isGenerate(),
                                         always=self.frames.isAlways(),
                                         initial=self.frames.isInitial())
          self.visit(node.true_statement)
          self.frames.setCurrent(current)
          return label
  
      def _if_false(self, node, label):
          if node.false_statement is None: return
          label = self._toELSE(label)
          current = self.frames.addFrame(ScopeLabel(label, 'if'),
                                         frametype='ifelse',
                                         condition=node.cond,
                                         functioncall=self.frames.isFunctioncall(),
                                         taskcall=self.frames.isTaskcall(),
                                         generate=self.frames.isGenerate(),
                                         always=self.frames.isAlways(),
                                         initial=self.frames.isInitial())
          self.visit(node.false_statement)
          self.frames.setCurrent(current)
          return label
  
      def visit_CaseStatement(self, node):
          start_frame = self.frames.getCurrent()
          self._case(node.comp, node.caselist)
          self.frames.setCurrent(start_frame)
  
      def visit_CasexStatement(self, node):
          return self.visit_CaseStatement(node)
          
      def _case(self, comp, caselist):
          if len(caselist) == 0: return
          case = caselist[0]
          cond = IntConst('1')
          if case.cond is not None:
              if len(case.cond) > 1:
                  cond = Eq(comp, case.cond[0])
                  for c in case.cond[1:]:
                      cond = Lor(cond, Eq(comp, c))
              else:
                  cond = Eq(comp, case.cond[0])
          label = self.labels.get( self.frames.getLabelKey('if') )
          current = self.frames.addFrame(ScopeLabel(label, 'if'),
                                         frametype='ifthen',
                                         condition=cond,
                                         functioncall=self.frames.isFunctioncall(),
                                         taskcall=self.frames.isTaskcall(),
                                         generate=self.frames.isGenerate(),
                                         always=self.frames.isAlways(),
                                         initial=self.frames.isInitial())
          if case.statement is not None: self.visit(case.statement)
          self.frames.setCurrent(current)
          if len(caselist) == 1: return
          label = self._toELSE(label)
          current = self.frames.addFrame(ScopeLabel(label, 'if'),
                                         frametype='ifelse',
                                         condition=cond,
                                         functioncall=self.frames.isFunctioncall(),
                                         taskcall=self.frames.isTaskcall(),
                                         generate=self.frames.isGenerate(),
                                         always=self.frames.isAlways(),
                                         initial=self.frames.isInitial())
          self._case(comp, caselist[1:])
  
      def visit_ForStatement(self, node):
          ## pre-statement
          current = self.frames.getCurrent()
          pre_right = self.getTree(node.pre.right, current)
          pre_right_value = self.optimize(pre_right)
          loop = pre_right_value.value
          self.frames.setForPre()
          self.visit(node.pre)
          self.frames.unsetForPre()
          label = self.labels.get( self.frames.getLabelKey('for') )
          #loop = 0
          while True:
              ## cond-statement
              current = self.frames.getCurrent()
              tree = self.getTree(node.cond, current)
              rslt = self.optimize(tree)
              if not isinstance(rslt, DFEvalValue):
                  raise verror.FormatError(("Can not process the for-statement. "
                                            "for-condition should be evaluated statically."))
              # loop termination
              if rslt.value <= 0: break
  
              ## main-statement
              current = self.frames.addFrame(ScopeLabel(label, 'for', loop),
                                             frametype='for',
                                             functioncall=self.frames.isFunctioncall(),
                                             taskcall=self.frames.isTaskcall(),
                                             generate=self.frames.isGenerate(),
                                             always=self.frames.isAlways(),
                                             initial=self.frames.isInitial(),
                                             loop=loop, loop_iter=self.frames.getForIter())
              self.visit(node.statement)
              self.frames.setCurrent(current)
  
              ## post-statement
              current = self.frames.getCurrent()
              post_right = self.getTree(node.post.right, current)
              post_right_value = self.optimize(post_right)
              loop = post_right_value.value
              self.frames.setForPost()
              self.visit(node.post)
              self.frames.unsetForPost()
              #loop += 1
  
      def visit_WhileStatement(self, node):
          pass
  
      def visit_GenerateStatement(self, node):
          label = self.labels.get( self.frames.getLabelKey('generate') )
          current = self.frames.addFrame(ScopeLabel(label, 'generate'),
                                         generate=True)
          self.generic_visit(node)
          self.frames.setCurrent(current)
  
      def visit_Block(self, node):
          label = None
          if node.scope is not None:
              label = node.scope
          else:
              label = self.labels.get( self.frames.getLabelKey('block') )
          current = self.frames.addFrame(ScopeLabel(label, 'block'),
                                         frametype='block',
                                         functioncall=self.frames.isFunctioncall(),
                                         taskcall=self.frames.isTaskcall(),
                                         generate=self.frames.isGenerate(),
                                         always=self.frames.isAlways(),
                                         initial=self.frames.isInitial())
          self.generic_visit(node)
          self.frames.setCurrent(current)
  
      def visit_Assign(self, node):
          pass
  
      def visit_BlockingSubstitution(self, node):
          if self.frames.isForpre() or self.frames.isForpost():
              current = self.frames.getCurrent()
              name, definition = self.searchConstantDefinition(current,
                                                               node.left.var.name)
              value = self.optimize(self.getTree(node.right.var, current))
              self.setConstant(name, value)
              self.frames.setForIter(name)
  
      def visit_NonblockingSubstitution(self, node):
          pass
  
      ############################################################################
      def optimize(self, node):
          return self.optimizer.optimize(node)
  
      def stackInstanceFrame(self, instname, modulename):
          current = self.frames.addFrame(ScopeLabel(instname, 'module'), module=True,
                                         modulename=modulename)
          self.frames.updateSignal(self.moduleinfotable.getSignals(modulename))
          self.frames.updateConst(self.moduleinfotable.getConsts(modulename))
          return current
  
      def setInstanceSimpleConstantTerms(self):
          current = self.frames.getCurrent()
          for name, definitions in self.frames.getConsts(current).items():
              if len(definitions) > 1:
                  raise verror.FormatError("Multiple definitions for Constant")
              for definition in definitions:
                  simple_definition = copy.deepcopy(definition)
                  if simple_definition.width is not None:
                      simple_definition.width.msb = None
                      simple_definition.width.lsb = None
                  term = self.makeConstantTerm(name, simple_definition, current)
                  self.setConstantTerm(name, term)
  
      def setInstanceConstantTerms(self):
          current = self.frames.getCurrent()
          for name, definitions in self.frames.getConsts(current).items():
              if len(definitions) > 1:
                  raise verror.FormatError("Multiple definitions for Constant")
              for definition in definitions:
                  term = self.makeConstantTerm(name, definition, current)
                  self.setConstantTerm(name, term)
  
      def setInstanceConstants(self):
          current = self.frames.getCurrent()
  
          all_passed = False
          while not all_passed:
              all_passed = True
              for name, definitions in self.frames.getConsts(current).items():
                  if len(definitions) > 1:
                      raise verror.FormatError("Multiple definitions for Constant")
  
                  if self.hasConstant(name):
                      continue
  
                  for definition in definitions:
                      if isinstance(definition, Genvar):
                          continue
                      value = self.optimize(self.getTree(definition.value, current))
                      if not isinstance(value, DFEvalValue):
                          all_passed = False
                          continue
  
                      self.setConstant(name, value)
  
      def setConstant(self, name, value):
          self.optimizer.setConstant(name, value)
  
      def getConstant(self, name):
          return self.optimizer.getConstant(name)
  
      def hasConstant(self, name):
          return self.optimizer.hasConstant(name)
  
      def setConstantTerm(self, name, term):
          self.optimizer.setTerm(name, term)
  
      def hasConstantTerm(self, name):
          self.optimizer.hasTerm(name)
  
      ############################################################################
      def toScopeChain(self, blocklabel):
          scopelist = []
          for b in blocklabel.labellist:
              if b.loop is not None:
                  loop = self.optimize(b.loop)
                  if not isinstance(loop, DFEvalValue):
                      raise verror.FormatError('Loop iterator should be constant')
                  scopelist.append( ScopeLabel(b.name, 'for', loop) )
              scopelist.append( ScopeLabel(b.name, 'any') )
          return ScopeChain( scopelist )
  
      def searchScopeConstantValue(self, blocklabel, name):
          currentmodule = self.frames.getCurrentModuleScopeChain()
          localchain = currentmodule[-1:] + self.toScopeChain(blocklabel)
          matchedchain = self.frames.searchMatchedScopeChain(currentmodule, localchain)
          varname = currentmodule[:-1] + matchedchain + ScopeLabel(name, 'signal')
          const = self.getConstant(varname)
          return const
  
      #def searchScopeConstantDefinition(self, blocklabel, name):
      #    currentmodule = self.frames.getCurrentModuleScopeChain()
      #    localchain = currentmodule[-1:] + self.toScopeChain(blocklabel)
      #    matchedchain = self.frames.searchMatchedScopeChain(currentmodule, localchain)
      #    constdef = self.frames.getConstantDefinition(matchedchain, name)
      #    return constdef
  
      def searchConstantDefinition(self, key, name):
          foundkey, founddef = self.frames.searchConstantDefinition(key, name)
          if foundkey is not None:
              return foundkey + ScopeLabel(name, 'signal'), founddef
          foundkey, founddef = self.frames.searchSignalDefinition(key, name)
          if foundkey is not None:
              return foundkey + ScopeLabel(name, 'signal'), founddef
          if foundkey is None:
              raise verror.DefinitionError('constant value not found: %s' % name)
  
      def searchConstantValue(self, key, name):
          foundkey, founddef = self.searchConstantDefinition(key, name)
          const = self.getConstant(foundkey)
          return const
  
      ############################################################################
      def makeConstantTerm(self, name, node, scope):
          termtype = node.__class__.__name__
          termtypes = set([termtype])
          msb = DFIntConst('31') if node.width is None else self.makeDFTree(node.width.msb, scope)
          lsb = DFIntConst('0') if node.width is None else self.makeDFTree(node.width.lsb, scope)
          return Term(name, termtypes, msb, lsb)
  
      def getTree(self, node, scope):
          expr = node.var if isinstance(node, Rvalue) else node
          return self.makeDFTree(expr, scope)
  
      def makeDFTree(self, node, scope):
          if isinstance(node, str):
              return self.searchConstantValue(scope, node)
  
          if isinstance(node, Identifier):
              if node.scope is not None:
                  const = self.searchScopeConstantValue(node.scope, node.name)
                  return const
              return self.searchConstantValue(scope, node.name)
  
          if isinstance(node, IntConst):
              return DFIntConst(node.value)
  
          if isinstance(node, FloatConst):
              return DFFloatConst(node.value)
  
          if isinstance(node, StringConst):
              return DFStringConst(node.value)
  
          if isinstance(node, Cond):
              true_df = self.makeDFTree(node.true_value, scope)
              false_df = self.makeDFTree(node.false_value, scope)
              cond_df = self.makeDFTree(node.cond, scope)
              if isinstance(cond_df, DFBranch):
                  return reorder.insertCond(cond_df, true_df, false_df)
              return DFBranch(cond_df, true_df, false_df)
  
          if isinstance(node, UnaryOperator):
              right_df = self.makeDFTree(node.right, scope)
              if isinstance(right_df, DFBranch):
                  return reorder.insertUnaryOp(right_df, node.__class__.__name__)
              return DFOperator((right_df,), node.__class__.__name__)
  
          if isinstance(node, Operator):
              left_df = self.makeDFTree(node.left, scope)
              right_df = self.makeDFTree(node.right, scope)
              if isinstance(left_df, DFBranch) or isinstance(right_df, DFBranch):
                  return reorder.insertOp(left_df, right_df, node.__class__.__name__)
              return DFOperator((left_df, right_df,), node.__class__.__name__)
  
          if isinstance(node, Partselect):
              var_df = self.makeDFTree(node.var, scope)
              msb_df = self.makeDFTree(node.msb, scope)
              lsb_df = self.makeDFTree(node.lsb, scope)
  
              if isinstance(var_df, DFBranch):
                  return reorder.insertPartselect(var_df, msb_df, lsb_df)
              return DFPartselect(var_df, msb_df, lsb_df)
  
          if isinstance(node, Pointer):
              var_df = self.makeDFTree(node.var, scope)
              ptr_df = self.makeDFTree(node.ptr, scope)
  
              if (isinstance(var_df, DFTerminal) and
                  (signaltype.isRegArray(self.getTermtype(var_df.name)) or
                   signaltype.isWireArray(self.getTermtype(var_df.name)))):
                  return DFPointer(var_df, ptr_df)
              return DFPartselect(var_df, ptr_df, copy.deepcopy(ptr_df))
  
          if isinstance(node, Concat):
              nextnodes = []
              for n in node.list:
                  nextnodes.append(self.makeDFTree(n, scope))
              for n in nextnodes:
                  if isinstance(n, DFBranch):
                      return reorder.insertConcat(tuple(nextnodes))
              return DFConcat(tuple(nextnodes))
  
          if isinstance(node, Repeat):
              nextnodes = []
              times = self.optimize(self.getTree(node.times, scope)).value
              value = self.makeDFTree(node.value, scope)
              for i in range(int(times)):
                  nextnodes.append(copy.deepcopy(value))
              return DFConcat(tuple(nextnodes))
  
          if isinstance(node, SystemCall):
              if node.syscall == 'unsigned':
                  return self.makeDFTree(node.args[0])
              if node.syscall == 'signed':
                  return self.makeDFTree(node.args[0])
              return DFIntConst('0')
  
          raise verror.FormatError("unsupported AST node type: %s %s" %
                                   (str(type(node)), str(node)))
  
  