    #!   /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 ptsexcept import *

from utilFunctions import *
from spec_identification import *

"""
test01: 
    SPI communication with ad9516
    ad9516 reference clock (OSC1/OSC2 presence)
    ad9516 PLL locking on reference clock (OSC freq?)
    ad9516 PLL RESET line
    ad9516 clock lines to FPGA (CLK1OUT)
    ad9516 measure clock frequency
    OscDac control
    OscDac control range
    TODO: find method to check SYNC line
"""

OSC_TUNNING_PPM_THD = 10
FIRMWARE_PATH = '/../gateware/syn/spec_top.bin'
INPUT_TIMEOUT  = 30;

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("IC9 (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("IC9 (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("IC9 (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("IC9 (AD9516): SPI communication FAILED on 0x55")
    
    print "IC9 (AD9516): SPI test succeeded"

def set_dac_ad9516(carrier, val):
    #set value
    carrier.writel(DDS_REG_OSCDAC, val );
    # write strobe
    carrier.writel(DDS_REG_OSCDAC, val | ( 1<<16) );
    carrier.writel(DDS_REG_OSCDAC, val );


def measure_clk1(carrier):
    # make sure that this function is called after AD9516 init

    # In revisision v3 of the FMC the CLK1OUT was renamed to FMC_CLK_LA0CC
    # This is the clock going directly from AD9516 to the FPGA
    
    print "*\n*\n*"
    print "Testing IC9 (AD9516 PLL), OSC2 and IC8 (DAC for OSC2 tuning)"
    print "*\n*\n*"
    
    # configure test
    samples=20;
    delay=0.1
    pll_mult = 5
    osc_freq_mhz = 25
    
    print "Checking 25 MHz oscillator tuning range."

    print "Measuring minimal frequency"
    # set DAC value to zero
    set_dac_ad9516(carrier, 0x0000)
    
    # wait for the clk frequency measurement
    time.sleep(2)

    # get the minimum frequency
    min_freq = carrier.readl(DDS_REG_CLK1)/pll_mult;
    
    
    print "Measuring maximum frequency"
    # set DAC value to max
    set_dac_ad9516(carrier, 0xffff)
    
    # wait for the clk frequency measurement
    time.sleep(2)
    
    # store the maximum frequency
    max_freq = carrier.readl(DDS_REG_CLK1)/pll_mult;
    
    # set medium value
    set_dac_ad9516(carrier, 0x7fff)
    
    # wait for the clk frequency measurement
    time.sleep(2)
    mean_freq = carrier.readl(DDS_REG_CLK1)/pll_mult;

    print "Measured frequency min:%d max:%d mean:%d [Hz]" % ( min_freq, max_freq, mean_freq )
    
    tuning_range = max_freq - min_freq
    print "Tuning range: %d. Expect it to be at least: %d (%d ppm)" % (tuning_range, OSC_TUNNING_PPM_THD*osc_freq_mhz, OSC_TUNNING_PPM_THD)
    
    if( tuning_range < OSC_TUNNING_PPM_THD*osc_freq_mhz ):
        raise PtsError("Oscillator control range is too low")
    else:
        print "Tuning range is correct"
    
    if(tuning_range < 0):
        raise PtsError("Frequency tuning range is wrong!")
    else:
        print "Frequency tuning range is correct!"
    

    print "Main oscillator tests succeeded"
     
def ad9516_test_ld(carrier ):    
    
    print "*\n*\n*"
    print "Testing AD9516 LD pin"
    print "*\n*\n*"

    print "*"
    print "The test forces the AD9516 to set LD line to high and low values."
    print "The line is verified in the FPGA any on the PLL lock led."
    print "*"

    print "Setting the LD to high"
    device = IC9(carrier);
    
    device.write_reg( 0x01A, 0x030 );
    device.write_reg( 0x232, 1);
    time.sleep(0.1)

    if( device.gpio_get(device.LD) == 0 ):
        raise PtsError("FPGA did not didn\'t read correct value of AD9516 LD pin. This might be caused by interrupted trace.")
    else:
        print "Hight value read by the FPGA"

    print >> sys.__stdout__, "Please check if PLL LED on FmcDac600m12b1chaDDS is ON and type [y/n]."
    tmp = yesnoquestion( "Please check if PLL LED on FmcDac600m12b1chaDDS is ON and type [y/n].", INPUT_TIMEOUT )
        
    if ( tmp == 0 ):
        raise PtsError("User verification of PLL LED on FmcDac600m12b1chaDDS failed. LED didn\'t turn on.")
    if ( tmp == 1 ):
        print "User verification of PLL LED on FmcDac600m12b1chaDDS succeeded"
    else:
        raise PtsError("User didn\'t verify FMC's PLL LED")
    
    print "Setting the LD to low"
    device.write_reg( 0x01A, 0x020 );
    device.write_reg( 0x232, 1);
    time.sleep(0.5)
    if( device.gpio_get(device.LD) != 0 ):
        raise PtsError("FPGA did not correct value of AD9516 LD pin. This might be caused by interrupted trace.")
    else:
        print "Low value read by the FPGA"

    print >> sys.__stdout__, "Please check if PLL LED on FmcDac600m12b1chaDDS is OFF and type [y/n]."
    tmp = yesnoquestion( "Please check if PLL LED on FmcDac600m12b1chaDDS is OFF and type [y/n].", INPUT_TIMEOUT )
    
    if ( tmp == 0 ):
        raise PtsError("User verification of PLL LED on FmcDac600m12b1chaDDS failed. LED didn\'t turn off.")
    if ( tmp == 1 ):
        print "User verification of PLL LED on FmcDac600m12b1chaDDS succeeded"
    else:
        raise PtsError("User didn\'t verify FMC's PLL LED")
        
    print "FMC's PLL LED tests succeeded"
    

def ad9516_test_reset(carrier):
    dev=IC9(carrier);
    
    print "*\n*\n*"
    print "Testing AD9516 RESET pin"
    print "*\n*\n*"

    print "*"
    print "The test checks if the PLL in the FPGA is locked to the AD9516 clock."
    print "PTS sets the reset for the AD9516 and checks if the FPGA PLL lost the sync"
    print "*"

    if( (carrier.readl( DDS_REG_FPGACTRL ) & DDS_FPGACTRL_MAINPLL() ) == 1 ):
        print "PLL in the FPGA is locked on the AD9516 clock"
    else:
        raise PtsError("PLL in the FPGA is not locked on the AD9516 clock. Can't proceed with AD9516 reset test.") 
    
    print "Reseting the AD9516"
    carrier.fmc_rst_n(0)
    time.sleep(0.1)

    if( (carrier.readl( DDS_REG_FPGACTRL ) & DDS_FPGACTRL_MAINPLL() ) == 0 ):
        print "PLL in the FPGA is NO longer locked on the AD9516 clock. The reset signal worked."
    else:
        raise PtsError("PLL in the FPGA is still locked on the AD9516 clock. The reset signal didn't work.") 
    
    print "AD9516 reset line tests succeeded"

    carrier.fmc_rst_n(1)


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 );
       
    # verifies 
    #    AD9516 locking on it's oscillator
    ad9516_dev=IC9(carrier);
    
    # verifies 
    #    SPI communication with the device
    spi_test(ad9516_dev)

    # verifies 
    #     presence of reference oscillator
    ad9516_dev.check_refclk();
    
    # verifies
    #    coarse measurement of oscillator frequency
    #    and tuning range
    #    tuning requires DAC's so these are also tested
    #       In revisision v3 of the FMC the CLK1OUT was renamed to FMC_CLK_LA0CC
    #       This is the clock going directly from AD9516 to the FPGA
    measure_clk1(carrier);
    
    # verifies LD line 
    ad9516_test_ld(carrier );

    # verifies reset line
    ad9516_test_reset(carrier);
    
    
    return 0
        
if __name__ == '__main__' :
    
    import argparse

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

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

