#!/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      : Roselyne AVOT-CHOTIN - Sophie BELLOEIL           |
# |  E-mail      :           Sophie.Belloeil@asim.lip6.fr           |
# | =============================================================== |
# |  Py Module   :       "./st_slansky.py"                          |
# | *************************************************************** |
# |  U p d a t e s                                                  |
# |                                                                 |
# x-----------------------------------------------------------------x



from stratus import *

class Sub ( Model ) :
    
   def __init__(self, name, param):
      Model.__init__(self, name, param)
      if 'nbit' in param:
         self.nbit  = param['nbit']
         self.nbit0 = self.nbit
         self.nbit1 = self.nbit
      else:
         self.nbit0 = param['nbit0']
         self.nbit1 = param['nbit1']
         self.nbit  = max(self.nbit0,self.nbit1)

      if 'signed' not in param : self._signed = True
      else                     : self._signed = param['signed']

      if 'extended' not in param : self._extended = False
      else                     : self._extended = param['extended']
    
      if (self.nbit < 2): raise Exception ( "\n[Stratus ERROR] slansky : input arity should be greater than 1.\n" )

   def Interface(self):
      self.i0 = SignalIn("i0", self.nbit0)
      self.i1 = SignalIn("i1", self.nbit1)
      if self._extended : self.o = SignalOut("o", self.nbit+1)
      else : self.o = SignalOut("o", self.nbit)

      self.vdd = VddIn("vdd")
      self.vss = VssIn("vss")

   def Netlist(self):
      # extand the inputs to have the same width
      if (self.nbit0 < self.nbit) : 
        i0e = Signal("i0e", self.nbit)
        if self._signed : i0e <= self.i0.Extend(self.nbit,'signed')
        else : i0e <= self.i0.Extend(self.nbit,'zero')
      else:
        i0e = self.i0
      if (self.nbit1 < self.nbit) : 
        i1e = Signal("i1e", self.nbit)
        if self._signed : i1e <= self.i1.Extend(self.nbit,'signed')
        else : i1e <= self.i1.Extend(self.nbit,'zero')
      else:
        i1e = self.i1

      Generate ( "Slansky", "slansky_cin_%d" % ( self.nbit ), param={'nbit':self.nbit,'cin':True, 'extended':self._extended} )
      Inst ( "slansky_cin_%d" % ( self.nbit )
           , map = { 'i0' : i0e
                   , 'i1' : ~i1e
                   , 'cin' : One(1)
                   , 'o'  : self.o
                   , 'vdd' : self.vdd
                   , 'vss' : self.vss
                   }
           )

   def GetParam ( cls ):

      return {'nbit' : 'integer', 'nbit0' : 'integer', 'nbit1' : 'integer'}

   GetParam = classmethod ( GetParam )


