#!   /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 the Pendulum as pulse generator that in collaboration with the  |
##              Timetech Fanout and the USB-interconnection-box is sending pulses to each one of  |
##              the TDC channels; if there are registered pulses one or both signals are faulty.  |
##                                                                                                |
##                                                                                                |
## 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 numpy import *
from ctypes import *
from datetime import datetime

# Add common modules location to path
sys.path.append('../../../../pts/')
sys.path.append('../../../../pts/gnurabbit/python/')
sys.path.append('../../../../pts/common/')
sys.path.append('../../../../pts/common/usb_box')
sys.path.append('../../../../pts/common/cp210x')

# Import common modules
from ptsexcept import *
import rr
import csr
import fmc_tdc
from cp210x_eeprom import *
from PendulumCNT91 import *
from calibr_box import *
from find_usb_tty import *

# Pendulum
PENDULUM           = "/dev/usbtmc1"
USB_DEVICE         = "/dev/ttyUSB0"
RS232_BAUD         = 57600

def pendulum_output_on(meas):
    meas.write("OUTP:TYPE PULS")
    meas.write("SOUR:PULS:PER 0.5")
    meas.write("SOUR:PULS:WIDT 0.1")
    return (1)

def pendulum_output_off(meas):
    meas.write("OUTP:TYPE OFF")
    return (1)

# USB-interconnection-box
BOX_USB_VENDOR_ID = 0x10c4 # Cygnal Integrated Products, Inc.
BOX_USB_DEVICE_ID = 0xea60 # CP210x Composite Device


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

def main (default_directory='.'):

    start_test_time = time.time()

#-----> PCIe address, firmware, loader constants
    FMC_TDC_ADDR             = '1a39:0004/1a39:0004@000B:0000'
    FMC_TDC_BITSTREAM_PATH   = '../firmwares/tdc_allothertests.bit'
    FPGA_LOADER_PATH         = '../../../../pts/gnurabbit/user/fpga_loader'

#-----> FPGA constants
    FMC_TDC_CHANNEL_NB       = 5
    CARRIER_CSR              = 0xC0000
    ACAM_STARTDIS            = 0x40
    ACAM_STOPDIS             = 0x44


#-----> FPGA initializations 
    # SPEC object declaration
    spec = rr.Gennum()

    # 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 the Pendulum as pulse\n"
           "generator that in collaboration with the Timetech Fanout and the\n"
           "USB-interconnection-box is sending pulses to each one of the TDC\n"
           "channels; if there are registered pulses one or both signals are\n"
           "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)

    # 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)
    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")
        print (msg)
        raise PtsError (msg)
    if(bitstream_type != 0x1):
        msg = ("FATAL ERROR: Wrong bitstream type loaded 0x%08X") %bitstream_type
        print (msg)
        #raise PtsError (msg)
    else:
        print('Access to TDC core OK')


#-----> ACAM and TDC configuration
    # 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")
        print (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]))
            print (msg)
            raise PtsError (msg)

    # DAC
    tdc.configure_mezz_dac(0xA9CF)

    # Interrupts
    print "\n____________________________IRQs_________________________________\n"
    print('Set IRQ enable mask: %.4X')%tdc.set_irq_en_mask(0x1F)
    tdc.set_irq_tstamp_thresh(0x100)
    tdc.set_irq_time_thresh(0xFFFFF)

    # Enable channels and terminations of all channels
    tdc.enable_channels()
    tdc.channel_term(1, 1)
    tdc.channel_term(2, 1)
    tdc.channel_term(3, 1)
    tdc.channel_term(4, 1)
    tdc.channel_term(5, 1)


#-----> USB-interconnection-box
    # Communication and setup of Interconnection box
    print "\n_____________________USB interconnection box______________________\n"
    box_tty = CttyUSB().find_usb_tty(BOX_USB_VENDOR_ID, BOX_USB_DEVICE_ID)
    print("Device: %s" % box_tty[0])
    box = CCalibr_box(box_tty[0])
    box.select_trigger_loopback(0)
    box.select_output(4)


#-----> Pendulum
    # Communication with Pendulum
    print "\n__________________________USB Pendulum_____________________________\n"
    try:
        meas = PendulumCNT91(PENDULUM)
    except OSError:
        raise PtsError('CNT-91 is not switched on or badly connected, aborting.')
    a = pendulum_output_off(meas)


#-----> TDC
    # Enable channels and start timestamps acquisition (TDC_START_FPGA pulse sent at this step)
    print "\n______________________ACAM acquisition start________________________\n"

    tdc.start_acq()
    print ("Sending the ACAM START pulse\n")

    # StartDis, StopDis
    carrier_csr = csr.CCSR(spec, CARRIER_CSR)
    carrier_csr.wr_reg(ACAM_STARTDIS,0x1)
    carrier_csr.wr_reg(ACAM_STOPDIS,0x1)

    # sending pulses to all the channels
    print ("Sending pulses of period 8 ms to each TDC STOP channel")
    for i in range(4): # for the 4 pairs of channels (1,2), (1,3), (1,4), (1,5)

        otherch                   = i+1
        r_edge_timestamps_ch1     = []
        r_edge_timestamps_otherch = []
        all_measurs               = []

        # USB-interconnection-box
        box.select_trigger_loopback(0)
        box.select_output(4-i)

        # pendulum output on
        a = pendulum_output_on(meas)

        # pendulum sending pulses
        time.sleep(1.2)

        # pendulum output off
        a = pendulum_output_off(meas)


    # 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 "TDC write pointer                 : %d"%(tdc.get_pointer())
    print "TDC overflows                     : %d"%(tdc.get_overflow_counter())
    if (tdc.get_pointer() == 0 and tdc.get_overflow_counter() == 0):
        print ("ACAM StartDis and StopDis signals : OK!")
    else:
        msg = (("ERROR: ACAM IC8: StartDis and/or StopDis: pulses registered, despite disabling;\n"
                "                                        16 pulses sent, %d timestamps registered") %((tdc.get_pointer()/32))) # assuming get_overflow_counter=0
        os.close(spec.fd)
        print (msg)
        raise PtsError (msg)


    print "\n\n--------------------------------"
    end_test_time = time.time()
    print "Test elapsed time: %.2f seconds\n\n\n" % (end_test_time-start_test_time)


if __name__ == '__main__' :
    main()
