#!/usr/bin/env python

import sys
import os
import os.path
import subprocess
import optparse
from   helpers.io import ErrorMessage
from   helpers.io import catch


class Yosys ( object ):

    ReadRTLIL   = 0x0001
    ReadVerilog = 0x0002
    Flatten     = 0x0004
    KeepTcl     = 0x0008
    ReadMask    = ReadRTLIL | ReadVerilog

    def __init__ ( self ):
        self.flags       = Yosys.ReadRTLIL
        self.libertyFile = None
        return

    def setFlags ( self, mask, value ):
        if value: self.flags |=  mask
        else:     self.flags &= ~mask
        return

    def setFlatten ( self, value ):       self.setFlags( Yosys.Flatten, True )
    def setKeepTcl ( self, value ):       self.setFlags( Yosys.KeepTcl, True )

    def setLiberty ( self, libertyFile ):
        if not os.path.isfile(libertyFile):
          raise ErrorMessage( 1, [ 'Yosys.setLiberty(): Can\'t find liberty file.'
                                 , '"%s"' % libertyFile ] )
            
        self.libertyFile = libertyFile
        return

    def setInputRTLIL ( self ):
        self.setFlags( Yosys.ReadMask , False )
        self.setFlags( Yosys.ReadRTLIL, True  )
        return

    def setInputVerilog ( self ):
        self.setFlags( Yosys.ReadMask   , False )
        self.setFlags( Yosys.ReadVerilog, True  )
        return

    def run ( self, designName, top='top' ):
        if self.flags & Yosys.ReadRTLIL:
          if not os.path.isfile(designName+'.il'):
            raise ErrorMessage( 1, 'Yosys.run(): Can\'t find RTLIL design file "%s.il".' % designName )
          tclScript = 'yosys read_ilang   %(designName)s.il\n'
        else:
          if not os.path.isfile(designName+".v"):
            raise ErrorMessage( 1, 'Yosys.run(): Can\'t find Verilog design file "%s.v".' % designName )
          tclScript = 'yosys read_verilog %(designName)s.v\n'

        tclScript += 'yosys hierarchy -check -top %(designTop)s\n' \
                     'yosys synth            -top %(designTop)s\n'

        if self.flags & Yosys.Flatten:
          tclScript += 'yosys flatten               %(designTop)s\n' \
                       'yosys hierarchy        -top %(designTop)s\n'

        tclScript += 'yosys dfflibmap -liberty    %(libertyFile)s\n' \
                     'yosys memorydff\n'                             \
                     'yosys abc       -liberty    %(libertyFile)s\n' \
                     'yosys clean\n'                                 \
                     'yosys write_blif %(designName)s.blif\n'

        tclFile = designName + '.tcl'
        tclFd   = open( tclFile, 'w' )
        tclFd.write( tclScript % { 'designName' : designName
                                 , 'designTop'  : top
                                 , 'libertyFile': self.libertyFile
                                 } )
        tclFd.close()

        status = subprocess.call( [ 'yosys', '-c', tclFile ] )

        if not (self.flags & Yosys.KeepTcl): os.unlink( tclFile )
        return status


if __name__ == '__main__':

   parser = optparse.OptionParser()
   parser.add_option ( '-d', '--design'    , action='store'     , type='string', dest='design'   , help='The name of the design file, without extension.' )
   parser.add_option ( '-t', '--top'       , action='store'     , type='string', dest='top'
                                                                               , default='top'   , help='The hierarchy top level name model.' )
   parser.add_option ( '-f', '--flatten'   , action='store_true',                dest='flatten'  , help='Flatten the design hierarchy.' )
   parser.add_option ( '-k', '--keep-tcl'  , action='store_true',                dest='keepTcl'  , help='Keep the Yosys TCL command script.' )
   parser.add_option ( '-i', '--input-lang', action='store'     , type='string', dest='inputLang', help='Set the input description language (RTLIL or Verilog).' )
   parser.add_option ( '-l', '--liberty'   , action='store'     , type='string', dest='liberty'  , help='Set the path to the liberty file (.lib).' )
   (options, args) = parser.parse_args()

   try:
     yosys = Yosys()
     if options.inputLang == 'Verilog': yosys.setInputVerilog()
     if options.inputLang == 'RTLIL'  : yosys.setInputRTLIL()
     if options.flatten: yosys.setFlatten( True )
     if options.keepTcl: yosys.setKeepTcl( True )
     if options.liberty: yosys.setLiberty( options.liberty )
     
     rcode = yosys.run( options.design, top=options.top )

   except Exception, e:
     catch( e )
     sys.exit(2)

   sys.exit( rcode )
