#!   /usr/bin/env   python
#    coding: utf8
##_________________________________________________________________________________________________
##                                                                                                |
##                                           |TDC PTS|                                            |
##                                                                                                |
##                                         CERN,BE/CO-HT                                          |
##________________________________________________________________________________________________|

##-------------------------------------------------------------------------------------------------
##                                                                                                |
##                                        TDC clocks test                                         |
##                                                                                                |
##-------------------------------------------------------------------------------------------------
##                                                                                                |
## Description  Testing of the clocks coming from the PLL IC4 on the TDC mezzanine:               |
##               o 31.25 MHz FPGA_TDC_REF_CLK                                                     |
##               o 125   MHz CLK125                                                               |
##                                                                                                |
##              Upon initialization, the firmware loaded to the FPGA on the carrier is configuring|
##              the TDC PLL and is receiving the output clock signals; dedicated counters in the  |
##              FPGA count clock cycles and inform the PCIe interface of the counting.            |
##                                                                                                |
##              In detail these are the registers used for the control & evaluation of the clks:  |
##               0x00014: bit      0: PLL status bit                                              |
##               0x00024: bit      0: counters reset,  active high                                |
##               0x00000: bit      0: counters enable, active high                                |
##               0x0002C: bits 31..0: 31.25 MHz FPGA_TDC_REF_CLK_P counter                        |
##               0x0003C: bits 31..0: 125.0 MHz CLK125 counter                                    |
##                                                                                                |
##                                                                                                |
## FW to load   .bin                                                                              |
## Authors      Evangelia Gousiou (Evangelia.Gousiou@cern.ch)                                     |
## Website      http://www.ohwr.org/projects/pts                                                  |
## Date         11/01/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
import time
import os
import math

# Add common modules location tp path
sys.path.append('../../../')
sys.path.append('../../../gnurabbit/python/')
sys.path.append('../../../common/')

# Import common modules
from ptsexcept import *
import rr
import csr

# Import specific modules
import fmc_tdc