class Slansky ( Model ) :

   def __init__(self, name, param):
      Model.__init__(self, name, param)
      if 'nbit' in param:
         self.nbit  = param['nbit']
         self.nbit0 = self.nbit
         self.nbit1 = self.nbit
      else:
         self.nbit0 = param['nbit0']
         self.nbit1 = param['nbit1']
         self.nbit  = max(self.nbit0,self.nbit1)

      if 'signed' not in param : self._signed = True
      else                     : self._signed = param['signed']

      if 'extended' not in param : self._extended = False
      else                     : self._extended = param['extended']
    
      if 'cin' not in param  : self._cin = False
      else                   : self._cin = param['cin']

      if (self.nbit < 2): raise Exception ( "\n[Stratus ERROR] slansky : input arity should be greater than 1.\n" )

   def Interface(self):
      self.i0 = SignalIn("i0", self.nbit0)
      self.i1 = SignalIn("i1", self.nbit1)
      if self._cin : self.cin = SignalIn("cin", 1)
      if self._extended : self.o = SignalOut("o", self.nbit+1)
      else : self.o = SignalOut("o", self.nbit)

      self.vdd = VddIn("vdd")
      self.vss = VssIn("vss")

   def Netlist(self):
     # extand the inputs to have the same width
     if (self.nbit0 < self.nbit) : 
       i0e = Signal("i0e", self.nbit)
       if self._signed : i0e <= self.i0.Extend(self.nbit,'signed')
       else : i0e <= self.i0.Extend(self.nbit,'zero')
     else:
       i0e = self.i0
     if (self.nbit1 < self.nbit) : 
       i1e = Signal("i1e", self.nbit)
       if self._signed : i1e <= self.i1.Extend(self.nbit,'signed')
       else : i1e <= self.i1.Extend(self.nbit,'zero')
     else:
       i1e = self.i1
       
     # pigi preprocessing
     [p,g] = pigi(i0e,i1e)
    
     # Slansky algorithm
     [pp,gg] = pg_tree(p,g,'slansky')
     
     # postprocessing :
     if self._cin : self.o[0] <= p[0] ^ self.cin
     else         : self.o[0] <= p[0].Buffer()
     
     for i in range(1,self.nbit):
       if self._cin : self.o[i] <= p[i] ^ (gg[i-1] | (pp[i-1] & self.cin))
       else         : self.o[i] <= p[i] ^ gg[i-1]

     if self._extended:
       if self._signed:
         ovft = Signal("ovft",1)
         if self._cin:
           ovft <= (gg[self.nbit-1] | (pp[self.nbit-1] & self.cin)) ^ (gg[self.nbit-2] | (pp[self.nbit-2] & self.cin))
           # pb inout asimut remplacement de o[nbit-1] par sa valeur
           # self.o[self.nbit] <= (self.o[self.nbit-1] & ~ovf) | (cout & ovf)
           self.o[self.nbit] <= ((p[self.nbit-1] ^ (gg[self.nbit-2] | (pp[self.nbit-2] & self.cin))) & ~ovft) | (gg[self.nbit-1] & ovft)
         else:
           ovft <= gg[self.nbit-1] ^ gg[self.nbit-2]
           # pb inout asimut remplacement de o[nbit-1] par sa valeur
           # self.o[self.nbit] <= (self.o[self.nbit-1] & ~ovf) | (cout & ovf)
           self.o[self.nbit] <= ((p[self.nbit-1] ^ gg[self.nbit-2]) & ~ovft) | (gg[self.nbit-1] & ovft)
       else:
         if self._cin:
           self.o[self.nbit] <= gg[self.nbit-1] | (pp[self.nbit-1] & self.cin)
         else:
           self.o[self.nbit] <= gg[self.nbit-1].Buffer()

   def GetModelName ( cls, param ) :
     name = 'slansky'

     if 'nbit' in param:
       name += '_%d' % (param['nbit'])
     else:
       if param['nbit0'] != param['nbit1'] : name += '_%dx%d' % (param['nbit0'], param['nbit1'])
       else                                : name += '_%d'    % (param['nbit0'])

     if ('cin' in param) and (param['cin']) : name += '_cin'
     
     return name
   GetModelName = classmethod ( GetModelName )

   def GetParam ( cls ):

      return {'nbit' : 'integer', 'nbit0' : 'integer', 'nbit1' : 'integer', 'cin' : 'boolean'}

   GetParam = classmethod ( GetParam )

####################################################################################################
nb_pigi = 0
nb_pg_tree = 0

def pigi(a,b):
   global nb_pigi
   nb_pigi += 1
   pi = Signal("pi%d_a" %nb_pigi,a._arity)
   gi = Signal("gi%d_a" %nb_pigi,a._arity)
   Generate ( "st_slansky.PiGiBus", "pigibus%d" % a._arity, param = {'nbit' : a._arity} )
   Inst("pigibus%d" % a._arity,
      map = {
         'a' : a,
         'b' : b,
         'pi' : pi,
         'gi' : gi,
         'vdd' : a._st_cell._st_vdds[0],
         'vss' : a._st_cell._st_vsss[0]
      },
   )
   return [pi,gi]

class PiGiBus(Model):
   def __init__(self, name, param):
      Model.__init__(self, name, param)
      self.nbit = param['nbit']
      if self.nbit < 1:
         raise Exception ( 'PiGiBus : input arity should be greater than 0' )

   def Interface(self):
      self.a = SignalIn("a",self.nbit)
      self.b = SignalIn("b",self.nbit)
      self.pi = SignalOut("pi",self.nbit)
      self.gi = SignalOut("gi",self.nbit)

      self.vdd = VddIn("vdd")
      self.vss = VssIn("vss")

   def Netlist(self):
      for i in range (self.nbit):
         Generate ( "st_slansky.PiGi", "pigi" )
         Inst("pigi",
            map = {
               'a'  : self.a[i],
               'b'  : self.b[i],
               'pi' : self.pi[i],
               'gi' : self.gi[i],
               'vdd': self.vdd,
               'vss': self.vss
            }
         )

   def GetParam ( cls ):

      return {'nbit' : 'integer'}

   GetParam = classmethod ( GetParam )

         
