

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

from ctypes import *
from ptsexcept import *

from regs_lib import *
import OneWireMaster
import ds18d20


RF_MODE_MASTER = 0;
RF_MODE_SLAVE = 1;


class spec:

    speclib_path = '/../software/spec-sw/tools/libspec.so';
    rflib_path = '/../software/rf-lib.so';
    BASE_ADDR =     0x00010000;
    ONEWIRE_ADDR =  0x00020000;
    UIDEL = 1;
    SDB_SIG = 0x5344422D;
    GW_ID = 0xd00bb11d;
 
    
    def __init__(self, firmware, bus = -1, allwaysload = True):
        
        self.cwd = os.path.dirname(__file__)
	#the above dosn't work when code is sourced after importing it in python interactive terminal
        if self.cwd=="":
            self.cwd = os.getcwd()

        self.bus = bus;
        self.firmware = firmware;
        
        self.speclib = CDLL(self.cwd + self.speclib_path);
        self.rflib = CDLL(self.cwd + self.rflib_path);
        self.speclib.spec_readl.restype = c_uint32;
        
        print  "Initialising SPEC board..." 
        self.card = self.speclib.spec_open( c_int(self.bus), c_int(-1) );
        if(self.card < 1):
            raise PtsCritical("SPEC enumeration failed");
        
        
        self.gwid = self.readl(DDS_REG_GW);   
        if (self.gwid != self.GW_ID ):
            load = True;
        else:
            load = False;
            
        if allwaysload or load:
            print "Firmware loading"
            if( self.speclib.spec_load_bitstream(c_void_p( self.card ), c_char_p( self.cwd + self.firmware ) ) == 0 ):
                raise PtsCritical("Firmware loader failure");
            
            time.sleep(3);
        
              
        self.sdbid = self.speclib.spec_readl( c_void_p(self.card), c_uint32(0) );
        self.gwid = self.readl(DDS_REG_GW);
        self.get_fmc_present();
        
#         Set SPI/I2C pins to inactive state
        self.writel( DDS_REG_CR, 0);
        self.gpio_set( DDS_GPIOR_PLL_SYS_RESET_N(), 1);
        self.gpio_set( DDS_GPIOR_PLL_SYS_CS_N(), 1);
        self.gpio_set( DDS_GPIOR_PLL_VCXO_CS_N(), 1);
        self.gpio_set( DDS_GPIOR_PLL_VCXO_FUNCTION(), 1);
        self.gpio_set(DDS_GPIOR_ADF_LE(), 1);
        
        self.sys_osc_dac(0x7fff)
        # set system pll oscillator control voltage on middle value
        time.sleep(0.01)

        self.owm=OneWireMaster.OneWireMaster(self.read_ow, self.write_ow, base=0)
        self.temp=ds18d20.ds18d20(self.owm)
         
        self.owm.configure_dividers(62.5e6)

    
    def sys_osc_dac(self, val):
        self.writel(DDS_REG_OSCDAC, val );
        self.writel(DDS_REG_OSCDAC, val | ( 1<<16) );
        self.writel(DDS_REG_OSCDAC, val );
        
    def fmc_rst_n(self, val):
        self.gpio_set( DDS_GPIOR_PLL_SYS_RESET_N(), val)
        
    def ledr(self, val):
        tmp = self.readl(DDS_REG_TEST_COMM )
        if( val == 1 ):
            tmp |= DDS_TEST_COMM_LED_R();
        else: 
            tmp &= ~ DDS_TEST_COMM_LED_R();
        self.writel(DDS_REG_TEST_COMM, tmp )
    
    def ledg(self, val):
        tmp = self.readl(DDS_REG_TEST_COMM )
        if( val == 1 ):
            tmp |= DDS_TEST_COMM_LED_G();
        else: 
            tmp &= ~ DDS_TEST_COMM_LED_G();
        self.writel(DDS_REG_TEST_COMM, tmp )
    
    
    def get_fmc_present(self):
#         fmcpresent is pulled low on FMC board
#         the same line is pulled high on spec
#         if line is low then fmc must be connected 
        self.fmc_present = False if ( self.readl(DDS_REG_GPIOR) & DDS_GPIOR_FMC_PRESENT() ) else True;
     
    
#     SPI communication        
    def spi_txrx(self, cs_pin, nbits, value ):
