#!   /usr/bin/env   python

# Copyright Creotech SA, 2015
# Author: Marek Guminski <marek.guminski@creotech.pl>
# Licence: GPL v2 or later.
# Website: http://www.ohwr.org


import sys
import time
import os
import time



from spec import *
from ad9516 import *
from dac import *
from ptsexcept import *
from sys import __stdout__

from spec_identification import *

"""
    test AD9516 (IC26)
        Check SPI communication
        Communication over direct signals
        Connectivity of input and output clocks

"""

FIRMWARE_PATH = '/../gateware/syn/spec_top.bin'

TEST_RAW_DAC = False
CHIP = "IC26 (AD9516)"


def spi_test(dut):

    # Regiser 0x193 - Divider 1 low cycles
    # Output 1 not used
    raddr = 0x193
    
    written = 0xFF;
    dut.write_reg(raddr, written);
    read = dut.read_reg(raddr);
    
    if written != read:
        raise PtsError("IC26 (AD9516): SPI communication FAILED on 0xFF")
    
    time.sleep(0.001)
    
    written = 0x00;
    dut.write_reg(raddr, written);
    read = dut.read_reg(raddr);
    
    if written != read:
        raise PtsError("IC26 (AD9516): SPI communication FAILED on 0x00")
    
    time.sleep(0.001)
    
    written = 0xAA;
    dut.write_reg(raddr, written);
    read = dut.read_reg(raddr);
    
    if written != read:
        raise PtsError("IC26 (AD9516): SPI communication FAILED on 0xAA")
    
    time.sleep(0.001)
    
    written = 0x55;
    dut.write_reg(raddr, written);
    read = dut.read_reg(raddr);
    
    if written != read:
        raise PtsError("IC26 (AD9516): SPI communication FAILED on 0x55")
    
    print "IC26 (AD9516): SPI test succeeded"
    

# mast be called after SPI test
def test_reset_line(carrier, dut):
    
    print "*\n*\n*"
    print "Testing the reset line"
    print "*\n*\n*"
    
    print "Enabling the chip reset"
    carrier.fmc_rst_n(0)

    
    print "Trying to communicate over SPI. Should fail."
    try:
        spi_test(dut);
        raise PtsError("SPI communication didn't fail altho the reset line was enabled")
    except PtsError:
        print "IC26 (AD9516): Function line was verified"
        
    print "Disabling the chip reset"
    carrier.fmc_rst_n(1)
    
def test_refclk_presense(pll,dacinst):
    
    print "*\n*\n*"
    print "Testing %s REF input and LD outpu" % (CHIP)
    print "*\n*\n*"

    
    print "Configuring the DAC to output 10 MHz"
    dacinst.set_en( 1 );
    dacinst.set_shape( dacinst.SHAPE_SINE );
    dacinst.set_ampl( 1 );
    dacinst.set_frequency( 10e6 );
    
    print "DAC clock is connected to %s REF input on the PCB" % CHIP

    print "Configuring IC26 (AD9516) to lock on DAC clock:"
    pll.test_config()
    time.sleep(2)

    
    print "Checking if the PLL is locked via SPI register:"
    pll.check_lock()
    
    print "Checking if the PLL is locked via LD pin:"
    if pll.gpio_get(pll.PIN_STATUS) :
        print "LD is high"
    else:
        raise PtsError("%d: LD line value did't match lock status") % CHIP

    print "*"
    print "Disabling the DAC"
    print "*"
    dacinst.set_en(0);

    time.sleep(1)

    print "Checking if the PLL is locked via SPI register:"
    if(pll.read_reg( 0x1f) & (1 << 0)):
        raise PtsError("%s is locked altho the clock source is disabled") % CHIP
    else:
        print "%s is not locked because the clock source is disabled" %CHIP

    print "Checking if the PLL is locked LD pin:"
    if not pll.gpio_get(pll.PIN_STATUS) :
        print "%s: LD line value matched the lock status" % CHIP
    else:
        raise PtsError("%d: LD line value did't match lock status") % CHIP


def verify_clk2_freq(carrier, set_freq = 50e6, max_diff = 5e6 ):
    
    time.sleep(2)
   
    mean = carrier.readl(DDS_REG_CLK2)
    
    print "Measured frequency: %d. Expected: [%d, %d]." % (mean, set_freq-max_diff, set_freq+max_diff)
    
    if abs( set_freq-mean ) > max_diff :
        return 1
    else:
        return 0


   
def test_clk2(carrier, pll,dacinst):

    print "*\n*\n*"
    print "Testing clock line between %s output 6 and FPGA" % (CHIP)
    print "*\n*\n*"

    print "Testing following clock path: \
    IC9----------FMC_CLK_LA0CC_P--->\
    FPGA---------DAC iface--------->\
    DAC----------DAC_SPLIT_OUT2_P---REFIN---->\
    IC26(REFIN)----FMC_CLK_P--------->\
    FPGA"

    print "Configuring the DAC to output 10 MHz"
    dacinst.set_en( 1 );
    dacinst.set_shape( dacinst.SHAPE_SINE );
    dacinst.set_ampl( 1 );
    dacinst.set_frequency( 10e6 );

    print "Configuring the IC26 to lock on REF input and generate 50 Mhz (multiplied by two in the FPGA PLL)"
    pll.test_config()
    time.sleep(2)

    print "Checking if the PLL locked:"
    pll.check_lock()

    # the clock is multiplied by 2 in the FPGA PLL
    if verify_clk2_freq(carrier, 100e6, 1e6) > 0:
        raise PtsError("Test failed!")
    else:
        print "Test succeeded!"

    
