#!/usr/bin/python
#
# This file is part of the Coriolis Software.
# Copyright (c) UPMC/LIP6 2008-2012, All Rights Reserved
#
# +-----------------------------------------------------------------+ 
# |                   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_model.py"                            |
# +-----------------------------------------------------------------+


import re
import types
import string
import Cfg
import CRL
import Viewer
from   Hurricane  import *


FRAMEWORK = None
EDITOR    = None

MODELMAP  = {}
CELLS     = []

STRATUS   = 100
LOGICAL   = CRL.Catalog.State.Logical
PHYSICAL  = CRL.Catalog.State.Physical
VIEWS     = CRL.Catalog.State.Views

def setEditor ( editor ):
  global EDITOR
  
  EDITOR = editor
  return

#######################
##### Class Model #####
#######################
class Model :
 
  ##########################
  ##### Initialisation #####
  ##########################
  def __init__ ( self, nom, param = {}, hurCell = None, hurricane_plug = True ) :
    global FRAMEWORK, CELLS

    # Look up the editor
    if globals().has_key ( "__editor" ) : setEditor ( __editor )

    self._name         = nom
    self._param        = param
    self.pat           = None

    self._hur_cell     = hurCell

    self._st_insts     = [] # Tab : list of the instances
    self._st_ports     = [] # Tab : list of the nets
    self._st_sigs      = [] # Tab : list of the nets
    
    self._st_partsigs  = [] # Tab : list of the partial nets
    self._st_merge     = [] # Tab : list of the nets to merge, in order to have them in the right order

    self._hierarchy    = 0  # Modified in ModelCreation
    
    self._TAB_NETS_OUT = []
    self._TAB_NETS_CAT = []
    self._NB_INST      = 0
    
    # List of the generators used for overload
    self._and          = 'A2'
    self._or           = 'O2'
    self._xor          = 'Xr2'
    self._not          = 'Inv'
    self._buff         = 'Buf'
    self._reg          = 'Sff1'
    # Stratus generators
    self._mux          = 'Smux'
    self._shift        = 'Shifter'
    self._comp         = 'Comp'
    # Arithmetic generators
    self._add          = 'Slansky'
    self._sub          = 'Sub'
    self._mult         = 'Multiplier'
    self._div          = None

    self._signed       = True
    self._extended     = False

    self._model_map    = {}
    
    # List of the under-cells of a cell
    self._underCells   = {}
#    self._tabPlacement = []
    # Instance reference for placement
    self._insref       = None

    # Place and Route
    self._nb_alims_verticales    = 0  
    self._nb_pins                = 0  # to number the pins of the core  
    self._nb_vdd_pins            = 0  # to number the pins vdd of the core  
    self._nb_vss_pins            = 0  # to number the pins vss of the core 
    self.standard_instances_list = []
    self.pad_north               = [] # list to save the pads names 
    self.pad_south               = []
    self.pad_east                = []
    self.pad_west                = []
    
    # Creation of the database 
    if not FRAMEWORK :
     #Initialize()
      FRAMEWORK = CRL.AllianceFramework.get()
    
    self._st_vdds  = []
    self._st_vsss  = []
    
    if hurCell : 
      from st_net import VddInFromHur
      from st_net import VssInFromHur
      
      try:
        netVdd = iter(hurCell.getPowerNets()).next()
        self._st_vdds.append ( VddInFromHur ( netVdd ) )
      except StopIteration:
        print "[Stratus Warning] : Cell", self._name, "does not have a vdd port."
        pass
      try:
        netVss = iter(hurCell.getGroundNets()).next()
        self._st_vsss.append ( VssInFromHur ( netVss ) )
      except StopIteration:
        print "[Stratus Warning] : Cell", self._name, "does not have a vss port."
        pass

    self._st_cks = []
    
    if not hurCell :
      # List of the cells 
      CELLS.append ( self )
      # List of the undercells
      if CELLS[0] != self :
        CELLS[0]._underCells[self] = 0
