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

##-------------------------------------------------------------------------------------------------
##                                                                                                |
##                               TDC ENABLE_INPUTS test with ACAM                                 |
##                                                                                                |
##-------------------------------------------------------------------------------------------------
##                                                                                                |
## Description  Testing of the ENABLE_INPUTS signal.                                              |
##              The Fine Delay is used as pulse generator sending pulses to each one of the TDC   |
##              channels. On the TDC board the ACAM chip is registering the arriving pulses.      |
##              The test starts with the ENABLE_INPUTS activated and is checking if the expected  |
##              amount of pulses is arriving to each channel.                                     |
##              Then the ENABLE_INPUTS is deactivated and the test is checking if pulses arrive   |
##              to any of the channels.                                                           |
##                                                                                                |
##              Note that the tdc_pts_inputs_fpga_enable.py is a similar test enabling and        |
##              disabling the ENABLE_INPUTS signal and testing for pulses on the TDC_IN_FPGA      |
##              lines, rather than the TDC_STOP lines that are tested in this test.               |
##                                                                                                |
## FW to load   tdc_allothertests.bit                                                             |
## 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
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@000B:0000'
    FMC_TDC_BITSTREAM_PATH   = '../firmwares/tdc_allothertests.bit'
    FMC_DELAY_ADDR           = '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

    # SPEC object declaration
    spec = rr.Gennum()


#-----> TDC
    # Bind SPEC object to FMC TDC card
    print "\n-------------------------------------------------------------------"
    print "----------------------------- FMC TDC -----------------------------"
    print "----------------- TDC_STOP channels disable test ------------------\n"
    print ("Test of EN_INPUTS signal. The test is using the Fine Delay board as\n"
           "pulse generator, sending pulses to each one of the TDC channels.\n"
           "The test configures the ACAM chip and then retrieves the generated timestamps.\n"
           "When the EN_INPUTS is disabled no pulse should be retrieved.\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)

    # 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)

    # Disable the 5 TDC channels
    tdc.disable_channels()
    for ch in range(1,FMC_TDC_CHANNEL_NB+1):
        tdc.channel_term(ch, 1)

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


#-----> 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          = 100000000 # pulse width 100 ms
    delta          = 200000000 # rising edges every 200 ms


    # string to keep the ERROR messages from all the channels
    all_msg = ""

    for i in range(4):  # one iteration per channel;
                        # channels 4 and 5 receive pulses at the same time

        time.sleep(0.5) # needed between iterations

#-----> Fine Delay
        # Bind SPEC object to FMC Fine Delay card
        spec.bind(FMC_DELAY_ADDR)
        channel = i+1
        count   = 6+(2*i)
        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 timestamps arrived
        print "\n____________________TDC_STOP%d disable test_____________________\n" %channel
        time.sleep(1.5)
        print "The time now is                   : %s" %datetime.now()
        print "The temperature now is            : %3.3f°C" %tdc.mezz_get_temp()
        print "Fine Delay sent                   : %d pulses of 200ns period"%(count)
        print "TDC Write pointer                 : %d"%(tdc.get_pointer())
        timestamps, data = tdc.get_timestamps(0)

        if (i < 3):

            timestamp_arrived = 0
            for m in range(len(timestamps)):
                if ((timestamps[m][2] == 1) and (timestamps[m][1] == i)):
                    timestamp_arrived = timestamp_arrived+1
            if (timestamp_arrived != 0):
                msg = (("ERROR: ENABLE_INPUTS disabled: TDC_STOP%d failure; %d pulses sent, %d pulses received") %(channel, count, timestamp_arrived))   
                print (msg)
                all_msg+=msg
            else:
                print("Channel disabling                 : OK! no pulses arrived")

        else: # channels 4 and 5 are receiving timestamps at the same time

            timestamp_arrived_ch4 = 0
            timestamp_arrived_ch5 = 0
            for m in range(len(timestamps)):
                if ((timestamps[m][2] == 1) and (timestamps[m][1] == 3)):
                    timestamp_arrived_ch4 = timestamp_arrived_ch4+1
                elif ((timestamps[m][2] == 1) and (timestamps[m][1] == 4)):
                    timestamp_arrived_ch5 = timestamp_arrived_ch5+1
            if (timestamp_arrived_ch4 != 0):
                msg = (("ERROR: ENABLE_INPUTS disabled: TDC_STOP%d failure; %d pulses sent, %d pulses received") %(channel, count, timestamp_arrived_ch4))
                print (msg)
                all_msg+=msg
            else:
                print("Channel disabling                 : OK! no pulses arrived") 

            if (timestamp_arrived_ch5 != 0):   
                print "\n___________________TDC_STOP5 disable test____________________\n"
                msg = (("ERROR: ENABLE_INPUTS disabled: TDC_STOP5 failure; %d pulses sent, %d pulses received") %(count, timestamp_arrived_ch5))
                print (msg)
                all_msg+=msg
            else:
                print "\n___________________TDC_STOP5 disable test____________________\n"
                print("Channel disabling                 : OK! no pulses arrived") 

    if (all_msg != ""):
        os.close(spec.fd)
        raise PtsError (all_msg)

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


if __name__ == '__main__' :
    main()
