#!/usr/bin/python

# This file is part of the Coriolis Project.
# Copyright (C) Laboratoire LIP6 - Departement ASIM
# Universite Pierre et Marie Curie
#
# Main contributors :
#        Christophe Alexandre   <Christophe.Alexandre@lip6.fr>
#        Sophie Belloeil             <Sophie.Belloeil@lip6.fr>
#        Hugo Clement                   <Hugo.Clement@lip6.fr>
#        Jean-Paul Chaput           <Jean-Paul.Chaput@lip6.fr>
#        Damien Dupuis                 <Damien.Dupuis@lip6.fr>
#        Christian Masson           <Christian.Masson@lip6.fr>
#        Marek Sroka                     <Marek.Sroka@lip6.fr>
# 
# The  Coriolis Project  is  free software;  you  can redistribute  it
# and/or modify it under the  terms of the GNU General Public License
# as published by  the Free Software Foundation; either  version 2 of
# the License, or (at your option) any later version.
# 
# The  Coriolis Project is  distributed in  the hope  that it  will be
# useful, but WITHOUT ANY WARRANTY; without even the implied warranty
# of MERCHANTABILITY  or FITNESS FOR  A PARTICULAR PURPOSE.   See the
# GNU General Public License for more details.
# 
# You should have  received a copy of the  GNU General Public License
# along with the Coriolis Project;  if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA
#
# License-Tag
# Authors-Tag
# ===================================================================
#
# x-----------------------------------------------------------------x 
# |                                                                 |
# |                   C O R I O L I S                               |
# |        S t r a t u s   -  Netlists/Layouts Description          |
# |                                                                 |
# |  Author      :                    Sophie BELLOEIL               |
# |  E-mail      :       Sophie.Belloeil@asim.lip6.fr               |
# | =============================================================== |
# |  Py Module   :       "./st_instance.py"                         |
# | *************************************************************** |
# |  U p d a t e s                                                  |
# |                                                                 |
# x-----------------------------------------------------------------x



import CRL
from Hurricane        import *

from st_model         import Model, MODELMAP
from st_getrealmodel  import GetRealModel, InitBV

import sys, re, types, os

## Alliance's libraries ##
SXLIB   = "_x[0-8]"
PXLIB   = "_px"
RAMLIB  = "ram_"
RFLIB   = "rf_"
RF2LIB  = "rf2_"
ROMLIB  = "rom_"
DPSXLIB = "dp_.*_x[1-8]"

## Puts MBK_CATA_LIB at the begining of PYTHONPATH ##
#cata_lib = os.environ['MBK_CATA_LIB']
#chaine = re.search ( "([^:]*):(.*)", cata_lib )
#while chaine :
#  rep      = chaine.group ( 1 )
#  cata_lib = chaine.group ( 2 )
#
#  sys.path.insert ( 0, rep )
#
#  chaine = re.search ( "([^:]*):(.*)", cata_lib )

## Class of nets ##
NET = ( "st_net.SignalIn", "st_net.SignalOut", "st_net.SignalInOut" \
      , "st_net.SignalUnknown", "st_net.TriState" \
      , "st_net.CkIn", "st_net.SignalCk" \
      , "st_net.Signal", "st_net.Sig" \
      , "st_net.VddIn", "st_net.VssIn" \
      , "st_net.VddInFromHur", "st_net.VssInFromHur" \
      , "st_net.SignalVdd" , "st_net.SignalVss" \
      )
ALIM_NET = ( "st_net.VddIn", "st_net.VssIn" \
           , "st_net.VddInFromHur", "st_net.VssInFromHur"
           )

################
##### Inst #####
################
class Inst :
  
  ################  
  ##### Init #####
  ################
  def __init__ ( self, model, name = None, map = {}, cell = None ) :
    global MODELMAP, CELLS, FRAMEWORK
    
    from st_model   import MODELMAP, CELLS, FRAMEWORK
    from util_Place import UNPLACED, NOSYM
    
    if not cell : cell = CELLS[-1]

    ##### Virtual library #####
    global BV
    from st_parser import BV
    if BV == [] : InitBV()
    
    if model in BV : model, self._inout = GetRealModel ( model )

    ##### Attributes of the instance #####
    self._model        = model.lower()
    self._st_cell      = cell
    self._hur_instance = None
    self._plac         = UNPLACED
    self._map          = map

    ##### Name of the instance ######
    if not name :
      name = "%s_i%d" % ( self._model, cell._NB_INST )
      cell._NB_INST += 1
    self._name = name
    
    ##### The instance is put in the instances' list of the cell #####
    cell._st_insts.append ( self )
    
    ##### Errors #####
    # Error : if the model is not a string
    if type ( model ) != types.StringType :
      err = "\n[Stratus ERROR] Inst : the model must be described in a string.\n"
      raise Exception ( err )
    # Warning : the model can not contain capitalized letters
    if re.search ( "[A-Z]", model ) :
      print "[Stratus Warning] Inst : Upper case letters are not supported, the name", model, "is lowered."
      model = model.lower()
    # Error : spaces are forbidden
    if re.search ( " ", model ) :
      err = "\n[Stratus ERROR] Inst : " + name + " the name of the model \"" + model + "\" can not contain a space.\n"
      raise Exception ( err )
    if name :
      if re.search ( " ", name ) :
        err = "\n[Stratus ERROR] Inst : \"" + name + "\" the name of the instance can not contain a space.\n"
        raise Exception ( err )
      # Warning : the name can not contain capitalized letters
      if re.search ( "[A-Z]", name ) :
        print "[Stratus Warning] : Upper case letters are not supported, the name", name, "is lowered."
        name = name.lower ()
    
    # Error : if map[pin] is not a net
    if map :
      for pin in map :
        if str ( map[pin].__class__ ) not in NET :
          err = "\n[Stratus ERROR] Inst : \"" + name + "\" one argument is not a net : "
          err += "pin is : " + pin + " and is associated to : "
          if map[pin] : err += str(map[pin])
          else        : err += "None"
          err += "\n"
          raise Exception ( err )

    ##### MasterCell #####
    self._hur_masterCell = FRAMEWORK.getCell ( self._model, CRL.Catalog.State.Views )
    
    self._st_masterCell  = None
    for c in CELLS[0]._underCells :
      if self._model == c._name :
        self._st_masterCell = c
        break