class PiGi(Model):
   def Interface(self):
      self.a = SignalIn("a",1)
      self.b = SignalIn("b",1)
      self.pi = SignalOut("pi",1)
      self.gi = SignalOut("gi",1)
      
      self.vdd = VddIn("vdd")
      self.vss = VssIn("vss")

   def Netlist(self):
      self.pi <= self.a ^ self.b
      self.gi <= self.a & self.b

########################
def pg_tree(pi,gi,algo):
   global nb_pg_tree
   nb_pg_tree += 1
   p = Signal("p%d_a" %nb_pg_tree,pi._arity)
   g = Signal("g%d_a" %nb_pg_tree,pi._arity)
   Generate ( "st_slansky.PGTree", "pgtree%d%s" % ( pi._arity, algo ), param = {'nbit' : pi._arity, 'algo' : algo} )
   Inst("pgtree%d%s" % ( pi._arity, algo ),
      map = {
         'pi' : pi,
         'gi' : gi,
         'p' : p,
         'g' : g,
         'vdd' : pi._st_cell._st_vdds[0],
         'vss' : pi._st_cell._st_vsss[0]
      }
   )
   return [p,g]

class PGTree(Model):

   def __init__(self, name, param):
      Model.__init__(self, name, param)
      self.nbit = param['nbit']
      if self.nbit < 1:
         raise Exception ( 'input arity should be greater than 0' )
      self.algo = param['algo']
      if self.algo != 'slansky':
         raise Exception ( '%s algorithm is not yet implemented' %self.algo )
        
   def Interface(self):
      self.pi = SignalIn("pi",self.nbit)
      self.gi = SignalIn("gi",self.nbit)
      self.p = SignalOut("p",self.nbit)
      self.g = SignalOut("g",self.nbit)

      self.vdd = VddIn("vdd")
      self.vss = VssIn("vss")

   def Netlist(self):
      nb_stages = log2(self.nbit)
      matrix = prefixMatrix(self.algo,self.nbit)
      p = []
      g = []
      p.append(self.pi)
      g.append(self.gi)
      # group propagation
      Generate ( "st_slansky.PG", "pg" )
      for i in range(0, nb_stages):
         p.append(Signal("p%d" %(i+1), self.nbit))
         g.append(Signal("g%d" %(i+1), self.nbit))
         for j in range(0, self.nbit):
            if (j % 2**i) == 0:
               k = j-1
            if matrix[i][j] == 1:
               Inst("pg",
                  map = {
                     'p1'  : p[i][j],
                     'g1'  : g[i][j],
                     'p2'  : p[i][k],
                     'g2'  : g[i][k],
                     'p'   : p[i+1][j],
                     'g'   : g[i+1][j],
                     'vdd' : self.vdd,
                     'vss' : self.vss
                  }
               )
            else:
               p[i+1][j] <= p[i][j].Buffer()
               g[i+1][j] <= g[i][j].Buffer()
      self.p <= p[nb_stages].Buffer()
      self.g <= g[nb_stages].Buffer()

   def GetParam ( cls ):

      return {'nbit' : 'integer'}

   GetParam = classmethod ( GetParam )


class PG(Model):
   def Interface(self):
      self.p1 = SignalIn("p1",1)
      self.g1 = SignalIn("g1",1)
      self.p2 = SignalIn("p2",1)
      self.g2 = SignalIn("g2",1)
      self.p = SignalOut("p",1)
      self.g = SignalOut("g",1)

      self.vdd = VddIn("vdd")
      self.vss = VssIn("vss")
           
   def Netlist(self):
       self.p <= self.p1 & self.p2
       self.g <= self.g1 | (self.g2 & self.p1)

def prefixMatrix(algo,size):
   if algo == 'slansky':
      nb_stages = log2(size)
      matrix = []
      for i in range(0, nb_stages):
         matrix.append([])
         connection = True
         j = -1
         while (j < size-1):
            connection = not connection
            k = -1
            while((k < 2**i-1) and (j < size-1)):
               j = j + 1
               k = k + 1
               if connection :
                  matrix[i].append(1)
               else:
                  matrix[i].append(0)
      return matrix
   else:
      raise Exception ( '%s algorithm is not yet implemented' %algo )
