##________________________________________________________________________________________________
##
##                                      CONV-TTL-RS485 PTS
##
##                                         CERN,BE/CO-HT
##________________________________________________________________________________________________
##
##------------------------------------------------------------------------------------------------
##
##                                   CONV-TTL-RS485 SFP test
##
##------------------------------------------------------------------------------------------------
##
## Description  This Python module performs the testing of the MGT module on the CONV-TTL-RS485 FPGA
##              and the connections to the small form-factor pluggable (SFP) module.
##              For this test the SFP Loopback Adapter Module meeds to be plugged into the SFP
##              connector. The idea of the test is to loopback the transmitter and the receiver
##              of the link and check if the link is established. Loopback takes place through the
##              SFP loopback adapter.
##
##              Gateware inside the FPGA implements an endpoint module together with a miniNIC
##              module to implement the communication. There are also 16kB of packet RAM available
##              to the two modules. The test program views these three components as one, with the
##              base address of the unified component as the endpoint's base address.
##
##              Thejendpoint module is located at address 0x200 (offset 0x000),
##              The miniNIC module is located  at address 0x400 (offset 0x200),
##              The 16 kB RAM is located       at address 0x800 (offset 0x600)
##
## GW 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         03/11/2014
##-------------------------------------------------------------------------------------------------
##
##------------------------------------------------------------------------------------------------
##                               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 traceback

# Import common modules

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

from ptsdefine import *

##-------------------------------------------------------------------------------------------------
##                                          Minic class                                          --
##-------------------------------------------------------------------------------------------------

class CMinic:

    EP_REG_ECR       = 0x0
    EP_REG_MACL      = 0x28
    EP_REG_MDIO_CR   = 0x2c
    EP_REG_MDIO_SR   = 0x30
    EP_REG_IDR       = 0x34
    EP_MDIO_SR_READY = 0x80000000
    EP_ECR_TX_EN     = 0x40
    EP_ECR_RX_EN     = 0x80
    MDIO_MCR         = 0
    MDIO_MSR         = 1
    MDIO_MCR_PDOWN   = (1<<11)
    MDIO_MCR_RESET   = (1<<15)
    MDIO_MSR_LSTATUS = (1<<2)

    def __init__(self, bus, base):
        self.base = base;
        self.bus = bus;
        self.init_ep();

    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 ep_readl(self, addr):
        return self.rd_reg(SFP_ENDPOINT_OFS + addr)

    def ep_writel(self, addr, val):
        self.wr_reg(SFP_ENDPOINT_OFS + addr, val) # used to be self.PTS_SFP_ENDPO...

    def pcs_readl(self, addr):
        val = 0
        self.ep_writel(self.EP_REG_MDIO_CR, addr << 16);

        tmo = 10000
        while (val & self.EP_MDIO_SR_READY) == 0:
            val = self.ep_readl(self.EP_REG_MDIO_SR)
            tmo = tmo -1
            if tmo <= 0:
                msg = "ERROR: MINIC: Not responding"
                raise PtsError(msg)

        return (val & 0xffff)

    def pcs_writel(self, addr, val):
        val = 0
        self.ep_writel(self.EP_REG_MDIO_CR, (addr << 16) | (1 << 31) | val);

        tmo = 10000
        while (val & self.EP_MDIO_SR_READY) == 0:
            val = self.ep_readl(self.EP_REG_MDIO_SR)
            tmo = tmo -1
            if tmo <= 0:
                msg = "ERROR: MINIC: Not responding"
                raise PtsError(msg)

    def init_ep(self):
        idr =  self.ep_readl(self.EP_REG_IDR)
        self.ep_writel(self.EP_REG_ECR, self.EP_ECR_TX_EN | self.EP_ECR_RX_EN)

    def reset_phy(self):
        self.pcs_writel(self.MDIO_MCR, self.MDIO_MCR_PDOWN);
        self.pcs_writel(self.MDIO_MCR, self.MDIO_MCR_RESET);
        self.pcs_writel(self.MDIO_MCR, 0);

    def link_up(self):
        return self.pcs_readl(self.MDIO_MSR) & self.MDIO_MSR_LSTATUS

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

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

    """
    tests : SFP J1
    uses  : pts.bit and sfp_test.py
    """

    pel = PTS_ERROR_LOGGER(inf,log)

    try:

        # SFP link test
        msg = "Begin SFP link test"
        print msg
        inf.write("\n%s\n" % (msg))

        minic = CMinic(bus, SFP_BASE);
        minic.init_ep()
        minic.reset_phy()
        minic.link_up()

        if minic.link_up():
            msg = "SFP link up OK"
            inf.write("%s\n" % (msg))
        else:
            msg = "ERROR: SFP high speed link down"
            pel.set(msg)

        return pel.get()

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

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