#         10 us
        DEL = 0.00001
        
        time.sleep(DEL)
        self.gpio_set(DDS_GPIOR_PLL_SCLK(), 0)
        time.sleep(DEL)
        self.gpio_set(cs_pin, 0)
        time.sleep(DEL)
        self.gpio_set(DDS_GPIOR_PLL_SCLK(), 0)
        
        rv = 0
        
        for i in range(nbits):
            value <<= 1;
            self.gpio_set(DDS_GPIOR_PLL_SDIO(), value & (1<<nbits));
            time.sleep(DEL)
            self.gpio_set(DDS_GPIOR_PLL_SCLK(), 1);
            time.sleep(DEL)
            rv <<= 1;
            rv |= 1 if self.gpio_get(DDS_GPIOR_PLL_SDIO()) else 0
            time.sleep(DEL)
            self.gpio_set(DDS_GPIOR_PLL_SCLK(), 0);
            time.sleep(DEL)
            
        self.gpio_set( cs_pin, 1);

        return rv


######################## LOWEST LEVEL FUNCTIONS ####################################
    
    def write_eeprom_from_file(self, filename,  offset):
        print filename
        return self.rflib.write_eeprom2_from_file(c_void_p(self.card), c_char_p(filename), c_uint8(offset) );
    
    def read_eeprom(self,addr):
        r = c_uint8();

        # int eeprom_read(void *dev, uint8_t i2c_addr, uint32_t offset, uint8_t *buf, size_t size)
        s = self.rflib.eeprom2_read(c_void_p(self.card),c_uint8(0x50), c_uint8(addr), byref(r), c_uint8(1))
        if s==1:
            return r.value
        else:
            return -1

    def write_eeprom(self,addr, dat):
        r = c_uint8();
        r.value = dat
        # int eeprom_write(void *dev, uint8_t i2c_addr, uint32_t offset, uint8_t *buf, size_t size)
        return self.rflib.eeprom2_write(c_void_p(self.card),c_uint8(0x50), c_uint8(addr), byref(r), c_uint8(1))

    def read_mpc9800_temperature(self, nr):

        ga0 = 0
        ga1 = 0
        mpcid=0x9
        regaddr = 0

        if nr!=0:
            nr=1

        # i2c address: 1_0_0_1_A2_A1_A0
        # A2 is connected to gnd on one chip and to vcc on the other
        # A1 is connected to ga0
        # A0 is connected to ga1
        i2c_addr = mpcid<<3 | nr<<2 | ga0<<1| ga1

        r = c_uint16();

        # int eeprom_read(void *dev, uint8_t i2c_addr, uint32_t offset, uint16_t *buf, size_t size)
        s = self.rflib.read_mpc9800(c_void_p(self.card),c_uint8(i2c_addr), c_uint8(regaddr), byref(r), c_uint8(1))
        if s==1:
            r=r.value
            intval = (r>>8)&0xff
            fracval = r&0xff
            r = intval + fracval/(2.0**8)
            return r
        else:
            return -1

    def gpio_set(self, pin, val ):
        # set value on single gpio pin
        curr = self.readl(DDS_REG_GPIOR)
        if( val ):
            self.writel(DDS_REG_GPIOR, pin|curr)
        else:
            self.writel(DDS_REG_GPIOR, curr&(~ pin) )
        
            
    def gpio_get(self, pin):
        # get current value of gpio pin
        return self.readl(DDS_REG_GPIOR) & pin;


    def writel(self, addr, data ):
#       basic write function from speclib
#       void spec_writel(void *card, uint32_t data, uint32_t addr)        
        self.speclib.spec_writel( c_void_p(self.card), c_uint32(data), c_uint32(self.BASE_ADDR + addr) );
    
    def readl(self, addr ):
#         basic read function from speclib
#         uint32_t spec_readl(void *card, uint32_t addr)
        return self.speclib.spec_readl( c_void_p(self.card), c_uint32(self.BASE_ADDR + addr) );
    
    def write_ow(self, addr, data ):
        # print "SPEC: writing addr: %d" % addr
        self.speclib.spec_writel( c_void_p(self.card), c_uint32(data), c_uint32(self.ONEWIRE_ADDR + addr) );
    
    def read_ow(self, addr):
        # print "SPEC: reading addr: %d" % addr
        return self.speclib.spec_readl( c_void_p(self.card), c_uint32(self.ONEWIRE_ADDR + addr) );