def test_refin2clk(carrier, pll,dacinst):

    print "*\n*\n*"
    print "Testing clock line between DAC and IC24 clock mux"
    print "*\n*\n*"
    
    print "Testing following clock path: \
    IC9----------FMC_CLK_LA0CC_P--->\
    FPGA---------DAC iface--------->\
    DAC----------DAC_SPLIT_OUT2_P---REFIN---->\
    IC24(CLK0)---AD_CLK_P---------->\
    IC26(CLK)----FMC_CLK_P--------->\
    FPGA"

    print "Selecting CLK0 on IC24 clock mux."
    pll.input_sel_dac_clk(1)

    print "Enabling DAC (that is driven by clock from IC9"
    dacinst.set_en( 1 );
    dacinst.set_shape( dacinst.SHAPE_SINE );
    dacinst.set_ampl( 1 );
    dacinst.set_frequency( 10e6 );

    print "Configuring the IC26 (AD9516) to forward this clock to output 6 (FPGA)"
    cwd=os.path.dirname(os.path.realpath(__file__))
    pll.write_config(load_config(cwd+'/../doc/new_ad9516_pts_ad9510_clk.stp'))

    time.sleep(2)
    
    # measure frequency in FPGA
    # the clock is multiplied by 2 in the FPGA PLL
    if verify_clk2_freq(carrier, 20e6, 1e6) > 0:
        raise PtsError("RF IN input might be corrupted")

def test_rfin2clk(carrier, pll,dacinst):
    
    print "*\n*\n*"
    print "Testing clock line between RF IN connector and IC24 clock mux and between DAC and REF CLOCK OUT connector "
    print "*\n*\n*"
    
    print "Testing following clock path: \
    IC9----------FMC_CLK_LA0CC_P--->\
    FPGA---------DAC iface--------->\
    DAC----------DAC_OUT---REF CLOCK OUT---->\
    LEMO cable---RF IN-----RF_IN_X->\
    IC24(CLK1)---AD_CLK_P---------->\
    IC26(CLK)----FMC_CLK_P--------->\
    FPGA"

    print "Selecting CLK1 on IC24 clock mux."
    pll.input_sel_dac_clk(0)

    print "Enabling DAC (that is driven by clock from IC9"
    dacinst.set_en( 1 );
    dacinst.set_shape( dacinst.SHAPE_SINE );
    dacinst.set_ampl( 1 );
    dacinst.set_frequency( 10e6 );


    print "Configuring the IC26 (AD9516) to forward this clock to output 6 (FPGA)"
    cwd=os.path.dirname(os.path.realpath(__file__))
    pll.write_config(load_config(cwd+'/../doc/new_ad9516_pts_ad9510_clk.stp'))

    time.sleep(2)
    
    # measure frequency in FPGA
    # the clock is multiplied by 2 in the FPGA PLL
    if verify_clk2_freq(carrier, 20e6, 1e6) > 0:
        raise PtsError("RF IN input might be corrupted")


def main (card=None, default_directory='.',suite=None, configure_fpga=True):

    specid = spec_identification()[0];
    if specid < 0 :
        raise PtsCritical("Bus number of FmcDac600m12b1chadds was not found")

    carrier=spec( FIRMWARE_PATH, specid, configure_fpga );
    # sys_pll = ad9516(carrier);
    sys_pll = IC9(carrier);
    
    # default configuration
    # vcxo_pll = ad9516(carrier, IC9=False);
    vcxo_pll = IC26(carrier);
    cwd=os.path.dirname(os.path.realpath(__file__))
    vcxo_pll.write_config(load_config(cwd+'/../doc/new_ad9516_pts_test.stp'))
    
    dacinst = dac(carrier);
    

    print "*\n*\n*"
    print "Testing the SPI communication"
    print "*\n*\n*"
    # check SPI communication
    spi_test(vcxo_pll);
    
    # check function line
    test_reset_line(carrier, vcxo_pll);

    # repeat the configuration after reset
    sys_pll = IC9(carrier);
    vcxo_pll.write_config(load_config(cwd+'/../doc/new_ad9516_pts_test.stp'))
    dacinst = dac(carrier);

    # check refclk detect register
    test_refclk_presense(vcxo_pll, dacinst);

    # validate CLK2 lines to FPGA and IC26 REFIN
    test_clk2(carrier, vcxo_pll,dacinst)
     
    # validate REF OUT output connector
    test_rfin2clk(carrier, vcxo_pll,dacinst)
         
    test_refin2clk(carrier, vcxo_pll,dacinst)

    return 0
        
if __name__ == '__main__' :
    
    import argparse

    parser = argparse.ArgumentParser(description='Start test03')
    parser.add_argument('-p', action='store_true')
    args = vars(parser.parse_args())

    if args['p']:
        main(configure_fpga=False)
    else:
        main()

