

import sys
import random
import time
import math
import os
import signal
import time

from ctypes import *
from ptsexcept import *

from regs_lib import *
from utilFunctions import *

#     print massage that have to be logged
def log( msg ):
    print msg

#     print question for user - doesn't need to be logged
def info( msg ):
    print msg
    

class ad9516:

    conf_raddr = [ 0x0000,0x0001,0x0002,0x0003,0x0004,0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001A,0x001B,0x001C,0x001D,0x001E,0x001F,0x00A0,0x00A1,0x00A2,0x00A3,0x00A4,0x00A5,0x00A6,0x00A7,0x00A8,0x00A9,0x00AA,0x00AB,0x00F0,0x00F1,0x00F2,0x00F3,0x00F4,0x00F5,0x0140,0x0141,0x0142,0x0143,0x0190,0x0191,0x0192,0x0193,0x0194,0x0195,0x0196,0x0197,0x0198,0x0199,0x019A,0x019B,0x019C,0x019D,0x019E,0x019F,0x01A0,0x01A1,0x01A2,0x01A3,0x01E0,0x01E1,0x0230,0x0231,0x0232]


    def __init__(self, carrier):
        
        self.carrier = carrier;        
        self.carrier.fmc_rst_n(1)

        # set communication method
        self.set_serial_format()
        time.sleep(0.5);
         
    
    def check_refclk(self):
        print "Checking if reference clock frequency is higher then 1 MHz (if osc is correctly placed)"
        if(self.read_reg( 0x1f ) & (1 << 1)):
            print "AD9516 reference clock detected"
        else:
            raise PtsCritical("AD9516: reference frequency lower then 1 MHz! OSC1/OSC2 or their connections to AD9516 might be broken.")
    
    def check_lock(self):
        for i in range(50):
	    time.sleep(0.5)
            if(self.read_reg( 0x1f) & (1 << 0)):
                print "AD9516 PLL locked after: " + str((i+1)*0.5) + "s"
                break    
        else:
            raise PtsCritical("AD9516 PLL did NOT lock. This might be caused by placing wrong OSC1/OSC2 oscillator.")
        

    def sync_outputs(self):
        # software sync all outputs
        self.write_reg( 0x230, 1);
        self.write_reg( 0x232, 1);
        self.write_reg( 0x230, 0);
        self.write_reg( 0x232, 1);
    
    def read_reg( self, reg ):
        rval = self.carrier.spi_txrx( self.spi_cs, 24, ( (reg & 0xfff) << 8) | (1<<23) )
        return rval & 0xff;

    
    def write_reg(self, reg, val):
        self.carrier.spi_txrx( self.spi_cs, 24, ((reg & 0xfff) << 8) | val );   

    
    def set_serial_format(self):
        self.write_reg( 0, 0x99);
        self.update_config()

    def update_config(self):
        self.write_reg( 0x232, 1);

######################## LOWEST LEVEL FUNCTIONS ####################################

    def write_config(self, cfg):
                
        self.set_serial_format()

        # load configuration
        # skip register 0
        # configuration software doesn't let me change it's status and keeps useless default
        for i in range( 1, len( cfg ) ):
            self.write_reg( cfg[i][0], cfg[i][1]);
           
        # update loaded registers
        self.update_config()


    def vcxo_cal(self):    
        # read register in whitch vcxocal field is located
        vcxocalreg=self.read_reg(0x18)

        # make sure that vcxocal field is zeroed
        self.write_reg(0x18, vcxocalreg&0xfe)

        # update loaded registers
        self.update_config()

        # set the vcxocal field
        self.write_reg(0x18, vcxocalreg | 1)

        # update loaded registers
        self.update_config()
    
    def read_config(self):
        print "Current configuration\n"

        for i in range( len( self.conf_raddr ) ):
            regaddr = self.conf_raddr[i]
            regval = self.read_reg( regaddr)
            print "\"{:04X}\",\"{:08b}\",\"{:02X}\"".format(regaddr, regval, regval)

class IC9(ad9516):


    SYNC_N=0;
    LD=3;
    
    def __init__(self,carrier ):
        self.spi_cs = DDS_GPIOR_PLL_SYS_CS_N()
        self.carrier = carrier;

        self.gpio_set( self.SYNC_N, 1);
        ad9516.__init__(self,carrier)
        
        # set reset signal
        time.sleep(0.1);

        self.write_config_default()
        self.check_lock()
        self.sync_outputs()
        
        print "Checking if PLL in FPGA locked on the clock generated by AD9516 in order to verify clock line"
        if( (self.carrier.readl( DDS_REG_FPGACTRL ) & DDS_FPGACTRL_MAINPLL() ) == 0 ):
            raise PtsCritical("PLL in FPGA did not lock on clock signal from AD9516. Clock line might be corrupted.")
        else:
            print "FPGA PLL locked."

    def gpio_set(self, pin, value):
        if pin == self.SYNC_N:
            self.carrier.gpio_set( DDS_GPIOR_PLL_SYS_SYNC_N(), value);

    def gpio_get(self, pin):
        if pin == self.SYNC_N:
            return self.carrier.gpio_get( DDS_GPIOR_PLL_SYS_SYNC_N());
        elif pin == self.LD:
            return self.carrier.gpio_get( DDS_GPIOR_PLL_SYS_LD());

    def write_config_default(self):
        print "Writing default config for AD9516"
        # self.write_config(self.conf_raddr)
        cwd=os.path.dirname(os.path.realpath(__file__))
        self.write_config(load_config(cwd+'/../doc/ad9516_pts_orig_cmos8.stp'))
    


class IC26(ad9516):

    PIN_STATUS=4
    PIN_FUNCTION = 5

    def __init__(self,carrier ):
        self.spi_cs = DDS_GPIOR_PLL_VCXO_CS_N()
        ad9516.__init__(self,carrier)

    def gpio_set(self, pin, value):
        if pin == self.PIN_FUNCTION:
            self.carrier.gpio_set( DDS_GPIOR_PLL_VCXO_FUNCTION(), value);

    def gpio_get(self, pin):
        if pin == self.PIN_STATUS:
            return self.carrier.gpio_get( DDS_GPIOR_PLL_VCXO_STATUS_N());
        elif pin == self.PIN_FUNCTION:
            return self.carrier.gpio_get( DDS_GPIOR_PLL_VCXO_FUNCTION());

    def input_sel_dac_clk(self,  val):
        self.carrier.gpio_set( DDS_GPIOR_PLL_VCXO_REF_SEL(), val);

    def test_config(self):      
        cwd=os.path.dirname(os.path.realpath(__file__))
        self.write_config(load_config(cwd+'/../doc/new_ad9516_pts_test.stp'))

        self.vcxo_cal()