def main (default_directory='.'):

    # Constants declaration
    FMC_TDC_ADDR            = '1a39:0004/1a39:0004@000B:0000'
    FMC_TDC_BITSTREAM_PATH  = '../firmwares/tdc_pts_clks.bit'
    FPGA_LOADER_PATH        = '../../../gnurabbit/user/fpga_loader'

    # Addressing for the retrieval of carrier csr information
    CARRIER_CSR          = 0x00000 # base address
    CSR_TYPE_VER         = 0x00
    CSR_BSTM_TYPE        = 0x04
    CSR_BSTM_DATE        = 0x08
    CSR_STATUS           = 0x0C
    CSR_CTRL             = 0x10
    PCB_VER_MASK         = 0x000F
    CARRIER_TYPE_MASK    = 0xFFFF0000
    STATUS_FMC_PRES      = (1<<0)

    # Addressing for the control and monitoring of PLL clocks
    PLL_STATUS           = 0x14
    RST_COUNTERS         = 0x24
    EN_COUNTERS          = 0x28
    CLK_31M25_COUNTER    = 0x2C
    CLK_125M_COUNTER     = 0x3C
   
    # SPEC object declaration
    spec = rr.Gennum()

    # Bind SPEC object to FMC TDC card
    print "\n-------------------------------------------------------------------"
    print "----------------------------- FMC TDC -----------------------------"
    print "--------------------------- Clocks test ---------------------------\n"
    print ("Test of the PLL clocks: 31.25MHz FPGA_TDC_REF_CLK and 125MHz CLK125.\n"
           "The test starts by configuring the PLL IC4. Then, to verify the clock\n"
           "frequencies, there are dedicated counters implemented in the carrier FPGA.\n")

    print "\n_______________________________Info______________________________\n"
    print "FMC TDC address to parse: %s"%(FMC_TDC_ADDR)
    for name, value in spec.parse_addr(FMC_TDC_ADDR).iteritems():
        print "%s:0x%04X"%(name, value)
    spec.bind(FMC_TDC_ADDR)
    carrier_csr = csr.CCSR(spec, CARRIER_CSR)
    tdc         = fmc_tdc.CFMCTDC(spec)

    print "\n_________________________Initialisations_________________________\n"
    # Load FMC TDC firmware
    print "Loading FMC TDC firmware...",
    firmware_loader = os.path.join(default_directory, FPGA_LOADER_PATH)
    bitstream = os.path.join(default_directory, FMC_TDC_BITSTREAM_PATH)
    os.system(firmware_loader + ' ' + bitstream)
    time.sleep(1)
    firmware_loader = os.path.join(default_directory, FPGA_LOADER_PATH)
    bitstream = os.path.join(default_directory, FMC_TDC_BITSTREAM_PATH)
    os.system(firmware_loader + ' ' + bitstream)
    time.sleep(2)
    print "Firmware loaded!"


    # Check bitsteam type
    bitstream_type = carrier_csr.rd_reg(CSR_BSTM_TYPE)
    print('Bitstream type     : 0x%.8X') % bitstream_type
    if(bitstream_type == 0xFFFFFFFF):
        msg = ("FATAL ERROR: Firmware not properly loaded")
        os.close(spec.fd)
        print (msg)
        raise PtsCritical (msg)
    if(bitstream_type != 0x1):
        msg = ("FATAL ERROR: Wrong bitstream type loaded")
        os.close(spec.fd)
        print (msg)
        raise PtsCritical (msg)

    # Carrier Information
    print("PCB version        : %d") % (PCB_VER_MASK & carrier_csr.rd_reg(CSR_TYPE_VER))
    print("Carrier type       : %d") % ((CARRIER_TYPE_MASK & carrier_csr.rd_reg(CSR_TYPE_VER))>>16)
    print("Bitstream type     : 0x%.8X") % (carrier_csr.rd_reg(CSR_BSTM_TYPE))
    print("Bitstream date     : 0x%.8X") % (carrier_csr.rd_reg(CSR_BSTM_DATE))
    print("Status             : 0x%.8X") % (carrier_csr.rd_reg(CSR_STATUS))
    print("Control            : 0x%.8X") % (carrier_csr.rd_reg(CSR_CTRL))

    # Check mezzanine presence flag
    status = carrier_csr.rd_reg(CSR_STATUS)
    if(status & STATUS_FMC_PRES):
        print('Carrier csr status : 0x%.8X') % status
        msg = ("FATAL ERROR: Mezzanine appears missing.")
        os.close(spec.fd)
        print (msg)
        raise PtsCritical  (msg)
    else:
        print("Mezzanine present  : OK")


    time.sleep(3)

    print "\n\n___________________________PLL status____________________________\n"
    pll_status = carrier_csr.rd_reg(PLL_STATUS)
    if pll_status == 1:
        print ("PLL status OK\n")
    else:
        msg = ("ERROR: PLL IC4 status bit at zero.")
        os.close(spec.fd)
        print (msg)
        raise PtsError (msg)

    print "\n_____________________________Clocks______________________________\n"
    carrier_csr.wr_reg(EN_COUNTERS,0x1)  # counters enabled
    carrier_csr.wr_reg(RST_COUNTERS,0x1) # counters under reset
    time.sleep(1)
    carrier_csr.wr_reg(RST_COUNTERS,0x0) # counters reset release
    time.sleep(2)                        # counters counting

    clk_31M25 = ((carrier_csr.rd_reg(CLK_31M25_COUNTER))/ (2*1e6))
    clk_125M  = ((carrier_csr.rd_reg(CLK_125M_COUNTER))/ (2*1e6))
    
    # FPGA_TDC_REF_CLK 31.25MHz clock evaluation
    if (clk_31M25 > 32.8) or (clk_31M25 < 29.6): # 5% limits due to software inacuracies
        msg = (("ERROR: PLL IC4: FPGA_TDC_REF_CLK: measured frequency %.2f MHz is out of accepted limits [29.6MHz..32.8MHz]") % (clk_31M25))
        os.close(spec.fd)
        print (msg)
        raise PtsError (msg)
    else:
        print("FPGA_TDC_REF_CLK: measured frequency: %.2f  MHz OK") % (clk_31M25)

    # CLK125 125MHz clock evaluation
    if (clk_125M > 131) or (clk_125M < 118):     # 5% limits due to software inacuracies in time measurement (time.sleep(2))
        msg = (("ERROR: PLL IC4: CLK125: measured frequency %.2f MHz is out of accepted limits [118MHz..131MHz]") % (clk_125M))
        os.close(spec.fd)
        print (msg)
        raise PtsError (msg)
    else:
        print("CLK125:           measured frequency: %.2f MHz OK") % (clk_125M)


    os.close(spec.fd)
    print "\n\n-----------------------------------------------------------------"

if __name__ == '__main__' :
    main()