#        CELLS[0]._tabPlacement.append ( self )

    self._hur_plug = hurricane_plug

    # HurricanePLug if : no hurCell, in mode hurricane plug (i.e. hurricane_plug and hurricane_plug of the masterCell)
    if ( not hurCell ) and ( hurricane_plug ) :
      if CELLS[0] == self :
        self.HurricanePlug()
      else :
        if CELLS[0]._hur_plug :
          self.HurricanePlug()

  ##### Delete #####
  def Delete ( self ) :
    UpdateSession.open()

    under_cells = self._underCells
    self._hur_cell.Delete()
    for cell in under_cells : cell._hur_cell.Delete()

    UpdateSession.close()
    
  #####################################################
  ##### Initialisation of attributes for the cell #####
  #####################################################
  def InitGraph ( self ) :
    self._graph = True

    # External nets
    for net in self._st_ports :
      net._st_inst_in   = None
      net._st_insts_out = []
      net._covered      = False
    
    # Internal nets
    for net in self._st_sigs  :
      net._st_inst_in   = None
      net._st_insts_out = []
      net._covered      = False

    # Part nets
    for pnet in self._st_partsigs :
      pnet._st_inst_in   = None
      pnet._st_insts_out = []
      pnet._covered      = False
    
    # Instances
    for inst in self._st_insts :
      inst._st_nets_in  = []
      inst._st_nets_out = []

      # Initialisation of instances and nets
      self.initNet  ( inst )
      self.initInst ( inst )

  ########################################################
  ##### Initialisation of the attributes of the nets #####
  ########################################################
  def initNet ( self, instance ) :
    # For each pin
    for pin in instance._map :
      # Check if the pin is an output or an input
      direct = ""
      direct = self.getDirect ( instance._model, pin )

      # Get the net connected to this pin
      if instance._map[pin]._to_merge : mapNet = instance._map[pin]._to_merge[0][0]
      else                            : mapNet = instance._map[pin]
     
      if len ( instance._map[pin]._to_merge ) > mapNet._arity :
        mapNet._real_arity = len ( instance._map[pin]._to_merge )
        mapNet._inverse_merge = instance._map[pin]
      
      # Initialisation 
      if direct == 'input' :
        if mapNet._real_net :
          mapNet = mapNet._real_net
          
        if '_st_insts_out' not in mapNet.__dict__ : mapNet._st_insts_out = []

        if instance not in mapNet._st_insts_out :   mapNet._st_insts_out += [instance]

      elif direct == 'output' : # FIXME _real_net not taken care of
        mapNet._st_inst_in = instance
        
  #############################################################
  ##### Initialisation of the attributes of the instances #####
  #############################################################
  def initInst ( self, instance ) :
    # For each pin
    for pin in instance._map :
      # Check if the pin is an output or an input
      direct = ""
      direct = self.getDirect ( instance._model, pin )

      # Get the net connected to this pin
      if instance._map[pin]._to_merge : mapNet = instance._map[pin]._to_merge[0][0]
      else                            : mapNet = instance._map[pin]
    
      # Initialisation 
      if   direct == 'input'  : instance._st_nets_in  += [mapNet]
      elif direct == 'output' : instance._st_nets_out += [mapNet]

  ###########################################
  ##### Returns the direction of a port #####
  ###########################################
  def getDirect ( self, model, pin ) :
    global FRAMEWORK
    
    hurCell = FRAMEWORK.getCell ( model, CRL.Catalog.State.Views )
   
    stCell = None 
    if not hurCell :
      for c in self._underCells :
        if c._name == model :
          stCell = c
          break

    if not hurCell and not stCell :
      err = "\nError : no cell found with model %s.\n" % model
      raise Exception ( err )
   
    # Hurricane cell found: 
    if hurCell :
      found = False
      for net in hurCell.getExternalNets():
        direction = net.getDirection()
        name      = str(net.getName())

        if net.getType() in ( TypePOWER, TypeGROUND, TypeCLOCK ) :
          found = True
          continue
          
        chaine = re.search ( "(.*)\([0-9]+\)", name )
        if chaine : name = chaine.group(1)
      
        if name == pin :
          found = True
          if   direction == DirectionOUT : return "output"
          elif direction == DirectionIN  : return "input"
          else :
            err = "\nError : unable to find direction of port %s in model %s. Direction is %d\n" % ( pin, model, direction )
          raise Exception ( err )
          
      if not found :
        err = "\nError : unable to find port %s in model %s.\n" % ( pin, model )
        raise Exception ( err )
          
    # Stratus cell found:
    elif stCell :
      found = False
      
      for net in stCell._st_vdds :
        if net._name == pin :
          found = True
          break
      
      for net in stCell._st_vsss :
        if net._name == pin :
          found = True
          break
      
      for net in stCell._st_cks :
        if net._name == pin :
          found = True
          break

      # _st_ports i.e. not alimentations and not clock
      for net in stCell._st_ports : 
        if net._name == pin :
          found = True
          if   net._direct == "OUT" : return "output"
          elif net._direct == "IN"  : return "input"
          else :
            err = "\nError : unable to find direction of port %s in model %s.\n" % ( pin, model )
            raise Exception ( err )
   
      if not found :
        ports = ""
        for net in stCell._st_ports : ports += net._name + ","
        err = "\nError : unable to find port %s in model %s.\nPorts are : %s\n" % ( pin, model, ports )
        raise Exception ( err )

    else :
      err = "\n[ERROR] InitGraph : no model named " + model + " in the database.\n"
      raise Exception ( err )

  #############################
  ##### Print of the cell #####
  #############################
  def Print ( self ) :
    print "################## The Cell ##################"
    for inst in self._st_insts :
      print " * inst name :", inst._name
      print "    inst model :", inst._model
      for pin in inst._map :
        if pin != 'vdd' and pin != 'vss' :
          net = inst._map[pin]
          print "     pin :", pin, "net :", net._name, "with arity :", net._arity
          if net._to_merge :
            net = inst._map[pin]._to_merge[0][0]
            print "       net merged with :", net._name, "with arity :", net._arity

  #############################
  ##### Find An Instance  #####
  #############################
  def getInstance ( self, name ):
    for instance in self._st_insts:
      if instance._name == name:
        return instance
    return None

  #########################
  def PrintGraph ( self ) :
    if  "_graph" not in self.__dict__ :
      err = "\n[Stratus ERROR] PrintGraph : The graph does not exist. Use initGraph before.\n"
      raise Exception ( err )
    else :
      if self._graph == False :
        err = "\n[Stratus ERROR] PrintGraph : The graph does not exist. Use initGraph before.\n"
        raise Exception ( err )
      
    print "################## Cell's Graph features ##################"
    for inst in self._st_insts :
      print " => inst name :", inst._name, "( model :", inst._model, ")"
      print "    inst st_nets_in :"
      for net in inst._st_nets_in : print "        ", net._name
      print "    inst st_nets_out :"
      for net in inst._st_nets_out : print "        ", net._name
    for port in self._st_ports :
      print " => port name :", port._name
      if port._st_inst_in : print "    port _st_inst_in :", port._st_inst_in._name
      print "    port _st_insts_out :"
      for p in port._st_insts_out : print "        ", p._name
    for sig in self._st_sigs :
      print " => sig name :", sig._name
      if sig._st_inst_in : print "    sig _st_inst_in :", sig._st_inst_in._name
      print "    sig _st_insts_out :"
      for s in sig._st_insts_out : print "        ", s._name
    for psig in self._st_partsigs :
      print " => part sig name :", psig._name
      if '_st_inst_in' in psig.__dict__ and psig._st_inst_in: print "   part sig _st_inst_in :", psig._st_inst_in._name
      if '_st_insts_out' in psig.__dict__ :
        print "   part sig _st_insts_out :"
        for s in psig._st_insts_out : print "        ", s._name

  #####################
  ##### overloard #####
  #####################
  ##### __getattr #####
