#!/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      :                      Ludovic NOURY               |
# |  E-mail      :              Ludovic.Noury@lip6.fr               |
# | =============================================================== |
# |  Py Module   :       "./stratus.py"                             |
# | *************************************************************** |
# |  U p d a t e s                                                  |
# |                                                                 |
# x-----------------------------------------------------------------x

# $Id: patread.py,v 1.1 2007/07/20 13:30:15 cobell Exp $
# LIP6/ASIM ludovic.noury@lip6.fr
__all__ = ['PatRead']

from stratus import *

## Read a pattern file and analyze it
#
# This class is designed to load and analyze output result test
# vectors from simulation. Currently it only supports Alliance ".pat"
# format.
#
# @remark we suppose that the source vector was created by a PatWrite
# instance (See PatWrite documentation).
# @todo also get time unit (with first pattern)
# @todo add code for '?uuuu' and 'uuuu'
# @todo replace compile regexps
# @version $Id: patread.py,v 1.1 2007/07/20 13:30:15 cobell Exp $
class PatRead:
    # Public attributes doxydoc ----------------------------
    ## @var connectors
    # store input/output connectors order.

    ## @var connectors_current_values
    # dictionary, key=connector_name

    ## @var current_time
    # current pattern time stamp
    
    # Private attributes -----------------------------------
    ## @var _f
    # file object linked to the output file.
    ## @var _p
    # Shortcut to the regular expression used to split strings

    # ------------------------------------------------------
    ## The constructor
    #
    # Open the pattern file, initialize instance attributes and call
    # _get_interface method.
    #
    # @param filename Pattern to load & analyze filename
    def __init__(self, filename):
        # Init instance attributes
        self.connectors = []
        self.connectors_current_values = {}
        self.current_time = 0
        self._p=re.compile(r'\W+')

        # Open input file
        self._f = open(filename, 'r')

        # Get interface
        self._get_interface()

        # Regular expressions

        # used to dermine if a value can be used
        self._re_valid = re.compile('[XxUu]') 

    # ------------------------------------------------------
    ## Extract interface from input pattern file 
    #
    # Read the input pattern until the line with "begin" indicating
    # interface declaration end.
    # For each connector (or internal signal) extracts direction, name
    # and arity.
    #
    # @remark arity extraction suppose N+1 downto 0 declaration form
    # and 'B' option. Ok, if pattern generated by PatWrite instance.
    #
    # @remark For signal, the direction is set to the value 'signal'
    def _get_interface(self):
        # Shortcuts to regexps
        re_in=re.compile("^in")
        re_out=re.compile("^out")
        re_signal=re.compile("^signal")

        # Extract connectors data from file until "begin" line.
        for line in self._f:
            # Remove leading spaces
            line = line.lstrip()
            
            # Stop if end of interface
            if re.compile("^begin").search(line):
                break
            # Connector
            if (re_in.search(line) or re_out.search(line) or re_signal.search(line)):
                pieces = self._p.split(line)
                connector = {}
                connector['direction'] = pieces[0]
                connector['name'] = pieces[1]
                # (arity>1) => vector
                # '' is to pass synopsys output pattern bug :
                # in ck ;; instead of
                # in ck B;; gives pieces[2] == '' instead of 'B'
                if (pieces[2] != 'B') and (pieces[2] != ''):
                    connector['arity'] = int(pieces[2])-int(pieces[4])+1
                else:
                    connector['arity'] = 1
                #DEBUG: print '%s=>%s\n' % (line, connector)
                self.connectors.append(connector)

    # ------------------------------------------------------
    ## Update current_time and connectors_current_values with next
    # pattern values.
    #
    # @return Return True if a pattern has been loaded. Else return
    # False.
    # @todo call end method if end of patterns ?
    def get_next_pattern(self):
        for line in self._f:
            # Stop if end of patterns
            if re.compile("^end").search(line):
                return False
            # Special case, only one pattern
            if re.compile("\s*:").match(line):
                pieces = self._p.split(line)
                # Update time
                self.current_time = 0
                # Update connectors values dictionary 
                for pos in range(len(self.connectors)):
                    name = self.connectors[pos]['name']
                    self.connectors_current_values[name] = pieces[pos+1]
                return True
            # We got a pattern
            if re.compile("^<").search(line):
                pieces = self._p.split(line)
                # Update time
                self.current_time = int(pieces[1])
                # Update connectors values dictionary 
                for pos in range(len(self.connectors)):
                    name = self.connectors[pos]['name']
                    self.connectors_current_values[name] = pieces[pos+3]
                return True


    # ----------------------------------------------------------------
    ## Return true if the value is either X or U.
    #
    # @param signal signal name (string).
    # @return True is the signal is invalid.
    #
    # Before trying to convert a value we need to be sure it's a valid
    # value.
    def is_invalid(self, signal):
        # match is not ok because we wouldn't find partial invalid as '01XXX'
        return self._re_valid.search(self.connectors_current_values[signal])
        
    

    
    # ----------------------------------------------------------------
    def __del__(self):
        self._f.close()