#    if not( self._hur_masterCell or self._st_masterCell ) : print "\n[Stratus Warning] Inst : no master cell found for instance " + self._name

    # Creation of the hurricane instance 
    if CELLS[0]._hur_plug : self.create_hur_inst ( model )
                                         
  ##############                         
  ### Delete ###                         
  ##############                         
  def Delete ( self ) :                  
    # Erasement of the references to the  instance
    for i in range ( len ( self._st_cell._st_insts ) ) :
      if self._st_cell._st_insts[i] == self :
        del self._st_cell._st_insts[i]
        break
    # Erasement of the hurricane instance associated
    if self._hur_instance : self._hur_instance.Delete()

  ##############################################
  ##### Creation of the hurricane instance #####
  ##############################################
  def create_hur_inst ( self, model ) :
    if not self._hur_masterCell :
     #self._hur_masterCell = FRAMEWORK.getCell ( self._model, CRL.Catalog.State.Views )
      self._hur_masterCell = FRAMEWORK.getCell ( model, CRL.Catalog.State.Views )

    if not self._hur_masterCell :
      err = "\n[Stratus ERROR] HurricanePlug : Problem of master cell " + model + ".\nCheck model name and/or .environment.alliance.xml configuration file.\n"
      raise Exception ( err )

    if not self._st_masterCell :
      if MODELMAP.has_key ( str ( self._hur_masterCell ) ) :
        self._st_masterCell = MODELMAP[str ( self._hur_masterCell )]
      else :
        self._st_masterCell = Model ( str ( self._hur_masterCell.getName() ), hurCell = self._hur_masterCell )

    if not self._st_cell._hur_cell :
      err = "\n[Stratus ERROR] HurricanePlug : Problem of hurricane cell.\nTry to contact Coriolis team.\n"
      raise Exception ( err )
      
    inst = Instance.create ( self._st_cell._hur_cell
                           , self._name
                           , self._hur_masterCell
                           )
    # The hurricane instance is connected to the stratus instance
    self._hur_instance = inst
    
    ##### Connection #####
    ##### Function to be applied on each pin 
    def connectPin ( pin ) : 
      # Error : if there is a space in the name of the pin (usually done at the end of the pin ...)
      if re.search ( " ", pin ) :
        err = "\n[Stratus ERROR] Inst : " + self._name + " the keys of the connection map can not contain a space : \"" + pin + "\".\n"
        raise Exception ( err )
        
      # Error : if the net to connect does not have it's arity defined
      if not ( mapNet._arity ) :
        err = "\n[Stratus ERROR] Inst : " + self._name + " : the size of " + mapNet._name + " has not been defined properly.\n"
        raise Exception ( err )
        
      wrong_pin  = 1
      for net in self._hur_masterCell.getExternalNets():
        if ( pin == str(net.getName()) ) or re.search ( "%s\(" % pin, str(net.getName()) ) :
          wrong_pin = 0
          break

      # Error : if the pin doesn't exist in the model's ports
      if wrong_pin :
        err = "\n[Stratus ERROR] Inst : " + str(self._name) + " : port " + pin \
               + " does not exist in model " + str(self._hur_masterCell.getName()) + ". Pins are :"
        for pin in self._hur_masterCell.getExternalNets():
          err += str ( pin.getName() )
          err += ","
        sea = re.search ( "(.*),$", err )
        if not sea : err += "No pins found ..."
        else       : err  = sea.group(1)
        raise Exception ( err )
        
      # Error : if the arities of the nets don't correspond
      tabPins = self._st_masterCell._st_ports + self._st_masterCell._st_cks + self._st_cell._st_vdds + self._st_cell._st_vsss
      for net in tabPins :
        if net._name == pin :
          if net._arity != mapNet._arity :
            err = "\n[Stratus ERROR] Inst : " + str(self._name) + " : The arity of the net " + mapNet._name + " " + str(mapNet._arity) \
                + " does not correspond to the arity of the port of the cell : "+ net._name + " " + str(net._arity) + ".\n"
            raise Exception ( err )
 
      # If the port of the masterCell doesn't have a LSB which is 0
      lsb = 0
      if mapNet._arity > 1 :
        netBus = None
        while not ( netBus ) :
          netBus = self._hur_masterCell.getNet ( pin.lower() + "(" + str ( lsb ) + ")" )
          lsb += 1
          if lsb > 20 : # value chosen in order to avoid infinite loop in case of a problem (may be higher)
            err = "\n[Stratus ERROR] Inst : " + str(self._name) + " : Probem of map, check the arities of your nets.\n"
            raise Exception ( err )
        if lsb : lsb -= 1
            
      ### Connection ###
      if mapNet._real_net : realNet = mapNet._real_net
      else                : realNet = mapNet

      # range 1: given the indice's net
      nbit1 = mapNet._ind - realNet._ind
      # range 2: deal with reverse nets
      if ( '_reverse' in mapNet.__dict__ ) and ( mapNet._reverse ) :
        nbit2 = nbit1 - mapNet._arity
        pas   = -1
      else :
        nbit2 = nbit1 + mapNet._arity
        pas   =  1
      
      j = 0
      for i in range ( nbit1, nbit2, pas ) :
        # If the net has to be concatened
        if ( realNet._to_cat ) and ( realNet._to_cat[i] ) :
          net = realNet._to_cat[i][0]
          bit = realNet._to_cat[i][1]
          if net._real_net : net = net._real_net
          
          # If the net which is concatened is an alias
          if ( net._alias ) and ( net._alias[bit] ) :
            netA = net._alias[bit].keys()[0]
            bitA = net._alias[bit][netA]
            if net._real_net : netA = netA._real_net
          
            hurNet = netA._hur_net[bitA-net._ind]
          else :
            hurNet = net._hur_net[bit-net._ind]
          
        # If the net is an alias
        elif ( realNet._alias ) and ( realNet._alias[i] ) :
          net = realNet._alias[i].keys()[0]
          bit = realNet._alias[i][net]
          if net._real_net : net = net._real_net
          
          hurNet = net._hur_net[bit+net._ind]

        else :
          hurNet = realNet._hur_net[i]

        if mapNet._arity == 1 : tempNet = self._hur_masterCell.getNet ( pin.lower() )
        else                  : tempNet = self._hur_masterCell.getNet ( pin.lower() + "(" + str(j+lsb) + ")" )
        j += 1
      
        if not ( tempNet ) :
          err = "\n[Stratus ERROR] Inst : Problem in map. Check that the arities of your nets are correct.\n"
          raise Exception ( err )
        
        plug = self._hur_instance.getPlug ( tempNet )
        plug.setNet ( hurNet )
       
        # In order to see the ring
        if str ( realNet.__class__ ) not in ALIM_NET : CRL.createPartRing ( self._st_cell._hur_cell, hurNet.getName() )

    ##### Loop on each pin
    for pin in self._map :
      mapNet = self._map[pin]

      ### Virtual library ###
      if "_inout" in self.__dict__ :
        import types
        if type ( self._inout[pin] ) == types.ListType :
          for realpin in self._inout[pin] :
            connectPin ( realpin )
        else :
          realpin = self._inout[pin]
          connectPin ( realpin )
      ### Other ###
      else :
        connectPin ( pin )
    
    # Error message if the connection is not correct (detection before vst driver)
    # Not for vdd/vss in case of utilisation of SetGlobal
    # The detection is done with vst driver in this case ...
    for plug in self._hur_instance.getUnconnectedPlugs():
      if plug.getMasterNet().getType() not in ( TypePOWER, TypeGROUND ) :
        name = str(plug.getMasterNet().getName())
        chaine = re.search ( "(.*)\(", name )
        if chaine : name = chaine.group(1)
    
        err = "\n[Stratus ERROR] Inst : plug " + name + " of instance " + self._name + " must be connected.\n"
        err += '               (raw name: <%s>)\n' % str(plug.getMasterNet().getName())
        raise Exception ( err )

  ##############
  ### Prints ###
  ##############
  def printInstance ( self ) :
    print "  => model", self._model
    print "  => map"
    for pin in self._map :
      n = self._map[pin]
      if n._to_merge : n = n._to_merge[0][0]
      print "    ", pin, "->", n._name

  def printMap ( self ) :
    print "Map:", self._name
    for pin in self._map :
      print "  ", pin, self._map[pin]._name

#########################
#### SetCurrentModel ####
#########################
def SetCurrentModel ( instance ) :
  global CELLS
  from st_model import CELLS

  if not instance :
    err = "\n[Stratus ERROR] SetCurrentModel : argument given does not exist.\n"
    raise Exception ( err )
  cell = instance._st_masterCell
  
  if not cell :
    err = "\n[Stratus ERROR] SetCurrentModel : cannot find model for instance " + str ( getName ( instance ) ) + ".\n"
    raise Exception ( err )

  CELLS.append ( cell )
    
  return cell