#  def __getattr__ ( self, attr ) :
    
  ##### __str #####
  def __str__ ( self ) :
    s = "Model" + str ( self._hur_cell )
    return s
    
  ########################
  ##### VLSI Methods #####
  ########################
  ##### Interface #####
  def Interface ( self ) :
    print "[Stratus Warning] : Execution of empty Interface method for", self._name, "."
                           
  ##### Netlist #####
  def Netlist ( self ) :
    print "[Stratus Warning] : Execution of empty Netlist method for", self._name, "."
    pass
                              
  ##### Layout #####
  def Layout ( self ) :
    print "[Stratus Warning] : Execution of empty Layout method for", self._name, "."
    pass
                        
  ##### Vbe #####
  def Vbe ( self ) :
    self._vbe = True
    
  ##### View : in order to see the in the editor #####
  def View ( self, stopLevel=1, message="Status stop point" ) :
    global EDITOR

    if not self._hur_cell :
      err = "\n[Stratus ERROR] View : Hurricane Cell does not exist.\nCheck CRL_IN_LO/CRL_IN_PH variables.\n"
      raise Exception ( err )

    if EDITOR:
      EDITOR.setCell ( self._hur_cell )
      EDITOR.fit     ()
      if stopLevel > 0:
        Breakpoint.stop ( stopLevel, message )
    
  ##### Save : in order to create the output files #####
  def Save ( self, views = 0, fileName = None ) :
    global FRAMEWORK
    global CELLS

    netlistFormat = Cfg.getParamString('stratus1.format').asString()

    if views == STRATUS :
      self.exportStratus ( fileName )

    elif netlistFormat in ['vst','vhd'] :
      UpdateSession.open()
  
      hurCell = self._hur_cell
  
      if str ( hurCell.getName() ) != "__Scratch__" :
        if netlistFormat == 'vst' :
          FRAMEWORK.saveCell ( hurCell, views|CRL.Catalog.State.Logical )
        else :
          self.exportVHD()
  
      if len ( CELLS ) == 0 :
        err = "\n[Stratus ERROR] Save : CELLS stack is empty.\n"
        raise Exception ( err )
  
      CELLS.pop()
      
      UpdateSession.close()

    elif netlistFormat == 'stratus' :
      self.exportStratus ( fileName )

    elif netlistFormat == 'vlog' :
      raise Exception('Format %s not yet implemented' % netlistFormat)

    else :
      raise Exception('Unrecognized format %s' % netlistFormat)

  ##### Simul : in order to use simulation tool #####
  def Simul ( self, name = None, tool = 'asimut' ) :
    from utils import runpat
      
    if not name : name = self._name

    simulator = Cfg.getParamString('stratus1.simulator').asString()

    if simulator == 'asimut' :
      runpat ( self._name, name, '-l 1 -p 100 -zerodelay -bdd' )
    elif simulator == 'ghdl' :
      import os
      cmd_str = ('ghdl -c -g -Psxlib --ieee=synopsys *.vhd -r %s_run --vcd=%s.vcd' %(name,name))
      os.system(cmd_str)
    elif tool == 'asimut' : runpat ( self._name, name, '-l 1 -p 100 -zerodelay -bdd' )
    else                : raise Exception ( 'not implemented yet' )

  ##### TestBench : in order to create testbench #####
  def Testbench ( self ) :
    netlistFormat = Cfg.getParamString('stratus1.format').asString()

    import stimuli
    stim = stimuli.Stimuli(self)
    if netlistFormat == 'vhd' :
      # Create stimuli input file
      stim.create_stimuli_text_file()

      # Create testbench
      stim.create_testbench(delay = 20, unit = 'ns', debug = False, downto = True, logic = False)

      # Create testbench and block instance
      stim.create_run(debug = False, downto = True)
    elif netlistFormat == 'vst' :
      stim.create_pat_file()
    else :
      raise Exception('Testbench not yet implemented for format %s' % netlistFormat)

  ##### Create a stratus file given the database #####
  def exportStratus ( self, fileName ) :

    ## Cat formating ##
    def catName ( net, tableau ) :
       
      name = "Cat ( "
      paire = tableau[-1]
      if paire :
        net1  = paire[0]
        bit1  = str(paire[1])
      else :
        net1  = net
        bit1 = str(len(tableau)-1)

      for i in range ( len(tableau)-2, -1, -1 ) :
        change = False
        if tableau[i] and not tableau[i+1] :
          change = True
        elif not tableau[i] and tableau[i+1] :
          change = True
        elif tableau[i] and tableau[i+1] :
          if tableau[i][0] != tableau[i+1][0] :
            change = True

        if change :
          if tableau[i+1] : bit2 = str(tableau[i+1][1])
          else            : bit2 = str(i+1)
        
          name += "self." 
          if int(bit2) == int(net1._ind) and int(bit1) == (net1._arity-1) :
            name += net1._name
          else :
            name += net1._name
            name += "["
            name += bit1
            name += ":"
            name += bit2
            name += "]"
            
          name += ", "
    
          if tableau[i] : net1 = tableau[i][0]
          else          : net1 = net

          if tableau[i] : bit1 = str(tableau[i][1])
          else          : bit1 = str(i)
          
      if tableau[0] :
        bit2 = str(tableau[0][1])
      else :
        bit2 = "0"
        
      name += "self." 
      if int(bit2) == int(net1._ind) and int(bit1) == (net1._arity-1) :
        name += net1._name
      else :
        name += net1._name
        name += "["
        name += bit1
        name += ":"
        name += bit2
        name += "]"        
      
      name += " )"
        
      return name
   
    #########################   
    nom = str(self.__class__)
    chaine = re.search ( "[^\.]*\.(.*)", nom )
    nom = chaine.group(1)

    if not fileName : fileName = nom + "_created"
    fileName += ".py"
    
    file = open ( fileName, "w+" )
    
    file.write ( "#!/usr/bin/python\n\n" )
    file.write ( "from stratus import *\n\n" )

    file.write ( "class %s ( Model ) :\n\n" % nom )

    ##### Interface #####
    file.write ( "  def Interface ( self ) :\n\n" )
        
    for net in self._st_ports :
      if   net._direct == "IN"  : classe = "SignalIn"
      elif net._direct == "OUT" : classe = "SignalOut"
      elif net._direct == "INOUT" : classe = "SignalInOut"

      file.write ( "    self.%s = %s ( \"%s\", %d )\n" % ( net._name, classe, net._name, net._arity ) )
    file.write ( "\n" )

    for ck in self._st_cks :
      if ck._ext:
        file.write ( "    self.%s = CkIn ( \"%s\" )\n" % (ck._name, ck._name) )

    file.write ( "    self.vdd = VddIn ( \"vdd\" )\n" )
    file.write ( "    self.vss = VssIn ( \"vss\" )\n" )
    file.write ( "\n" )
      
    ##### Netlist #####
    file.write ( "  def Netlist ( self ) :\n\n" )

    # Signals
    for net in self._st_sigs :
      if net._to_merge :
        towrite = False
        for paire in net._to_merge :
          if paire == 0 : towrite = True
        if not towrite : continue
      file.write ( "    self.%s = Signal ( \"%s\", %d )\n" % ( net._name, net._name, net._arity ) )
    file.write ( "\n" )

    # Generate
    for cell in CELLS[0]._underCells :
      if CELLS[0]._underCells[cell] == 1 :
        classe = str(cell.__class__)
        nom    = cell._name
        param  = cell._param
    
        # Gestion of libraries with specific treatment
        chaine = re.search ( "(.*)\.([^\.]*)", classe )
        if chaine :
          fichier = chaine.group(1)
          if re.search ( "dpgen_", fichier ) or re.search ( "st_", fichier ) :
            classe = chaine.group(2)
          # Special treatment for virtual library
          if classe == "Bool" : 
            nom    = cell._name
            classe = cell._param['model']
            param  = cell._param
            del param['model']
            del param['realModel']

        classe = string.upper(classe[0]) + classe[1:] #FIXME !!!
        if re.search ( "(.*)\.([^\.]*)", classe ) : # ArithLib generator
           classe = string.lower(classe[0]) + classe[1:]

        file.write ( "    Generate ( \"%s\", \"%s\", param = %s )\n" % ( classe, nom, param ) )    
    file.write ( "\n" )

    # Instances
    for inst in self._st_insts :
      #chaine = re.search ( "([^_]*)_(.*)", inst._name ) #XTOF FIXME !!!
      #instName = chaine.group(1) #XTOF FIXME !!
      file.write ( "    self.%s = Inst ( \"%s\"\n" % ( inst._name, inst._model ) )
      file.write ( "                          , \"%s\"\n" % inst._name )
      file.write ( "                          , map = { " )

      # Map
      for pin in inst._map :
        toto = False
        tata = False
      
        netInMap = inst._map[pin]
        nom = "self." + netInMap._name
       
        # <=
        if netInMap._to_merge :
          oneperone = True
          for i in range ( len ( netInMap._to_merge ) ) :
            paire = netInMap._to_merge[i]
            
            if paire :
              net = paire[0]
              if i > 0 :
                if netInMap._to_merge[i-1] != 0 :
                  netbefore = netInMap._to_merge[i-1][0]
                else :
                  oneperone = False
                  break
                if net != netbefore :
                  oneperone = False
                  break
                if paire[1] != i :
                  oneperone = False
                  break
                  
            else :
              toto = True
              oneperone = False
              break

          # "easy" case : 1 per 1 corresponding
          if oneperone :
            netInMap = netInMap._to_merge[0][0]
            nom = "self." + netInMap._name

          # else : Cat of the different signals
          else :
            nom = catName ( netInMap, netInMap._to_merge )

        # Cat
        if netInMap._to_cat :
          tata = True
          nom = catName ( netInMap, netInMap._to_cat )
            
        if toto and tata : print "Attention est ce un cas bien gere ???"

        if pin == inst._map.keys()[0] : file.write ( "\"%s\" : %s\n" % ( pin, nom ) )
        else                          : file.write ( "                                  , \"%s\" : %s\n" % ( pin, nom ) )

      file.write ( "                                  }\n" )
      file.write ( "                          )\n\n" )
   
    ##### End ##### 
    file.close ()

  ##### Create a IEEE VHDL file given the database #####
  def exportVHD ( self ) :

    file = open ( self._name + '.vhd', "w+" )
    
    file.write ( "--\n" )
    file.write ( "-- Generated by VHDL export\n" )
    file.write ( "--\n" )
    file.write ( "library ieee;\n" )
    file.write ( "use ieee.std_logic_1164.all;\n\n" )
    
    if 'realModel' in self._param :
      file.write ( "library sxlib;\n" )
      file.write ( "use sxlib.all;\n\n" )
     

    ##### Entity #####
    file.write ( "entity %s is\n" % self._name )
    file.write ( "  port(\n" )
        
    strPorts = ""
    for net in self._st_ports :
      if net._arity == 1 :
        strPorts += "    %s : %s std_logic;\n" % ( net._name, net._direct )
      else :
        strPorts += "    %s : %s std_logic_vector(%d downto %d);\n" % ( net._name, net._direct, net._arity-1+net._ind, net._ind )

    for ck in self._st_cks :
      if ck._ext:
        strPorts += "    %s : IN std_logic;\n" % ( ck._name )

    file.write(strPorts[:-2])
    file.write ( "\n  );\n" )
    file.write ( "end %s ;\n\n" % self._name )
      
    ##### Architecture #####
    file.write ( "architecture structural of %s is\n" % self._name )


    # Components
    if 'realModel' in self._param :
      cellList = [self]
    else :
      cellList = self._underCells
    for cell in cellList :
      if 'realModel' in self._param :
        nom = cell._param['realModel']
      else :
        nom = cell._name
      #classe = str(cell.__class__)
      #nom    = cell._name
      #param  = cell._param
      #print "nom " + nom
      #print "classe " + classe
    
      ## Gestion of libraries with specific treatment
      #chaine = re.search ( "(.*)\.([^\.]*)", classe )
      #if chaine :
      #  fichier = chaine.group(1)
      #  if re.search ( "dpgen_", fichier ) or re.search ( "st_", fichier ) :
      #    classe = chaine.group(2)
      #  # Special treatment for virtual library
      #  if classe == "Bool" : 
      #    #nom    = cell._name
      #    nom    = cell._param['realModel']
      #    classe = cell._param['model']
      #    param  = cell._param
      #    #del param['model']
      #    #del param['realModel']

      #classe = string.upper(classe[0]) + classe[1:] #FIXME !!!
      #if re.search ( "(.*)\.([^\.]*)", classe ) : # ArithLib generator
      #   classe = string.lower(classe[0]) + classe[1:]

      file.write ( "  component %s\n" % nom )    
      file.write ( "    port(\n" )    

      # Ports
      strPorts = ""
      for net in cell._st_ports :
        if net._arity == 1 :
          strPorts += "      %s : %s std_logic;\n" % ( net._name, net._direct )
        else :
          strPorts += "      %s : %s std_logic_vector(%d downto %d);\n" % ( net._name, net._direct, net._arity-1+net._ind, net._ind )

      for ck in cell._st_cks :
        if ck._ext:
          strPorts += "      %s : IN std_logic;\n" % ( ck._name )

      file.write(strPorts[:-2])

      file.write ( "\n    );\n" )    
      file.write ( "  end component;\n\n")    

    # Signals
    for net in self._st_sigs :
      if net._to_merge :
        towrite = False
        for paire in net._to_merge :
          if paire == 0 : towrite = True
        if not towrite : continue
      if net._arity == 1 :
        file.write ( "  signal %s : std_logic;\n" % net._name )
      else :
        file.write ( "  signal %s : std_logic_vector(%d downto %d);\n" % ( net._name, net._arity-1+net._ind, net._ind ) )

    # Instances
    file.write ( "\nbegin\n" )
    for inst in self._st_insts :
      #chaine = re.search ( "([^_]*)_(.*)", inst._name ) #XTOF FIXME !!!
      #instName = chaine.group(1) #XTOF FIXME !!
      file.write ( "  %s : %s\n" % ( inst._name, inst._model ) )
      file.write ( "    port map(\n" )

      # Map
      strMap = ""
      for pin in inst._map :
        toto = False
        tata = False
      
        netInMap = inst._map[pin]
        if netInMap._real_net :
          nom = netInMap._real_net._name
        else :
          nom = netInMap._name
       
        # <=
        if netInMap._to_merge :
          oneperone = True
          for i in range ( len ( netInMap._to_merge ) ) :
            paire = netInMap._to_merge[i]
            
            if paire :
              net = paire[0]
              if i > 0 :
                if netInMap._to_merge[i-1] != 0 :
                  netbefore = netInMap._to_merge[i-1][0]
                else :
                  oneperone = False
                  break
                if net != netbefore :
                  oneperone = False
                  break
                if paire[1] != i :
                  oneperone = False
                  break
                  
            else :
              toto = True
              oneperone = False
              break

          # "easy" case : 1 per 1 corresponding
          if oneperone :
            netInMap = netInMap._to_merge[0][0]
            nom = netInMap._name

          # else : Cat of the different signals
          else :
            nom = catName ( netInMap, netInMap._to_merge )

        # Cat
        if netInMap._to_cat :
          tata = True
          nom = catName ( netInMap, netInMap._to_cat )
            
        if toto and tata : print "Attention est ce un cas bien gere ???"

        if netInMap not in self._st_vdds + self._st_vsss :
          if pin == inst._map.keys()[0] : strMap += "      %s => %s,\n" % ( pin, nom )
          else                          : strMap += "      %s => %s,\n" % ( pin, nom )

      file.write(strMap[:-2] + '\n')
      file.write ( "    );\n\n" )
   
    ##### End ##### 
    file.write('end structural;')
    file.close ()

    
  #### Quit : to quit the current cell without saving #####
  def Quit ( self ) :
    global CELLS
        
    CELLS.pop ()
    
  #########################################
  ##### GetModelName : Class's method #####
  #########################################
  def GetModelName ( param ) :
    return None
  GetModelName = staticmethod ( GetModelName )
  
  #########################################
  ##### GetParam : Class's method #####
  #########################################
  def GetParam () :
    return None
  GetParam = staticmethod ( GetParam )

  ###############
  ##### Set #####
  ###############
  def SetAnd   ( self, model ) : self._and   = model
  def SetOr    ( self, model ) : self._or    = model
  def SetXor   ( self, model ) : self._xor   = model
  def SetNot   ( self, model ) : self._not   = model
    
  def SetBuff  ( self, model ) : self._buff  = model
  def SetReg   ( self, model ) : self._reg   = model
  def SetMux   ( self, model ) : self._mux   = model
  def SetShift ( self, model ) : self._shift = model
  def SetComp  ( self, model ) : self._comp  = model
       
  def SetAdd   ( self, model ) : self._add   = model
  def SetMult  ( self, model ) : self._mult  = model
  def SetDiv   ( self, model ) : self._div   = model

  def SetSigned ( self, model ) : self._signed = model
  def SetExtended ( self, model ) : self._extended = model

  #############################
  ##### Creation of model #####
  #############################
  def ModelCreation ( self, modele, modele2, dict, hierarchy, realModele = None, inOut = None ) :
    global CELLS

    from st_parser     import BVg
  
    ## Virtual library ##
    from st_bool       import Bool
  
    ## Stratus generators ##
    from st_mux        import Smux
    from st_shifter    import Shifter
    from st_comp       import Comp
    from st_const      import Constant
    from st_extend     import Extend
    
    from st_slansky    import Slansky
    from st_slansky    import Sub
    from st_mult       import Multiplier
  
    ## Genlib ##
    from dpgen_Shifter import DpgenShifter
    from dpgen_Shrot   import DpgenShrot
    from dpgen_Nul     import DpgenNul
    from dpgen_RAM     import DpgenRam
    from dpgen_ROM2    import DpgenRom2
    from dpgen_ROM4    import DpgenRom4
    from dpgen_ADSB2F  import DpgenAdsb2f
    from dpgen_RF1     import DpgenRf1, DpgenRf1d, DpgenRf1r0, DpgenRf1dr0, DpgenFifo
    from dpgen_RF2     import DpgenRf2, DpgenRf2d, DpgenRf2r0, DpgenRf2dr0
    from dpgen_Regular import DpgenAnd2, DpgenAnd3, DpgenAnd4, DpgenBuff, DpgenBuse, DpgenConst, DpgenDff, DpgenDfft, DpgenInv, DpgenMux2, DpgenNand2, DpgenNand2mask, DpgenNand3, DpgenNand4, DpgenNbuse, DpgenNmux2, DpgenNor2, DpgenNor2mask, DpgenNor3, DpgenNor4, DpgenOr2, DpgenOr3, DpgenOr4, DpgenSff, DpgenSfft, DpgenXnor2, DpgenXnor2mask, DpgenXor2

    from util_Gen      import F_MSB_FIRST
    
    if type ( dict ) != types.DictType :
      err = "\n[Stratus ERROR] Inst : instanciation of a user's defined generator. The methods' arguments must be dictionnaries.\n"
      raise Exception ( err )
      
    ##### Creation of the instance #####
