#!   /usr/bin/env   python
#    coding: utf8

# Copyright CERN, 2012
# Author: Matthieu Cattin (CERN)
# Licence: GPL v2 or later.
# Website: http://www.ohwr.org
# Last modifications: 8/5/2012

# Import standard modules
import sys
import time
import random
import math
import random

# Import specific modules
#import rr
from csr import *
from onewire import *
from i2c import *
from spi import *

from ds18b20 import *
from eeprom_24aa64 import *
from ltc217x import *
from max5442 import *
from si57x import *

# Import register maps
from utc_core_regs import *
from fmc_adc_eic_regs import *
from fmcadc100m_csr import *





# Converts digital value to volts
def digital2volt(value, full_scale, nb_bit):
    return float(value) * float(full_scale)/2**nb_bit

# Converts two's complement hex to signed
def hex2signed(value):
    if(value & 0x8000):
        return -((~value & 0xFFFF) + 1)
    else:
        return value


# Class to access fmcadc100m14b4cha mezzanine specific Wishbone cores

class FmcAdc100mOperationError(Exception):
    def __init__(self, msg):
        self.msg = msg
    def __str__(self):
        return ("FmcAdc100m: %s" %(self.msg))

class CFmcAdc100m:

    FMC_EIC_ADDR = 0x0
    UTC_CORE_ADDR = 0x100

    # FOR SVEC ONLY
    DDR_DAT_ADDR = 0x1000
    DDR_ADR_ADDR = 0x200

    FMC_SYS_I2C_ADDR = 0x1000
    EEPROM_ADDR = 0x50

    FMC_SPI_ADDR = 0x1100
    FMC_SPI_DIV = 100
    FMC_SPI_SS = {'ADC': 0,'DAC1': 1,'DAC2': 2,'DAC3': 3,'DAC4': 4}

    FMC_I2C_ADDR = 0x1200
    SI570_ADDR = 0x55

    FMC_CSR_ADDR = 0x1300

    FMC_ONEWIRE_ADDR = 0x1400

    # UTC core
    # tag = [wb_addr_offset, meta, second, coarse, fine]
    tag_trig = [0x8, 0, 0, 0, 0]
    tag_start = [0x18, 0, 0, 0, 0]
    tag_stop = [0x28, 0, 0, 0, 0]
    tag_end = [0x38, 0, 0, 0, 0]

    # ADC core CSR
    FSM_CMD_START = 0x1
    FSM_CMD_STOP = 0x2
    FSM_STATES = ['N/A','IDLE','PRE_TRIG','WAIT_TRIG',
                  'POST_TRIG','DECR_SHOT','N/A','others']

    IN_TERM = (1<<3)
    IN_TERM_MASK = 0x08
    IN_RANGES = {'100mV': 0x23, '1V': 0x11, '10V': 0x45, 'CAL': 0x40, 'OPEN': 0x00, 'CAL_100mV': 0x42, 'CAL_1V': 0x40, 'CAL_10V': 0x44}

    # DAC correction data local storage
    dac_corr_data = {'10V':{'offset':[0x0,0x0,0x0,0x0],'gain':[0x8000,0x8000,0x8000,0x8000]},
                     '1V':{'offset':[0x0,0x0,0x0,0x0],'gain':[0x8000,0x8000,0x8000,0x8000]},
                     '100mV':{'offset':[0x0,0x0,0x0,0x0],'gain':[0x8000,0x8000,0x8000,0x8000]}}

    # Timeout to avoid infinte loops
    WAIT_TIME_OUT = 2



    def __init__(self, bus, offset=0x2000):
        self.bus = bus
        self.adc_mezz_offset = offset
        self.adc_core_offset = offset + 0x2000

        # FOR SVEC ONLY
        self.DDR_DAT_ADDR = self.adc_mezz_offset + self.DDR_DAT_ADDR
        self.DDR_ADR_ADDR = self.adc_mezz_offset + self.DDR_ADR_ADDR

        try:
            # Objects declaration
            self.fmc_eic = CCSR(self.bus, self.adc_mezz_offset + self.FMC_EIC_ADDR, FMC_ADC_EIC_REGS)
            self.utc_core = CCSR(self.bus, self.adc_mezz_offset + self.UTC_CORE_ADDR, UTC_CORE_REGS)

            self.fmc_sys_i2c = COpenCoresI2C(self.bus, self.adc_core_offset + self.FMC_SYS_I2C_ADDR, 249)
            self.fmc_i2c = COpenCoresI2C(self.bus, self.adc_core_offset + self.FMC_I2C_ADDR, 249)

            self.eeprom_24aa64 = C24AA64(self.fmc_sys_i2c, self.adc_core_offset + self.EEPROM_ADDR)
            self.si570 = CSi57x(self.fmc_i2c, self.adc_core_offset + self.SI570_ADDR)

            self.fmc_onewire = COpenCoresOneWire(self.bus, self.adc_core_offset + self.FMC_ONEWIRE_ADDR, 624, 124)
            self.ds18b20 = CDS18B20(self.fmc_onewire, 0)

            self.fmc_spi = COpenCoresSPI(self.bus, self.adc_core_offset + self.FMC_SPI_ADDR, self.FMC_SPI_DIV)
            self.adc_cfg = CLTC217x(self.fmc_spi, self.FMC_SPI_SS['ADC'])
            self.dac_ch = []
            self.dac_ch.append(CMAX5442(self.fmc_spi, self.FMC_SPI_SS['DAC1']))
            self.dac_ch.append(CMAX5442(self.fmc_spi, self.FMC_SPI_SS['DAC2']))
            self.dac_ch.append(CMAX5442(self.fmc_spi, self.FMC_SPI_SS['DAC3']))
            self.dac_ch.append(CMAX5442(self.fmc_spi, self.FMC_SPI_SS['DAC4']))

            self.fmc_adc_csr = CCSR(self.bus, self.adc_core_offset + self.FMC_CSR_ADDR, FMCADC100M_CSR)

            # Set channels gain to 1
            self.fmc_adc_csr.set_field('CH1_GAIN', 'VAL', 0x8000)
            self.fmc_adc_csr.set_field('CH2_GAIN', 'VAL', 0x8000)
            self.fmc_adc_csr.set_field('CH3_GAIN', 'VAL', 0x8000)
            self.fmc_adc_csr.set_field('CH4_GAIN', 'VAL', 0x8000)

            # Enable mezzanine clock and offset DACs
            self.en_sampfreq()
            self.dc_offset_reset()

            # Disable ADC test pattern
            self.adc_cfg.dis_testpat()

        except (I2CDeviceOperationError, CSRDeviceOperationError, LTC217xOperationError) as e:
            raise FmcAdc100mOperationError(e)


    def channel_check(self, channel):
        if(channel < 1 or channel > 4):
            raise FmcAdc100mOperationError("Channel number %d, not in range 1 to 4." % channel)
        else:
            return channel


    # Front panel LED manual control
    def acq_led(self, state):
        ACQ_LED_OFFSET = 7
        try:
            reg = self.fmc_adc_csr.get_reg('CTL')
            # print("CTL reg value:%.8X")%reg
            if(state == 0):
                reg &= ~(1<<ACQ_LED_OFFSET)
            else:
                reg |= (1<<ACQ_LED_OFFSET)
            # The set_field function can't be used here, because of write
            # only command fields that have undefined value when read.
            # Therefore, write only command fields are set to 0
            reg &= 0xEC
            # print("CTL reg value:%.8X")%reg
            self.fmc_adc_csr.set_reg('CTL', reg)
        except CSRDeviceOperationError as e:
            raise FmcAdc100mOperationError(e)

    def trig_led(self, state):
        TRIG_LED_OFFSET = 6
        try:
            reg = self.fmc_adc_csr.get_reg('CTL')
            # print("CTL reg value:%.8X")%reg
            if(state == 0):
                reg &= ~(1<<TRIG_LED_OFFSET)
            else:
                reg |= (1<<TRIG_LED_OFFSET)
            # The set_field function can't be used here, because of write
            # only command fields that have undefined value when read.
            # Therefore, write only command fields are set to 0
            reg &= 0xEC
            # print("CTL reg value:%.8X")%reg
            self.fmc_adc_csr.set_reg('CTL', reg)
        except CSRDeviceOperationError as e:
            raise FmcAdc100mOperationError(e)

    # print LTC2174 configuration
    def print_adc_config(self):
        print '\nLTC2174 configuration'
        print("Format and power down register : %.2X") % self.adc_cfg.get_fmt()
        print("Output mode register           : %.2X") % self.adc_cfg.get_outmode()
        print("Test pattern                   : %.4X") % self.adc_cfg.get_testpat()
        print("Test pattern status            : %.1X") % self.adc_cfg.get_testpat_stat()

    #======================================================================
    # FMC-ADC Enhanced Interrupt Controller (EIC)

    # Print FMC-ADC EIC register map
    def print_eic_regs(self):
        self.fmc_eic.print_reg_map()

    # Enable trigger interrupt
    def enable_trig_irq(self):
        try:
            self.fmc_eic.set_field('IER', 'TRIG', 1)
        except CSRDeviceOperationError as e:
            raise FmcAdc100mSpecOperationError(e)

    # Disable trigger interrupt
    def disable_trig_irq(self):
        try:
            self.fmc_eic.set_field('IDR', 'TRIG', 1)
        except CSRDeviceOperationError as e:
            raise FmcAdc100mSpecOperationError(e)

    # Clear trigger interrupt
    def clear_trig_irq(self):
        try:
            self.fmc_eic.set_field('ISR', 'TRIG', 1)
        except CSRDeviceOperationError as e:
            raise FmcAdc100mSpecOperationError(e)

    # Enable acquisition end interrupt
    def enable_acq_end_irq(self):
        try:
            self.fmc_eic.set_field('IER', 'ACQ_END', 1)
        except CSRDeviceOperationError as e:
            raise FmcAdc100mSpecOperationError(e)

    # Disable acquisition end interrupt
    def disable_acq_end_irq(self):
        try:
            self.fmc_eic.set_field('IDR', 'ACQ_END', 1)
        except CSRDeviceOperationError as e:
            raise FmcAdc100mSpecOperationError(e)

    # Clear acquisition end interrupt
    def clear_acq_end_irq(self):
        try:
            self.fmc_eic.set_field('ISR', 'ACQ_END', 1)
        except CSRDeviceOperationError as e:
            raise FmcAdc100mSpecOperationError(e)

    # Get FMC-ADC EIC enable mask
    def get_eic_en_mask(self):
        try:
            return self.fmc_eic.get_reg('IMR')
        except CSRDeviceOperationError as e:
            raise FmcAdc100mSpecOperationError(e)

    # Get FMC-ADC interrupt source
    def get_eic_src(self):
        try:
            return self.fmc_eic.get_reg('ISR')
        except CSRDeviceOperationError as e:
            raise FmcAdc100mSpecOperationError(e)

    # Get trigger interrupt status
    def get_trig_status(self):
        try:
            return self.fmc_eic.get_field('ISR', 'TRIG')
        except CSRDeviceOperationError as e:
            raise FmcAdc100mSpecOperationError(e)

    # Get end of acq interrupt status
    def get_acq_end_status(self):
        try:
            return self.fmc_eic.get_field('ISR', 'ACQ_END')
        except CSRDeviceOperationError as e:
            raise FmcAdc100mSpecOperationError(e)


    #======================================================================
    # UTC core

    # Print UTC core register map
    def print_utc_core_regs(self):
        self.utc_core.print_reg_map()

    # Set UTC core with current computer time
    def set_utc_time(self):
        try:
            current_time = time.time()
            utc_seconds = int(current_time)
            self.utc_core.set_reg('SECONDS', utc_seconds)
            utc_coarse = int((current_time - utc_seconds)/8E-9)
            self.utc_core.set_reg('COARSE', utc_coarse)
            return current_time
        except CSRDeviceOperationError as e:
            raise FmcAdc100mSvecOperationError(e)

    # Returns UTC seconds counter value
    def get_utc_second_cnt(self):
        try:
            return self.utc_core.get_reg('SECONDS')
        except CSRDeviceOperationError as e:
            raise FmcAdc100mSvecOperationError(e)

    # Set UTC seconds counter
    def set_utc_second_cnt(self, value):
        try:
            self.utc_core.set_reg('SECONDS', value)
            return self.utc_core.get_reg('SECONDS')
        except CSRDeviceOperationError as e:
            raise FmcAdc100mSvecOperationError(e)

    # Returns UTC coarse counter value
    def get_utc_coarse_cnt(self):
        try:
            return self.utc_core.get_reg('COARSE')
        except CSRDeviceOperationError as e:
            raise FmcAdc100mSvecOperationError(e)

    # Set UTC coarse counter
    def set_utc_coarse_cnt(self, value):
        try:
            self.utc_core.set_reg('COARSE', value)
            return self.utc_core.get_reg('COARSE')
        except CSRDeviceOperationError as e:
            raise FmcAdc100mSvecOperationError(e)


    ###########################################################################
    ##########                  Code to review                       ##########

    # Returns last trigger event time-tag
    def get_utc_trig_tag(self):
        # Get metadata
        addr = self.tag_trig[0]
        self.tag_trig[1] = self.utc_core.rd_reg(addr)
        # Get seconds
        addr = self.tag_trig[0]+0x4
        self.tag_trig[2] = self.utc_core.rd_reg(addr)
        # Get coarse
        addr = self.tag_trig[0]+0x8
        self.tag_trig[3] = self.utc_core.rd_reg(addr)
        # Get fine
        addr = self.tag_trig[0]+0xc
        self.tag_trig[4] = self.utc_core.rd_reg(addr)

        return self.tag_trig

    # Returns last acquisition start event time-tag
    def get_utc_start_tag(self):
        # Get metadata
        addr = self.tag_start[0]
        self.tag_start[1] = self.utc_core.rd_reg(addr)
        # Get seconds
        addr = self.tag_start[0]+0x4
        self.tag_start[2] = self.utc_core.rd_reg(addr)
        # Get coarse
        addr = self.tag_start[0]+0x8
        self.tag_start[3] = self.utc_core.rd_reg(addr)
        # Get fine
        addr = self.tag_start[0]+0xc
        self.tag_start[4] = self.utc_core.rd_reg(addr)

        return self.tag_start

    # Returns last acquisition stop event time-tag
    def get_utc_stop_tag(self):
        # Get metadata
        addr = self.tag_stop[0]
        self.tag_stop[1] = self.utc_core.rd_reg(addr)
        # Get seconds
        addr = self.tag_stop[0]+0x4
        self.tag_stop[2] = self.utc_core.rd_reg(addr)
        # Get coarse
        addr = self.tag_stop[0]+0x8
        self.tag_stop[3] = self.utc_core.rd_reg(addr)
        # Get fine
        addr = self.tag_stop[0]+0xc
        self.tag_stop[4] = self.utc_core.rd_reg(addr)

        return self.tag_stop

    # Returns last acquisition end event time-tag
    def get_utc_end_tag(self):
        # Get metadata
        addr = self.tag_end[0]
        self.tag_end[1] = self.utc_core.rd_reg(addr)
        # Get seconds
        addr = self.tag_end[0]+0x4
        self.tag_end[2] = self.utc_core.rd_reg(addr)
        # Get coarse
        addr = self.tag_end[0]+0x8
        self.tag_end[3] = self.utc_core.rd_reg(addr)
        # Get fine
        addr = self.tag_end[0]+0xc
        self.tag_end[4] = self.utc_core.rd_reg(addr)

        return self.tag_end

    ###########################################################################


    #======================================================================
    # Onewire thermometer and unique ID

    # print FMC unique ID
    def print_unique_id(self):
        try:
            print('FMC unique ID: %016X') % self.ds18b20.read_serial_number()
        except DS18B20OperationError as e:
            raise FmcAdc100mOperationError(e)

    # print FMC temperature
    def print_temp(self):
        try:
            serial_number = self.ds18b20.read_serial_number()
            print("FMC temperature: %3.3f°C") % self.ds18b20.read_temp(serial_number)
        except DS18B20OperationError as e:
            raise FmcAdc100mOperationError(e)

    # Returns FMC unique ID
    def get_unique_id(self):
        try:
            return self.ds18b20.read_serial_number()
        except DS18B20OperationError as e:
            raise FmcAdc100mOperationError(e)

    # Returns FMC temperature
    def get_temp(self):
        try:
            serial_number = self.ds18b20.read_serial_number()
            return self.ds18b20.read_temp(serial_number)
        except DS18B20OperationError as e:
            raise FmcAdc100mOperationError(e)

    #======================================================================
    # I2C buses

    # scan FMC i2c bus
    def i2c_scan(self):
        print '\nScanning FMC I2C bus'
        return self.fmc_i2c.scan()

    # scan FMC system i2c bus
    def sys_i2c_scan(self):
        print '\nScanning FMC system I2C bus'
        return self.fmc_sys_i2c.scan()

    # write to EEPROM on system i2c bus
    def sys_i2c_eeprom_write(self, data):
        try:
            byte_to_write = len(data)
            addr = 0
            while byte_to_write:
                if byte_to_write > 32:
                    size = 32
                else:
                    size = byte_to_write
                self.eeprom_24aa64.wr_page(addr,data[addr:(addr+size)])
                byte_to_write -= size
                addr += size
                time.sleep(0.005) # have to wait between writes otherwise the eeprom gives NACK on the i2c bus
        except Eeprom24AA64OperationError as e:
            raise FmcAdc100mOperationError(e)

    # read from EEPROM on system i2c bus
    def sys_i2c_eeprom_read(self, addr, size):
        try:
            return self.eeprom_24aa64.rd_seq(addr, size)
        except Eeprom24AA64OperationError as e:
            raise FmcAdc100mOperationError(e)

    #======================================================================
    # CSR and LTC217x

    # Set input range
    def set_input_range(self, channel, in_range):
        reg_name = 'CH'+str(self.channel_check(channel))+"_CTL"
        try:
            # read channel control register
            reg_val = self.fmc_adc_csr.get_reg(reg_name)
            # Save input termination bit state
            reg_val = self.IN_TERM_MASK & reg_val
            #print "[FmcAdc100m] ssr reg ch%1d: %.8X" % (channel, reg_val)
            #print "[FmcAdc100m] Channel %d Input range: %s" % (channel,in_range)
            if(in_range in self.IN_RANGES):
                reg_val |= self.IN_RANGES[in_range]
            else:
                raise FmcAdc100mOperationError("Requested input range (%s) doesn\'t exist." % in_range)
            #print "[FmcAdc100m] ssr reg ch%1d: %.8X" % (channel, reg_val)
            self.fmc_adc_csr.set_reg(reg_name, reg_val)
            #print "[FmcAdc100m] ssr reg ch%1d: %.8X"  % (channel, self.fmc_adc_csr.get_reg(reg_name))
        except CSRDeviceOperationError as e:
            raise FmcAdc100mOperationError(e)

    # Set SSR register
    def set_ssr(self, channel, value):
        reg_name = 'CH'+str(self.channel_check(channel))+"_CTL"
        try:
            self.fmc_adc_csr.set_reg(reg_name, value)
        except CSRDeviceOperationError as e:
            raise FmcAdc100mOperationError(e)

    # Get SSR register
    def get_ssr(self, channel):
        reg_name = 'CH'+str(self.channel_check(channel))+"_CTL"
        try:
            return self.fmc_adc_csr.get_reg(reg_name)
        except CSRDeviceOperationError as e:
            raise FmcAdc100mOperationError(e)

    # Set channel DC offset (MAX5442 DAC)
    def set_dc_offset(self, channel, value):
        ch = self.channel_check(channel)
        try:
            self.dac_ch[ch-1].set_value(value)
        except MAX5442OperationError as e:
            raise FmcAdc100mOperationError(e)

    # Save a copy of the correction data locally
    def set_dac_corr(self, corr_data):
        self.dac_corr_data = corr_data

    # Apply correction factors to DAC data
    def dac_apply_corr(self, value, gain_corr, offset_corr):
        #print "[FmcAdc100m] dac_apply_corr: value:%d offset:%d gain:%d"%(value, hex2signed(offset_corr), gain_corr)
        value_corr = (((((value - 0x8000) + hex2signed(offset_corr)) << 15) * gain_corr) >> 30) + 0x8000
        return value_corr

    # Set DC offset with gain and offset correction
    # value = DAC unsigned integer value
    def set_dc_offset_corrected(self, channel, in_range, value, print_value=False):
        uncorr_value = value
        value = self.dac_apply_corr(value,
                                    self.dac_corr_data[in_range]['gain'][channel-1],
                                    self.dac_corr_data[in_range]['offset'][channel-1])
        if(print_value):
            print('[FmcAdc100m] CH%d DAC uncorrected value: 0x%.4X')%(channel, uncorr_value)
            print('[FmcAdc100m] CH%d DAC corrected value  : 0x%.4X')%(channel, value)
        ch = self.channel_check(channel)
        try:
            self.dac_ch[ch-1].set_value(value)
        except MAX5442OperationError as e:
            raise FmcAdc100mOperationError(e)

    # Reset DC offset DACs
    def dc_offset_reset(self):
        try:
            self.fmc_adc_csr.set_field('CTL', 'OFFSET_DAC_CLR_N', 0)
            self.fmc_adc_csr.set_field('CTL', 'OFFSET_DAC_CLR_N', 1)
        except CSRDeviceOperationError as e:
            raise FmcAdc100mOperationError(e)

    # Set 50ohms termination
    # channel: integer (1 to 4)
    # state: string ('ON' or 'OFF')
    def set_input_term(self, channel, state):
        reg_name = 'CH'+str(self.channel_check(channel))+"_CTL"
        try:
            # Read channel control register
            reg_val = self.fmc_adc_csr.get_reg(reg_name)
            #print "[FmcAdc100m] ssr reg ch%1d: %.8X" % (channel, reg_val)
            if('ON' == state):
                reg_val |= self.IN_TERM
            elif('OFF' == state):
                reg_val &= ~(self.IN_TERM)
            else:
                raise FmcAdc100mOperationError("Unsupported state parameter (%s), should be ON or OFF." % state)
            #print "[FmcAdc100m] ssr reg ch%1d: %.8X"  % (channel, reg_vale)
            self.fmc_adc_csr.set_reg(reg_name, reg_val)
            #print "[FmcAdc100m] ssr reg ch%1d: %.8X" % (channel, self.fmc_adc_csr.get_reg(reg_name))
        except CSRDeviceOperationError as e:
            raise FmcAdc100mOperationError(e)

    # Set decimation factor
    def set_decimation(self, factor):
        try:
            self.fmc_adc_csr.set_field('SR', 'DECI', factor)
        except CSRDeviceOperationError as e:
            raise FmcAdc100mOperationError(e)

    # Get decimation factor
    def get_decimation(self):
        try:
            return self.fmc_adc_csr.get_field('SR', 'DECI')
        except CSRDeviceOperationError as e:
            raise FmcAdc100mOperationError(e)

    # Enable Sampling clock (Si570)
    def en_sampfreq(self):
        try:
            self.fmc_adc_csr.set_field('CTL', 'FMC_CLK_OE', 1)
        except CSRDeviceOperationError as e:
            raise FmcAdc100mOperationError(e)

    # Disable Sampling clock (Si570)
    def dis_sampfreq(self):
        try:
            self.fmc_adc_csr.set_field('CTL', 'FMC_CLK_OE', 0)
        except CSRDeviceOperationError as e:
            raise FmcAdc100mOperationError(e)

    # Serdes 'manual' calibration
    def serdes_sync(self):
        try:
            print "\n[FmcAdc100m] Start serdes synchro"
            pattern = 0x3456
            self.adc_cfg.set_testpat(pattern)
            self.adc_cfg.en_testpat()
            print "[FmcAdc100m] Test pattern                   : %.4X" % self.adc_cfg.get_testpat()
            print "[FmcAdc100m] Test pattern status            : %.1X" % self.adc_cfg.get_testpat_stat()
            while(pattern != self.fmc_adc_csr.get_field('CH1_STA', 'VAL')):
                for ch in range(1,5,1):
                    print "[FmcAdc100m] CH%d value : %.4X" % (ch, self.fmc_adc_csr.get_reg('CH'+str(ch)+'_STA', 'VAL'))
                self.fmc_adc_csr.set_field('CTL', 'MAN_BITSLIP', 1)
                time.sleep(.1)
            self.adc_cfg.dis_testpat()
            print("[FmcAdc100m] Serdes synced!")
        except (LTC217xOperationError, CSRDeviceOperationError) as e:
            raise FmcAdc100mOperationError(e)


    # Set test pattern
    def set_testpat(self, pattern):
        try:
            self.adc_cfg.set_testpat(pattern)
        except LTC217xOperationError as e:
            raise FmcAdc100mOperationError(e)

    # Get test pattern
    def get_testpat(self):
        try:
            return self.adc_cfg.get_testpat()
        except LTC217xOperationError as e:
            raise FmcAdc100mOperationError(e)

    # Enable test pattern
    def testpat_en(self, pattern):
        try:
            self.adc_cfg.set_testpat(pattern)
            self.adc_cfg.en_testpat()
        except LTC217xOperationError as e:
            raise FmcAdc100mOperationError(e)

    # Disable test pattern
    def testpat_dis(self):
        try:
            self.adc_cfg.dis_testpat()
        except LTC217xOperationError as e:
            raise FmcAdc100mOperationError(e)

    # Print adc config regs
    def print_adc_regs(self):
        try:
            self.adc_cfg.print_regs()
        except LTC217xOperationError as e:
            raise FmcAdc100mOperationError(e)

    # Set sampling frequency (Si570)
    #def set_sampfreq(self, freq):

    # Get sampling frequency

    # Get channel configuration
    #def get_channel_config(self, channel):

    # Set trigger configuration
    def set_trig_config(self, hw_sel, hw_pol, hw_en, sw_en, channel, int_thres, delay):
        ch = self.channel_check(channel)
        try:
            # Hardware trigger select (ext/int)
            self.fmc_adc_csr.set_field('TRIG_CFG', 'HW_TRIG_SEL', hw_sel)
            # Hardware trigger polarity
            self.fmc_adc_csr.set_field('TRIG_CFG', 'HW_TRIG_POL', hw_pol)
            # Hardware trigger enable
            self.fmc_adc_csr.set_field('TRIG_CFG', 'HW_TRIG_EN', hw_en)
            # Software trigger enable
            self.fmc_adc_csr.set_field('TRIG_CFG', 'SW_TRIG_EN', sw_en)
            # Internal trigger channel select (1 to 4)
            self.fmc_adc_csr.set_field('TRIG_CFG', 'INT_TRIG_SEL', ch-1)
            # Internal trigger threshold
            self.fmc_adc_csr.set_field('TRIG_CFG', 'INT_TRIG_THRES', int_thres)
            # Trigger delay (in sampling clock ticks)
            self.fmc_adc_csr.set_reg('TRIG_DLY', delay)
        except CSRDeviceOperationError as e:
            raise FmcAdc100mOperationError(e)

    # Get trigger configuration
    def print_trig_config(self):
        #print("trig config: 0x%08X"%self.fmc_adc_csr.get_reg('TRIG_CFG'))
        #print("trig delay: 0x%08X"%self.fmc_adc_csr.get_reg('TRIG_DLY'))
        DIS_EN = ['disable','enable']
        HW_POL = ['rising','falling']
        HW_SEL = ['internal', 'external']
        hw_sel = self.fmc_adc_csr.get_field('TRIG_CFG', 'HW_TRIG_SEL')
        hw_pol = self.fmc_adc_csr.get_field('TRIG_CFG', 'HW_TRIG_POL')
        hw_en = self.fmc_adc_csr.get_field('TRIG_CFG', 'HW_TRIG_EN')
        sw_en = self.fmc_adc_csr.get_field('TRIG_CFG', 'SW_TRIG_EN')
        channel = self.fmc_adc_csr.get_field('TRIG_CFG', 'INT_TRIG_SEL') + 1
        int_thres = self.fmc_adc_csr.get_field('TRIG_CFG', 'INT_TRIG_THRES')
        delay = self.fmc_adc_csr.get_reg('TRIG_DLY')
        print("[FmcAdc100m] Trigger config => hw_en:%s, sw_en: %s, hw_sel: %s, hw_pol: %s, int_channel: %d, int_thres: 0x%04X, delay: %d"%
              (DIS_EN[hw_en], DIS_EN[sw_en], HW_SEL[hw_sel], HW_POL[hw_pol], channel, int_thres, delay))

    # Internal trigger
    def set_int_trig(self, channel, polarity, threshold):
        ch = self.channel_check(channel)
        try:
            # Software trigger disable
            self.fmc_adc_csr.set_field('TRIG_CFG', 'SW_TRIG_EN', 0)
            # Select internal hardware trigger
            self.fmc_adc_csr.set_field('TRIG_CFG', 'HW_TRIG_SEL', 0)
            # Trigger polarity
            self.fmc_adc_csr.set_field('TRIG_CFG', 'HW_TRIG_POL', polarity)
            # Internal trigger channel select (1 to 4)
            self.fmc_adc_csr.set_field('TRIG_CFG', 'INT_TRIG_SEL', ch-1)
            # Internal trigger threshold
            self.fmc_adc_csr.set_field('TRIG_CFG', 'INT_TRIG_THRES', threshold)
            # Hardware trigger enable
            self.fmc_adc_csr.set_field('TRIG_CFG', 'HW_TRIG_EN', 1)
        except CSRDeviceOperationError as e:
            raise FmcAdc100mOperationError(e)

    # External trigger
    def set_ext_trig(self, polarity):
        try:
            # Software trigger disable
            self.fmc_adc_csr.set_field('TRIG_CFG', 'SW_TRIG_EN', 0)
            # Select external hardware trigger
            self.fmc_adc_csr.set_field('TRIG_CFG', 'HW_TRIG_SEL', 1)
            # Trigger polarity
            self.fmc_adc_csr.set_field('TRIG_CFG', 'HW_TRIG_POL', polarity)
            # Hardware trigger enable
            self.fmc_adc_csr.set_field('TRIG_CFG', 'HW_TRIG_EN', 1)
        except CSRDeviceOperationError as e:
            raise FmcAdc100mOperationError(e)

    # Software trigger
    def set_soft_trig(self):
        try:
            # Hardware trigger disable
            self.fmc_adc_csr.set_field('TRIG_CFG', 'HW_TRIG_EN', 0)
            # Software trigger enable
            self.fmc_adc_csr.set_field('TRIG_CFG', 'SW_TRIG_EN', 1)
        except CSRDeviceOperationError as e:
            raise FmcAdc100mOperationError(e)

    # Trigger delay
    def set_trig_delay(self, delay):
        try:
            # Trigger delay (in sampling clock ticks)
            self.fmc_adc_csr.set_reg('TRIG_DLY', delay)
        except CSRDeviceOperationError as e:
            raise FmcAdc100mOperationError(e)

    # Enable test data
    def test_data_en(self):
        try:
            self.fmc_adc_csr.set_field('CTL', 'TEST_DATA_EN', 1)
        except CSRDeviceOperationError as e:
            raise FmcAdc100mOperationError(e)

    # Disable test data
    def test_data_dis(self):
        try:
            self.fmc_adc_csr.set_field('CTL', 'TEST_DATA_EN', 0)
        except CSRDeviceOperationError as e:
            raise FmcAdc100mOperationError(e)

    # Start acquisition
    def start_acq(self):
        try:
            # Wait for serdes to be synced
            init_time=time.time()
            while(0 == self.get_serdes_sync_stat()):
                if (time.time()-init_time) > self.WAIT_TIME_OUT:
                    raise FmcAdc100mOperationError("Wait serdes sync timeout.")
                print '[FmcAdc100m] Wait for serdes to be synced'
                time.sleep(.1)
            self.fmc_adc_csr.set_field('CTL', 'FSM_CMD', self.FSM_CMD_START)
        except CSRDeviceOperationError as e:
            raise FmcAdc100mOperationError(e)

    # Stop acquisition
    def stop_acq(self):
        try:
            self.fmc_adc_csr.set_field('CTL', 'FSM_CMD', self.FSM_CMD_STOP)
        except CSRDeviceOperationError as e:
            raise FmcAdc100mOperationError(e)

    # Software trigger
    def sw_trig(self):
        try:
            init_time=time.time()
            while('WAIT_TRIG' != self.get_acq_fsm_state()):
                if (time.time()-init_time) > self.WAIT_TIME_OUT:
                    raise FmcAdc100mOperationError("Wait trigger timeout.")
                #print self.get_acq_fsm_state()
                #time.sleep(.001)
            self.fmc_adc_csr.set_reg('SW_TRIG', 0xFFFFFFFF)
        except CSRDeviceOperationError as e:
            raise FmcAdc100mOperationError(e)

    # Software trigger without wait on WAIT_TRIG state
    def sw_trig_no_wait(self):
        try:
            self.fmc_adc_csr.set_reg('SW_TRIG', 0xFFFFFFFF)
        except CSRDeviceOperationError as e:
            raise FmcAdc100mOperationError(e)

    # Set pre-trigger samples
    def set_pre_trig_samples(self, samples):
        try:
            self.fmc_adc_csr.set_reg('PRE_SAMPLES', samples)
        except CSRDeviceOperationError as e:
            raise FmcAdc100mOperationError(e)

    # Set post-trigger samples
    def set_post_trig_samples(self, samples):
        try:
            self.fmc_adc_csr.set_reg('POST_SAMPLES', samples)
        except CSRDeviceOperationError as e:
            raise FmcAdc100mOperationError(e)

    # Set number of shots
    def set_shots(self, shots):
        try:
            self.fmc_adc_csr.set_field('SHOTS', 'NB', shots)
        except CSRDeviceOperationError as e:
            raise FmcAdc100mOperationError(e)

    # Get acquisition state machine status
    def get_acq_fsm_state(self):
        try:
            state = self.fmc_adc_csr.get_field('STA', 'FSM')
            #print "[FmcAdc100m] FSM state: %d" % state
            return self.FSM_STATES[state]
        except CSRDeviceOperationError as e:
            raise FmcAdc100mOperationError(e)

    # Get trigger position (DDR address)
    def get_trig_pos(self):
        try:
            return self.fmc_adc_csr.get_reg('TRIG_POS')
        except CSRDeviceOperationError as e:
            raise FmcAdc100mOperationError(e)

    # Get number of acquired samples
    def get_nb_samples(self):
        try:
            return self.fmc_adc_csr.get_reg('SAMPLES_CNT')
        except CSRDeviceOperationError as e:
            raise FmcAdc100mOperationError(e)

    # Get serdes sync status
    def get_serdes_sync_stat(self):
        try:
            return self.fmc_adc_csr.get_field('STA', 'SERDES_SYNCED')
        except CSRDeviceOperationError as e:
            raise FmcAdc100mOperationError(e)

    # Get ADC core status
    def get_status(self):
        try:
            return self.fmc_adc_csr.get_reg('STA')
        except CSRDeviceOperationError as e:
            raise FmcAdc100mOperationError(e)

    # Get Channel current ADC value
    def get_current_adc_value(self, channel):
        reg_name = 'CH'+str(self.channel_check(channel))+"_STA"
        try:
            return self.fmc_adc_csr.get_field(reg_name, 'VAL')
        except CSRDeviceOperationError as e:
            raise FmcAdc100mOperationError(e)

    # Set channel gain and offset correction
    def set_adc_gain_offset_corr(self, channel, gain, offset):
        try:
            reg_name = 'CH'+str(self.channel_check(channel))+"_GAIN"
            self.fmc_adc_csr.set_field(reg_name, 'VAL', gain)
            reg_name = 'CH'+str(self.channel_check(channel))+"_OFFSET"
            self.fmc_adc_csr.set_field(reg_name, 'VAL', offset)
        except CSRDeviceOperationError as e:
            raise FmcAdc100mOperationError(e)

    # Get channel gain correction
    def get_adc_gain_corr(self, channel):
        try:
            reg_name = 'CH'+str(self.channel_check(channel))+"_GAIN"
            return self.fmc_adc_csr.get_field(reg_name, 'VAL')
        except CSRDeviceOperationError as e:
            raise FmcAdc100mOperationError(e)

    # Get channel offset correction
    def get_adc_offset_corr(self, channel):
        try:
            reg_name = 'CH'+str(self.channel_check(channel))+"_OFFSET"
            return self.fmc_adc_csr.get_field(reg_name, 'VAL')
        except CSRDeviceOperationError as e:
            raise FmcAdc100mOperationError(e)

    # Print ADC core config/status registers
    def print_adc_core_config(self):
        try:
            self.fmc_adc_csr.print_reg_map()
        except CSRDeviceOperationError as e:
            raise FmcAdc100mOperationError(e)


    #======================================================================
    # Si570 programmable oscillator

    # Print Si570 config
    def print_si570_config(self):
        try:
            print("\nPrint Si570 configuration")
            print("RFREQ  : %3.28f") % self.si570.get_rfreq()
            print("N1     : %d") % self.si570.get_n1_div()
            print("HS_DIV : %d") % self.si570.get_hs_div()
        except Si57xOperationError as e:
            raise FmcAdc100mOperationError(e)

    # Get Si570 config
    def get_si570_config(self):
        try:
            return self.si570.get_rfreq(), self.si570.get_n1_div(), self.si570.get_hs_div()
        except Si57xOperationError as e:
            raise FmcAdc100mOperationError(e)

    # Get Si570 raw config (hex value of the I2C registers in a table)
    def get_si570_raw_config(self):
        try:
            return self.si570.get_raw_config()
        except Si57xOperationError as e:
            raise FmcAdc100mOperationError(e)


    #======================================================================
    # DDR memory interface, FOR SVEC ONLY

    # Read data from DDR
    #   carrier_addr and length are in 32-bit word
    def get_data(self, carrier_addr, length, raw=False):
        ret = []
        data = []
        cc = self.bus.iwrite(0, self.DDR_ADR_ADDR, 4, carrier_addr)
        #print('[get_data] write: addr=0x%.8X cc=%d'%(self.DDR_ADR_ADDR, cc))
        for i in range(length):
            #adr_cnt_b = self.bus.iread(0, self.DDR_ADR_ADDR, 4)
            data.append(self.bus.iread(0, self.DDR_DAT_ADDR, 4))
            #print('[get_data] read: addr=0x%.9X i=%d'%(self.DDR_DAT_ADDR, i))
            #adr_cnt = self.bus.iread(0, self.DDR_ADR_ADDR, 4)
            #print('[get_data] address counter: before=0x%.8X after=0x%.8X'%(adr_cnt_b, adr_cnt))
        for i in range(length):
            ret.append(data[i] & 0xFFFF)
            ret.append(data[i]>>16)
        if raw:
            return data # 32-bit data array
        else:
            return ret # 16-bit data array

    # Write data to DDR
    #   carrier_addr is in 32-bit word
    #   data must be a array of 32-bit words
    def put_data(self, carrier_addr, data):
        self.bus.iwrite(0, self.DDR_ADR_ADDR, 4, carrier_addr)
        for i in range(len(data)):
            #adr_cnt_b = self.bus.iread(0, self.DDR_ADR_ADDR, 4)
            self.bus.iwrite(0, self.DDR_DAT_ADDR, 4, data[i])
            #print('[put_data] write: addr=0x%.9X i=%d data=0x%.8X'%(self.DDR_DAT_ADDR, i, data[i]))
            #adr_cnt = self.bus.iread(0, self.DDR_ADR_ADDR, 4)
            #print('[put_data] address counter: before=0x%.8X after=0x%.8X'%(adr_cnt_b, adr_cnt))

    # Clear DDR
    def clear_ddr(self):
        self.bus.iwrite(0, self.DDR_ADR_ADDR, 4, 0x0)
        for i in range(0x4000000):
            self.bus.iwrite(0, self.DDR_DAT_ADDR, 4, 0x0)


    #======================================================================
    # Acquisition
    def open_all_channels(self):
        for i in range(1,5):
            self.set_input_range(i, 'OPEN')
            time.sleep(0.05)

    def acq_init(self, pre_trig=100, post_trig=1000, nb_shots=1):
        # Reset offset DACs
        self.dc_offset_reset()
        # Make sure all switches are OFF
        self.open_all_channels()
        # Set software trigger
        self.set_soft_trig()
        # Set acquisition
        self.set_pre_trig_samples(pre_trig)
        self.set_post_trig_samples(post_trig)
        self.set_shots(nb_shots)

    def acq_channels(self, carrier_type, carrier, adc_fs, acq_length=1000, pause=1, timeout=10):
        # Make sure no acquisition is running
        self.stop_acq()
        time.sleep(0.05)
        # Start acquisition
        self.start_acq()
        time.sleep(pause)
        # Trigger
        self.sw_trig()
        # Wait end of acquisition
        timeout_cnt = 0
        while('IDLE' != self.get_acq_fsm_state()):
            time.sleep(.1)
            timeout_cnt += 1
            if(timeout < timeout_cnt):
                raise FmcAdc100mOperationError('Acquisition timeout. Missing trigger?\nAcq FSM state: %s'%self.get_acq_fsm_state())
        # Retrieve data from DDR memory
        # Read ACQ_LENGTH samples after the trigger for all channels
        trig_pos = self.get_trig_pos()
        if carrier_type == 'spec':
            channels_data = self.get_data((trig_pos<<3), acq_length*8)
        elif carrier_type == 'svec':
            channels_data = self.get_data((trig_pos<<3), acq_length*8)
        else:
            raise FmcAdc100mOperationError('Unsupported carrier type: %s'%carrier_type)
        #print [hex(val) for val in channels_data[0::4][:10]]
        channels_data = [hex2signed(item) for item in channels_data]
        #print [hex(val) for val in channels_data[0::4][:10]]
        channels_data = [digital2volt(item,adc_fs,16) for item in channels_data]
        #print channels_data[0::4][:10]
        return channels_data
