#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
raddds.py specializes Acq400 for RADCELF triple DDS device
- enumerates all site services, available as uut.sX.knob
- monitors transient status on uut, provides blocking events
- read_channels() reads all data from channel data service.
- simple property interface allows natural "script-like" usage
 - eg::
       uut1.s0.set_arm = 1
 - equivalent to running this on a logged in shell session on the UUT::
       set.site1 set_arm=1
Created on Sun Jan  8 12:36:38 2017
@author: pgm
"""
from . import acq400
from . import netclient
from builtins import staticmethod
[docs]class AD9854:
[docs]    class CR:
        regular_en = '0061'
        chirp_en   = '8761'
        low_power  = '0041'
        power_down = '1F000001'
        zero_hz = '00044041'
        CLR_ACC2 = 1<<14 
        
[docs]    @staticmethod
    # CR for clock * n
    def CRX(n = 4, mode=CR.low_power, clr_acc2=False): 
        ca2 = AD9854.CR.CLR_ACC2 if clr_acc2 else 0           
        return '{:08x}'.format(int(n << 16) | int(mode, 16) | ca2) 
        
[docs]    @staticmethod
    # UCR for chirps_per_sec
    def UCR(chirps_per_sec, intclk=300e6):
        return '{:08x}'.format(int(intclk/2/chirps_per_sec)) 
        
    
[docs]    @staticmethod
    def ftw2ratio(ftw):
        return float(int('0x{}'.format(ftw), 16)/float(0x1000000000000)) 
    
[docs]    @staticmethod
    def ratio2ftw(ratio):
        return format(int(ratio * pow(2, 48)), '012x')   
    
[docs]    @staticmethod
    def CRX_chirp_off(n = 4):
        return '{:08x}'.format(int(n << 16) | int(AD9854.CR.low_power, 16)) 
    
[docs]    @staticmethod
    def CRX_zero_hz(clr_acc2=True):
        return AD9854.CR.zero_hz 
    
[docs]    @staticmethod
    def CRX_power_down(clr_acc2=True):
        return AD9854.CR.power_down  
  
[docs]class AD9512:
[docs]    class DIVX:
        div4 = '1100'
        passthru = '0080' 
        
[docs]    @staticmethod
    def setDIVX(clkd, value):
        clkd.DIV0     = value
        clkd.DIV1     = value
        clkd.DIV2     = value
        clkd.DIV3     = value
        clkd.DIV4     = value
        clkd.UPDATE   = '01' 
        
[docs]    @staticmethod
    def clocksON(clkd):        
        clkd.LVPECL1 = '08'
        clkd.LVPECL0 = '08'
        clkd.UPDATE  = '01'  
         
[docs]class RAD3DDS(acq400.Acq400):
    
[docs]    @staticmethod 
    def best_clock_pps_sync(fs):
        return fs//512 * 512; 
    
[docs]    @staticmethod
    def ftw2ratio(ftw):
        return AD9854.ftw2ratio(ftw) 
    
    
[docs]    @staticmethod
    def ratio2ftw(ratio):
        return AD9854.ratio2ftw(ratio) 
    
[docs]    @staticmethod
    def pulse(knob):
        knob = 1
        knob = 0 
    
[docs]    def chirp_freq(self, idds):
        # idds 0: A, 1: B
        assert idds >= 0 and idds <= 1
        return acq400.freq(self.s0.get_knob('SIG_TRG_S{}_FREQ'.format(2+idds))) 
                                  
[docs]    def dds_freq(self, idds):
        # idds 0: A, 1: B, 2: C
        assert idds >= 0 and idds <= 2 
        return acq400.freq(self.s0.get_knob('SIG_CLK_S{}_FREQ'.format(3+idds))) 
    
[docs]    def radcelf_init(self):
        # port of original RADCELF_init shell script
    #Reset the entire clock chain
        RAD3DDS.pulse(self.s2.clkd_hard_reset)
        
        self.clkdA.CSPD     = '00'
        self.clkdA.UPDATE   = '01'
# Set Primary Clock LVPECL 2 Off, set LVDS 3 to Off, Set LVDS 4 to TTL
        self.clkdA.LVPECL2  = '0a'
        self.clkdA.LVDS3    = '01'
        self.clkdA.LVDS4    = '08'
        self.clkdA.UPDATE   = '01'
# Set Secondary Clock LVPECL 2 Off, set LVDS 3 to TTL
        self.clkdB.LVPECL2  = '0a'
        self.clkdB.LVDS3    = '08'
        self.clkdB.UPDATE   = '01'
#Set all the clkdA AD9512 dividers to divide by 4 to avoid overheat
#100MHz / 4 = 25Mhz source clock
        AD9512.setDIVX(self.clkdA, AD9512.DIVX.div4)       
        # set clkdB to pass-thru
        AD9512.setDIVX(self.clkdB, AD9512.DIVX.passthru)
            
# Reset the DDS
        RAD3DDS.pulse(self.s2.ddsX_hard_reset)
    
#Switch the clocks off on the DDS Devices to stop I/O Updates
#Clock Remapping DDS - Device clkA Output 1
        self.clkdA.LVPECL1  = '0a'
        self.clkdA.UPDATE   = '01'
#The two Main DDS devices on device clkB Outputs 0 and 1
        self.clkdB.LVPECL0  = '0a'
        self.clkdB.LVPECL1  = '0a'
        self.clkdB.UPDATE   = '01'
# Write to the Control Registers on the 3 DDS devices - 
# External I/O Update and SDO On
# Set the RefClk Multiplier on at x4 switch off the Inverse Sinc Filter
        self.ddsA.CR = AD9854.CR.low_power
        self.ddsB.CR = AD9854.CR.low_power
        self.ddsC.CR = AD9854.CR.low_power
#Switch the Clocks back on again
        AD9512.clocksON(self.clkdA)
        AD9512.clocksON(self.clkdB)
# tell FPGA to take over the clocking
        self.s2.ddsA_upd_clk_fpga = 1
        self.s2.ddsB_upd_clk_fpga = 1
        self.s2.ddsC_upd_clk_fpga = 1
        self.ddsA.strobe_mode = 1
        self.ddsB.strobe_mode = 1
        self.ddsC.strobe_mode = 1 
        
[docs]    def __init__(self, _uut, monitor=True):
            acq400.Acq400.__init__(self, _uut, monitor)
            site = 4
            for sm in [ 'ddsA', 'ddsB', 'ddsC']:                
                self.svc[sm] = netclient.Siteclient(self.uut, acq400.AcqPorts.SITE0+site)
                self.mod_count += 1
                site += 1
            site = 7
            for sm in [ 'clkdA', 'clkdB']:
                self.svc[sm] = netclient.Siteclient(self.uut, acq400.AcqPorts.SITE0+site)
                self.mod_count += 1
                site += 1