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

##-------------------------------------------------------------------------------------------------
##                                                                                                |
##                                TDC_START_DIS, TDC_STOP_DIS test                                |
##                                                                                                |
##-------------------------------------------------------------------------------------------------
##                                                                                                |
## Description  Testing of the TDC_START_DIS and TDC_STOP_DIS signals; when activated no          |
##              timestamps should be registered by the ACAM.                                      |
##              The test is using thr Fine Delay as pulse generator sending pulses to all the TDC |
##              channels; if there are registered pulses one or both signals are faulty.          |
##                                                                                                |
##                                                                                                |
## 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
import pylab
from pylab import *
from datetime import datetime

# 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
sys.path.append('../../../common/fmc_delay/software/python/')
import fdelay_lib


##-------------------------------------------------------------------------------------------------
##                                             Main                                              --
##-------------------------------------------------------------------------------------------------

def main (default_directory='.'):

    # Constants declaration
    FMC_TDC_ADDR             = '1a39:0004/1a39:0004@0001:0000'#'1a39:0004/1a39:0004@000B:0000'
    FMC_TDC_BITSTREAM_PATH   = '../firmwares/tdc_allothertests.bit'#tdc_pllstatus.bit
    FMC_DELAY_ADDR           = '018d:0000/018d:0000@0002:0000'#'1a39:0004/1a39:0004@0005:0000'
    FMC_DELAY_BITSTREAM_PATH = '../firmwares/fmc_delay_spec.bin'
    FPGA_LOADER_PATH         = '../../../gnurabbit/user/fpga_loader'

    FMC_TDC_CHANNEL_NB       = 5
    CARRIER_CSR              = 0xC0000
    ACAM_STARTDIS            = 0x40
    ACAM_STOPDIS             = 0x44

    # SPEC object declaration
    spec = rr.Gennum()


#-----> TDC
    # Bind SPEC object to FMC TDC card
    print "\n-------------------------------------------------------------------"
    print "----------------------------- FMC TDC -----------------------------"
    print "------------------ TDC_START_DIS, TDC_STOP_DIS test ---------------\n"
    print ("Testing of the TDC_START_DIS and TDC_STOP_DIS signals;\n"
           "upon activation, no timestamps should be registered by the ACAM.\n"
           "The test configures the ACAM and is using thr Fine Delay as pulse\n"
           "generator sending pulses to all the TDC channels; if there are\n"
           "registered pulses one or both signals are faulty.\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)

    # Load FMC TDC firmware
    print "\n_________________________Initialisations_________________________\n"
    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(1)
    print "Firmware loaded!"

    # TDC object declaration
    tdc = fmc_tdc.CFMCTDC(spec)

    # Check bitsteam type
    bitstream_type = tdc.read_bitstream_type()
    if(bitstream_type == 0xFFFFFFFF):
        msg = ("FATAL ERROR: No access to TDC core")
        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)
    else:
        print('Access to TDC core OK')

    # Configure ACAM and TDC core registers
    print "\n__________________________Configuration__________________________\n"
    tdc.config_acam()
    time.sleep(1)
    tdc.reset_acam()
    acam_status_test = tdc.read_acam_status()-0xC4000800
    if acam_status_test == 0:
        print "ACAM IC8: Status register OK"
    else:
        msg = ("ERROR: ACAM IC8: No communication")
        os.close(spec.fd)
        print (msg)
        raise PtsError (msg)
    acam_readback_regs = []
    acam_readback_regs = tdc.readback_acam_config()
    for i in range(len(tdc.ACAM_READBACK_REGS)): 
        if (acam_readback_regs[i] == tdc.ACAM_READBACK_REGS[i]):
            print "ACAM IC8: reg 0x%02X: 0x%08X OK"%(tdc.ACAM_READBACK_ADDR[i], acam_readback_regs[i])
        else:
            msg = ("ERROR: ACAM IC8: Configuration registers failure; reg 0x%2X: received 0x%8X, expected 0x%8X"%(tdc.ACAM_READBACK_ADDR[i], acam_readback_regs[i], tdc.ACAM_READBACK_REGS[i]))
            os.close(spec.fd)
            print (msg)
            raise PtsError (msg)


    # Enable terminations of channels 1,2,3,4
    # channels 4 and 5 are receiving the same Fine Delay pulse; only channel 5 termination activated
    tdc.enable_channels()
    tdc.channel_term(1, 1)
    tdc.channel_term(2, 1)
    tdc.channel_term(3, 1)
    tdc.channel_term(5, 1)

    # Enable timestamps aquisition (TDC_START_FPGA pulse sent at this step) 
    tdc.start_acq()

    # StartDis, StopDis
    carrier_csr.wr_reg(ACAM_STARTDIS,0x1)
    carrier_csr.wr_reg(ACAM_STOPDIS,0x1)