#    dict['flags'] = F_MSB_FIRST # When vst driver permits to do F_LSB_FIRST or F_LSB_FIRST TODO
    
    ## Virtual library ## 
    if modele in BVg :
      dict['model']     = modele.lower()
      dict['realModel'] = realModele
  
      instCell = Bool ( modele2, dict, inOut )
      
    ## Generator ##
    elif re.search ( "\.", modele ) :
      chaine    = re.search ( "(.*)\.([^\.]*)$", modele )
  
      ficName   = chaine.group(1)
      className = chaine.group(2)

      # Roselyne : pour prendre en compte les modeles recursifs
      #import sys
      moduleClass = __import__ ( ficName, globals(), locals(), [className] )
      modeleClass =   getattr  ( moduleClass, className )
      #try :
      #   modeleClass =   getattr  ( sys.modules['__main__'], className )
      #except AttributeError :
      #   moduleClass = __import__ ( ficName, globals(), locals(), [className] )
      #   modeleClass =   getattr  ( moduleClass, className )
      # fin Roselyne
      
      instCell = modeleClass ( modele2, dict )
      
    else :
      instCell = eval ( "%s ( \"%s\", %s )" % ( modele, modele2, str(dict) ) )
  
    ## MAJ of the hierarchy ##
    instCell._hierarchy = hierarchy

    ##### Methods #####
    if ( self._hur_cell ) and ( not instCell._hur_cell ) : instCell.HurricanePlug()

    instCell.Interface()
    instCell.Netlist()

    if ( 'clean' in dict ) and ( dict['clean'] != False  ) :
      if 'interactive' not in dict : dict['interactive'] = False
      instCell.Clean ( dict['interactive'], dict['clean'] )
    
    if ( 'behavioral' in dict ) and ( dict['behavioral'] == True  ) :
      instCell.Vbe()
    
    if instCell._hur_cell :
      v = 0
      if ( 'physical' in dict ) and ( dict['physical'] == True ) :
        instCell.Layout()
        v = CRL.Catalog.State.Physical
      instCell.Save( v|CRL.Catalog.State.Logical )

    else :
      instCell.Quit()

    if CELLS[0] != instCell : CELLS[0]._underCells[instCell] = hierarchy

    return instCell

  #################################################################
  ##### HurricanePlug : in order to create the hurricane cell #####
  #################################################################
  def HurricanePlug ( self ) :
    global FRAMEWORK, CELLS

    if self._hur_cell :
      print "[Stratus Warning] : The stratus cell already exists."
      return
    
    self._hur_plug = True

    ##### Creation of all the under cells #####
    # Initialisation of the max depth
    max_depth = 0
    for keyCell in self._underCells :
      if self._underCells[keyCell] > max_depth : max_depth = self._underCells[keyCell]
    # Creation from the leaf to the top cell 
    while max_depth :
      for keyCell in self._underCells :
        if self._underCells[keyCell] == max_depth :
          if not FRAMEWORK.getCell ( keyCell._name, CRL.Catalog.State.Views ) :
            CELLS.append ( keyCell )
            keyCell.CellHurCreation()
      max_depth -= 1

    # Creation of the top cell
    CELLS.append ( self )
    self.CellHurCreation()
    
  ##### CellHurCreation #####
  def CellHurCreation ( self ) :
    global FRAMEWORK, CELLS
      
    # The cell
    hurCell = FRAMEWORK.createCell ( self._name )
    hurCell.setTerminal ( 0 )
    
    self._hur_cell = hurCell

    MODELMAP [ str ( self._hur_cell ) ] = self
    
    # The nets
    for net in self._st_ports : net.create_hur_net()
    for net in self._st_sigs  : net.create_hur_net()

    
    for net in self._st_cks   : net.create_hur_net()
    
    for net in self._st_vdds  : net.create_hur_net()
    for net in self._st_vsss  : net.create_hur_net()
   
    # The instances and the connection
    for inst in self._st_insts : inst.create_hur_inst ( inst._model )
    
    # Merge
    for net in self._st_merge : net.hur_merge()

    if ( self != CELLS[0] ) and ( self._st_insts ) : # Layout done if HurricanePlug after Netlist method
     #self.Layout() # FIXME trouver une raison de le faire ou pas : instances placees ou pas ... HurricanePlug ne doit plus marcher avec placement A VERIFIER 14/01/08
      if self._st_insts[0]._hur_instance.getPlacementStatus() : self.Save ( CRL.Catalog.State.Physical )
      else                                                    : self.Save ( CRL.Catalog.State.Logical )
    else :
      self.Quit()

  ####################
  ##### Clean Up #####
  ####################
  def Clean ( self, interactive = False, tab_name_net = [] ) :
    
    # Hurricane cell
    hurricane_cell = self._hur_cell
   
    # Useful for print 
    TAB_NETS     = []
    TAB_INSTS    = []
    cpt_inst_del = 0
    cpt_net_del  = 0
    
    ##### Tabs initialisation #####
    net_entree  = []
    net_sortie  = []
    net_interne = []

    for net in hurricane_cell.getNets():
        if   net.getDirection() == DirectionUNDEFINED : net_interne.append ( net )
        elif net.getDirection() == DirectionIN        : net_entree.append  ( net )
        elif net.getDirection() == DirectionOUT       : net_sortie.append  ( net )

    ##### File initialisation #####
    file = []
    
    # Output nets put in the file if there are given as arguments
    if tab_name_net != True :
      for net_name in tab_name_net :
        for net in net_sortie :
          if net_name == str ( net.getName() ) :
            file.insert ( 0, net )
            if interactive : print "Output Net", net, "has to be erased, it is put in the fifo."

    ## Internal nets ##
    # Number of plugs of each net :
    # if equal to 0 : del net
    # if equal to 1 : put net in the file
    for net in net_interne :
      nb_plugs = self.count_plugs ( net )
    
      if nb_plugs == 0 :
        if interactive : print "* One net suppressed (a) :", net
        TAB_NETS.append ( net.getName () )
        cpt_net_del += 1
        
        net.Delete()
        
      elif nb_plugs == 1 :
        if net.getPlugs().next().getMasterNet().getDirection() == DirectionOUT: # output of an instance
          if interactive : print "* One net put in the fifo :", net
          file.insert ( 0, net )
    
    ## Ouput nets ##
    # Number of plugs of each net :
    # if equal to 0 : del net
    for net in net_sortie + net_entree :
      cpt_plugs_sor = self.count_plugs ( net )    
      if cpt_plugs_sor == 0 :
        print "[Stratus Warning] Clean : Interface of", self._name, "changed, net :", net, "is suppressed"
        TAB_NETS.append ( net.getName() )
        cpt_net_del += 1
        
        net.Delete()
    
    ##### Algorithm #####
    while len ( file ) > 0 :
      net_file = file.pop()
      plug     = net_file.getPlugs().next()
      inst     = plug.getInstance()
    
      # input nets of the instance
      plugs_inst      = inst.getPlugs()
      net_entree_inst = [] 
      net_sortie_inst = [] 
      cpt_sortie      = 0
      
      for plug_de_inst in plugs_inst:
        # compute the number of outputs of the instance
        if plug_de_inst.getMasterNet().getDirection() == DirectionOUT :
          cpt_sortie += 1 
          # output nets of the instance put in tab, except for the net being worked on
          if net_file !=  plug_de_inst.getNet() : net_sortie_inst.append ( plug_de_inst.getNet() )

        if plug_de_inst.getMasterNet().getDirection() == DirectionIN :
          # ignore vdd and vss
          type = plug_de_inst.getNet().getType()
          if type not in ( TypePOWER, TypeGROUND ) : net_entree_inst.append ( plug_de_inst.getNet() )

      ### Deletion of te instance ###
      # If the instance has only one output
      if cpt_sortie == 1 :
        if interactive : print "* One net suppressed (b) :", net_file
        TAB_NETS.append ( net_file.getName() )
        cpt_net_del += 1
        
        net_file.Delete()
        
        if interactive : print "* One instance suppressed (a) :", inst, inst
        TAB_INSTS.append(inst.getName())
        cpt_inst_del += 1
        
        UpdateSession.open()
        inst.Delete()
        UpdateSession.close()
        
        for net_ent in net_entree_inst :
          cpt_plugs_in = self.count_plugs ( net_ent )   
          if cpt_plugs_in == 0 : 
            if net_ent in net_entree :
              print "[Stratus Warning] Clean : Interface of", self._name, "changed, net :", net_ent, "is suppressed"
              
              TAB_NETS.append ( net_ent.getName() )
              cpt_net_del += 1
              
              net_ent.Delete()
            else :
              if interactive : print "* One net suppressed (c) : ", net_ent
              
              TAB_NETS.append ( net_ent.getName() )
              cpt_net_del += 1
              
              net_ent.Delete()
              
          elif cpt_plugs_in == 1 :
            if net_ent.getPlugs().next().getMasterNet().getDirection() ==  DirectionOUT : # is an output net of another instance
              if interactive : print "* One net put in the fifo :", net_ent
              file.insert ( 0, net_ent )
    
      # If the instance has more than one output
      elif cpt_sortie >= 2 :
        connect = False
        for net_sor in net_sortie_inst:
          cpt_sor = self.count_plugs ( net_sor )
          if cpt_sor >= 2 :
            connect = True # at least one output connected
            break
          elif cpt_sor == 1 :
            if net_sor in net_sortie :
              connect = True # at least one output connected
              break
              
        if not ( connect ) :
          if interactive : print "* One net suppressed (d) :", net_file

          TAB_NETS.append ( net_file.getName() )
          cpt_net_del += 1
          
          net_file.Delete()
          
          for net_sor in net_sortie_inst :
            if net_sor in file : file.remove ( net_sor )
    
            if interactive : print "* One net suppressed (e) :", net_sor
            TAB_NETS.append ( net_sor.getName() )
            cpt_net_del += 1
            
            net_sor.Delete()
          
          if interactive : print "* One instance suppressed (b) :", inst
          TAB_INSTS.append ( inst.getName() )
          cpt_inst_del += 1
          
          UpdateSession.open()
          inst.Delete()
          UpdateSession.close()

          for net_ent in net_entree_inst :
            cpt_plugs_in = self.count_plugs ( net_ent )
    
            if cpt_plugs_in == 0 :
              if net_ent in net_entree :
                print "[Stratus Warning] Clean : Interface of", self._name, "changed, net :", net_ent, "is suppressed"
                
                TAB_NETS.append ( net_ent.getName() )
                cpt_net_del += 1
                
                net_ent.Delete()
                
              else :
                if interactive : print "* One net suppressed (f) :", net_ent
                
                TAB_NETS.append ( net_ent.getName() )
                cpt_net_del += 1
                
                net_ent.Delete()

            elif cpt_plugs_in == 1 :
              if net_ent.getPlugs().next().getMasterNet().getDirection() == DirectionOUT: # in an output net of another instance
                if interactive : print "* One net net put in the fifo :", net_ent
                file.insert ( 0, net_ent )
                    
        else :
          if interactive : print "The net", net_file, "can not be delayed, it may be delayed later"
    
      else :
        print "[Warning] Pb in Clean."
   
    if interactive : 
      print ""
      print "* Number of net suppressed :",       cpt_net_del
      print "* List of these nets :",             TAB_NETS
      print ""
      print "* Number of instances suppressed :", cpt_inst_del     
      print "* List of these instance :",         TAB_INSTS
      print ""

  ###############################
  def count_plugs ( self, net ) :
    return len(net.getPlugs())


  #############################################################################
  ############################## Place and route ##############################
  #############################################################################
 #def getCore ( self ) :
 #  '''This function returns the instance "core" : the only one which is not a pad'''

 #  from placeandroute import isPad

 #  cores = []
 #  for instance in self._hur_cell.getInstances():
 #    if not isPad ( instance ):
 #      cores.append ( instance )

 #  if   len(cores) == 0 : err = "\n[Stratus ERROR] getCore : No core found.\n"
 #  elif len(cores)  > 1 : err = "\n[Stratus ERROR] getCore : More than one core found.\n"
 #  if len(cores) != 1 : raise Exception ( err )

 #  return cores[0]
