##________________________________________________________________________________________________
##
##                                      CONV-TTL-RS485 PTS
##
##                                         CERN,BE/CO-HT
##________________________________________________________________________________________________
##
##------------------------------------------------------------------------------------------------
##
##                                 CONV-TTL-RS485 SFP EEPROM test
##
##------------------------------------------------------------------------------------------------
##
## Description  Testing of the SFP EEPROM chip on the CONV-TTL-RS485 board (SFP J1). The gateware
##              loaded to the on-board FPGA implements the interface for the communication
##              between the I2C bus on the P1 VME connector and the I2C of the SFP EEPROM; the
##              interface is an I2C Wishbone master, running on a 20 MHz system clock.
##
##              The I2C Wishbone master can be accessed at base address 0x140.
##              The I2C address of the SFP EEPROM is 0x50, predefined in the part number.
##
##              The test checks the presence of the SFP connector, reads the connector type and
##              verifies the received value.
##
## FW to load   pts.bit
## Authors      Julian Lewis (Julian.Lewis@cern.ch)
##              Theodor-Adrian Stana (t.stana@cern.ch)
## Website      http://www.ohwr.org/projects/pts
## Date         15/04/2013
##------------------------------------------------------------------------------------------------
##
##------------------------------------------------------------------------------------------------
##                               GNU LESSER GENERAL PUBLIC LICENSE
##                              ------------------------------------
## This source file is free software; you can redistribute it and/or modify it under the terms of
## the GNU Lesser General Public License as published by the Free Software Foundation; either
## version 2.1 of the License, or (at your option) any later version.
## This source 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 Lesser General Public License for more details.
## You should have received a copy of the GNU Lesser General Public License along with this
## source; if not, download it from http://www.gnu.org/licenses/lgpl-2.1.html
##-------------------------------------------------------------------------------------------------

##-------------------------------------------------------------------------------------------------
##                                            Import
##-------------------------------------------------------------------------------------------------

# Import system modules

import sys
sys.path.append("pyts")
import time
import os

# Import common modules

from ctypes import *
from ptsexcept import *
from vv_pts import *

from ptsdefine import *

##-------------------------------------------------------------------------------------------------
##                                           I2C class                                           --
##-------------------------------------------------------------------------------------------------

class COpenCoresI2C:

    R_PREL = 0x0
    R_PREH = 0x4
    R_CTR  = 0x8
    R_TXR  = 0xC
    R_RXR  = 0xC
    R_CR   = 0x10
    R_SR   = 0x10

    CTR_EN   = (1<<7)
    CR_STA   = (1<<7)
    CR_STO   = (1<<6)
    CR_WR    = (1<<4)
    CR_RD    = (1<<5)
    CR_NACK  = (1<<3)
    SR_RXACK = (1<<7)
    SR_TIP   = (1<<1)

    def scan_bus(self):
        for i in range(0,128):
            self.wr_reg(self.R_TXR, i<<1);
            self.wr_reg(self.R_CR, self.CR_STA | self.CR_WR);
            self.wait_busy()
            self.wr_reg(self.R_CR, self.CR_STO);
            self.wait_busy()


    def wr_reg(self, addr, val):
        self.bus.vv_write(self.base + addr,val)

    def rd_reg(self,addr):
        return self.bus.vv_read(self.base + addr)

    def __init__(self, bus, base, prescaler):
        self.bus = bus;
        self.base = base;
        self.wr_reg(self.R_CTR, 0);
        self.wr_reg(self.R_PREL, (prescaler & 0xff))
        self.wr_reg(self.R_PREH, (prescaler >> 8))
        self.wr_reg(self.R_CTR, self.CTR_EN);
        self.scan_bus()

    def wait_busy(self):
        tmo = 100
        while(self.rd_reg(self.R_SR) & self.SR_TIP):
            tmo = tmo -1
            if tmo <= 0:
                msg = "ERROR: SFP-EEPROM: Not responding"
                raise PtsError(msg)

    def start(self, addr, write_mode):
        addr = addr << 1
        if(write_mode == False):
            addr = addr | 1;
        self.wr_reg(self.R_TXR, addr);
        self.wr_reg(self.R_CR, self.CR_STA | self.CR_WR);
        self.wait_busy()

        if(self.rd_reg(self.R_SR) & self.SR_RXACK):
            pass

    def write(self, data, last):
        self.wr_reg(self.R_TXR, data);
        cmd = self.CR_WR;
        if(last):
            cmd = cmd | self.CR_STO;
        self.wr_reg(self.R_CR, cmd);
        self.wait_busy();
        if(self.rd_reg(self.R_SR) & self.SR_RXACK):
            pass

    def read(self, last):
        cmd = self.CR_RD;
        if(last):
            cmd = cmd | self.CR_STO | self.CR_NACK;
        self.wr_reg(self.R_CR, cmd);
        self.wait_busy();

        return self.rd_reg(self.R_RXR);


##-------------------------------------------------------------------------------------------------
##                                        EEPROM SFP class                                       --
##-------------------------------------------------------------------------------------------------

class EEPROM_SFP:

    def __init__(self, i2c, addr):
        self.i2c = i2c;
        self.addr = addr;

    def wr_reg16(self, addr, value):
        self.i2c.start(self.addr, True);
        self.i2c.write(addr, False);
        tmp = (value >> 8) & 0xFF;
        self.i2c.write(value, False);
        tmp = value & 0xFF;
        self.i2c.write(value, True)

    def wr_reg8(self, addr, value):
        self.i2c.start(self.addr, True);
        self.i2c.write(addr, False);
        self.i2c.write(value, True);

    def rd_reg16(self, addr):
        self.i2c.start(self.addr, True);
        self.i2c.write(addr, False);
        self.i2c.start(self.addr, False);
        tmp_MSB = self.i2c.read(False);
        tmp_LSB = self.i2c.read(True);
        value = (tmp_MSB << 8) | tmp_LSB;
        return value;

    def rd_reg8(self, addr):
        self.i2c.start(self.addr, True);
        self.i2c.write(addr, False);
        self.i2c.start(self.addr, False);
        return self.i2c.read(True);


##-------------------------------------------------------------------------------------------------
##                                             main                                              --
##-------------------------------------------------------------------------------------------------

def main(bus,tname,inf,log):

    """
    tests : SFP J1 EEPROM
    uses  : pts.bit and sfp_eeprom.py
    """

    SFP_EEPROM_I2C_ADDR = 0x50

    pel = PTS_ERROR_LOGGER(inf,log)

    try:
        # Create bus and EEPROM objects
        i2c = COpenCoresI2C(bus, SFP_EEPROM_BASE, 39);
        eeprom = EEPROM_SFP(i2c, SFP_EEPROM_I2C_ADDR);

        # Read SFP I2C identifier value, which should return 0x03 for an SFP or SFP+ connector type
        # No other connectors are currently supported
        # For more information, see INF-8074 (SFP) and SFF-8472 (SFP+)
        type = eeprom.rd_reg8(0x0);

        if (type == 0x03):
            inf.write("SFP type is correct: 0x%02X\n" % type)
        else:
            msg = "ERROR: SFP-EEPROM: Wrong connector type (0x%02X) - expected 0x03." % type
            pel.set(msg)

        return pel.get()

    except BusException, e:
        raise PtsError("SKT Exception: %s" % (e))

    except BusWarning, e:
        raise PtsError("SKT Warning: %s" % (e))

    #finally:
    #    return pel.get()