#-----> Fine Delay
    # Bind SPEC object to FMC Fine Delay card
    print "\n___________________________Fine Delay____________________________"
    print "\nFine Delay initializations.."
    print "Fine Delay address to parse %s"%(FMC_DELAY_ADDR)
    for name, value in spec.parse_addr(FMC_DELAY_ADDR).iteritems():
        print "%s:0x%04X"%(name, value)
    spec.bind(FMC_DELAY_ADDR)

    # Load FMC Fine Delay firmware
    print "\nLoading FMC Fine Delay firmware...",
    sys.stdout.flush()
    firmware_loader = os.path.join(default_directory, FPGA_LOADER_PATH)
    bitstream = os.path.join(default_directory, FMC_DELAY_BITSTREAM_PATH)
    os.system(firmware_loader + ' ' + bitstream)
    time.sleep(1)
    print "Firmware loaded!"

    # Fine Delay object declaration
    try:
        fdelay = fdelay_lib.FineDelay(spec.get_fd())
    except:
        msg = ("ERROR: Fine Delay board Initiliazation failed")
        os.close(spec.fd)
        print (msg)
        raise PtsError (msg)

    # Set UTC and Coarse time in the Fine Delay
    fdelay.set_time(0, 0)
    fd_time = fdelay_lib.fd_timestamp()
    fd_time = fdelay.get_time()

    # Configure the Fine Delay as a pulse generator
    enable         = 1
    t_start_coarse = 0
    width          = 100000 # pulse width 100 ns
    delta          = 200000 # rising edges every 200 ns
    count          = 6

    all_measurs = []
    avg         = []

    for i in range(4):  # one iteration per channel;
                        # channels 4 and 5 receive pulses at the same time
#-----> Fine Delay
        time.sleep(0.5) # needed between iterations
        channel = i+1
        t_start_utc = fdelay.get_time().utc+1 # starting in  1 sec
        fdelay.conf_pulsegen(channel, enable, t_start_utc, t_start_coarse, width, delta, count)

#-----> TDC
    # Bind SPEC object to FMC TDC card
    spec.bind(FMC_TDC_ADDR)

    # Check if any timestamp arrived
    time.sleep(1)
    print "\n___________________ACAM StartDis, StopDis test___________________\n"
    print "The time now is                   : %s" %datetime.now()
    print "The temperature now is            : %3.3f°C" %tdc.mezz_get_temp()
    print "Fine Delay sent                   : 6 pulses of 200ns period to each TDC channel"
    print "TDC Write pointer                 : %d"%(tdc.get_pointer())
    if (tdc.get_pointer() == 0):
        print ("ACAM StartDis and StopDis signals : OK!")
    else:
        msg = (("ERROR: ACAM IC8: StartDis and/or StopDis: pulses registered, despite disabling;\n"
                "                                      in total %d pulses sent, %d timestamps registered") %(count*FMC_TDC_CHANNEL_NB,(tdc.get_pointer()/32)))
        os.close(spec.fd)
        print (msg)
        raise PtsError (msg)

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


if __name__ == '__main__' :
    main()
