#!   ./python
#    coding: utf8

# Copyright CERN, 2013
# Author: Matthieu Cattin <matthieu.cattin@cern.ch>
# Licence: GPL v2 or later.
# Website: http://www.ohwr.org

# Import system modules
import sys
import time
import os

# Add common modules and libraries location to path
sys.path.append('../../../')
sys.path.append('../../../common/')
sys.path.append('../../../../svec_pts/ubuntu/pts/pyts/')

# Import common modules
from ptsexcept import *
from rr2vv import *

# Import specific modules
from fmc_adc_svec import *
from fmc_adc import *


"""
svec_test01: Test mezzanines (all peripherals, 1-wire, i2c, spi, etc...)
             Requires a pps pulse on trigger inputs.
"""

def main (default_directory='.'):

    # Constants declaration
    LUN = 0
    TEST_NB = 1
    FMC_ADC_BITSTREAM = '../../../../../firmwares/svec_fmcadc100m14b4cha.bin'
    FMC_ADC_BITSTREAM = os.path.join(default_directory, FMC_ADC_BITSTREAM)
    EXPECTED_BITSTREAM_TYPE = 0x0
    NB_CHANNELS = 4
    EEPROM_ADDR = 0x50
    SI570_ADDR = 0x55
    TEST_PATTERN = 0x6A1


    start_test_time = time.time()
    print "\n================================================================================"
    print "==> [SVEC] Test%02d start\n" % TEST_NB

    # SVEC object declaration
    print "Loading hardware access library and opening device.\n"
    bus = VME_rr_compatible(LUN)
    print "Initialising device.\n"

    # Load FMC ADC firmware
    ask = 'N'
    while ((ask != "Y") and (ask != "N")) :
        ask = raw_input("Do you want to load the firmware: %s? [y,n]"%(FMC_ADC_BITSTREAM))
        ask = ask.upper()
        print " "

    if (ask == "Y"):
        print "Loading FMC ADC firmware: %s\n" % FMC_ADC_BITSTREAM
        bus.vv_init()
        ret = bus.vv_load(FMC_ADC_BITSTREAM, 1)
        print('')
        time.sleep(2)
    else:
        bus.vv_open()

    # Carrier object declaration (SPEC board specific part)
    try:
        carrier = CFmcAdc100mSvec(bus, EXPECTED_BITSTREAM_TYPE)
    except FmcAdc100mSvecOperationError as e:
        raise PtsCritical("Carrier init failed, test stopped: %s" % e)

    # Test carrier periherals
    print('\n-------------------------------------------------------------')
    print('[Carrier]')
    carrier.print_unique_id()
    carrier.print_temp()

    # Mezzanines object declaration (FmcAdc100m14b4cha board specific part)
    fmc = []
    for i in range(2):
        try:
            print('\n-------------------------------------------------------------')
            print('[FMC slot %d]'%(i+1))
            mezz_offset = 0x2000+i*0x4000
            print('Mezzanine offset: 0x%08X'%(mezz_offset))
            fmc.append(CFmcAdc100m(bus, mezz_offset))
        except FmcAdc100mOperationError as e:
            raise PtsCritical("Mezzanine %d init failed, test stopped: %s" % (i+1, e))

    # Test mezzanines peripherals
    error = ['','']
    for i in range(2):
        try:
            print('\n-------------------------------------------------------------')
            print('[FMC slot %d]\n'%(i+1))
            # 1-wire
            fmc[i].print_unique_id()
            fmc[i].print_temp()

            # System i2c bus
            EEPROM_ADDR = EEPROM_ADDR+i*0x2
            periph_addr = fmc[i].sys_i2c_scan()
            if(0 == len(periph_addr)):
                error[i] = "No peripheral detected on system management I2C bus"
                continue
            if(1 != len(periph_addr)):
                error[i] = "Signal integrity problem detected on system management I2C bus, %d devices detected instead of 1" % len(periph_addr)
            if(EEPROM_ADDR != periph_addr[0]):
                error[i] = "Wrong device mounted on system management I2C bus or soldering issues, address is:0x%.2X expected:0x%.2X" % (periph_addr[0],EEPROM_ADDR)
                continue

            # Timetag core
            fmc[i].print_utc_core_regs()


            # LEDs
            print('\nBlinking LEDs')
            for j in range(3):
                fmc[i].trig_led(1)
                fmc[i].acq_led(1)
                time.sleep(.5)
                fmc[i].trig_led(0)
                fmc[i].acq_led(0)
                time.sleep(.5)

            # i2c bus
            periph_addr = fmc[i].i2c_scan()
            if(0 == len(periph_addr)):
                error[i] = 'No peripheral detected on I2C bus'
                continue
            if(1 != len(periph_addr)):
                error[i] = 'Signal integrity problem detected on I2C bus, %d devices detected instead of 1'%(len(periph_addr))
                continue
            if(SI570_ADDR != periph_addr[0]):
                error[i] = 'Wrong device mounted on I2C bus, address is:0x%.2X expected:0x%.2X'%(periph_addr[0],SI570_ADDR)
                continue

            # CSR
            fmc[i].print_adc_core_config()

            # LTC2174 (SPI, serdes)
            fmc[i].testpat_en(TEST_PATTERN)
            pattern = fmc[i].get_testpat()
            if(TEST_PATTERN != pattern):
                print('pattern:%.4X read:%.4X')%(TEST_PATTERN, pattern)
                error[i] = 'Cannot access LTC2174 ADC through SPI'
                continue
            fmc[i].print_adc_regs()
            if(0 == fmc[i].get_serdes_sync_stat()):
                error[i] = 'SerDes are not synchronised'
                continue
            else:
                print "SerDes are synchronised.\n"
            print "Check received data."
            for j in range(1,NB_CHANNELS+1):
                adc_value = fmc[i].get_current_adc_value(j)
                adc_s = '0b{0:0>16b}'.format(int(adc_value))
                pat_s = '0b{0:0>16b}'.format(int(TEST_PATTERN<<2))
                print('ADC channel %d value:%s' % (j, adc_s))
                print('           expected:%s' % (pat_s))
                if((TEST_PATTERN<<2) != adc_value):
                    print('[Channel %d] Error: value mismatch!'%(j))
                    error[i] = '[Channel %d] LTC2174 test pattern error: value mismatch!'%(j)
                    continue

            # External trigger
            # hw trig, rising edge, external, sw disable, no delay
            print('')
            fmc[i].set_trig_config(1, 0, 1, 1, 1, 0, 0)
            fmc[i].set_pre_trig_samples(500)
            fmc[i].set_post_trig_samples(500)
            fmc[i].set_shots(1)
            fmc[i].stop_acq()
            print "Acquisition FSM state : %s (should be IDLE)" % fmc[i].get_acq_fsm_state()
            fmc[i].start_acq()
            print "Wait for trigger."
            time.sleep(1.1)
            if('WAIT_TRIG' == fmc[i].get_acq_fsm_state()):
                error[i] = 'Acquisition FSM state : %s' % fmc[i].get_acq_fsm_state()
                continue
            else:
                print('The external trigger input is working fine.')

            # DDR access
            print('\nTest DDR access')
            ddr_data_rd = fmc[i].get_data(0x0, 16, raw=True)

            ddr_data_wr = range(8)
            fmc[i].put_data(0x0, ddr_data_wr)
            ddr_data_wr.extend([0x0]*8)

            ddr_data_rdb = fmc[i].get_data(0x0, 16, raw=True)

            ddr_data_exp = ddr_data_wr[0:8] + ddr_data_rd[8:16]

            print('addr: read:       written:    read back   expected:')
            for j in range(len(ddr_data_rd)):
                print('%.3d   0x%.8x  0x%.8x  0x%.8x  0x%.8x'%(j, ddr_data_rd[j], ddr_data_wr[j], ddr_data_rdb[j], ddr_data_exp[j]))
            if ddr_data_rdb != ddr_data_exp:
                error[i] = 'Error in ddr access'
                continue

        except FmcAdc100mOperationError as e:
            raise PtsError("Mezzanine %d onewire test failed: %s" % (i+1, e))

    print('')
    print "==> End of test%02d" % TEST_NB
    print "================================================================================"
    end_test_time = time.time()
    print "[SVEC] Test%02d elapsed time: %.2f seconds\n" % (TEST_NB, end_test_time-start_test_time)

    err_s = ''
    if (error[0] != ''):
        err_s = '[FMC slot 1]' + error[0] + '\n'
    if (error[1] != ''):
        err_s += '[FMC slot 2]' + error[1]

    if (err_s != ''):
        raise PtsError(err_s)

    bus.vv_close()


if __name__ == '__main__' :
    main()
