class FEMB_CONFIG(FEMB_CONFIG_BASE):
    def __init__(self):
        super().__init__()
        #declare board specific registers
        self.FEMB_VER = "adctest"
        self.REG_RESET = 0
        self.REG_ASIC_RESET = 1
        self.REG_ASIC_SPIPROG = 2
        self.REG_SEL_ASIC = 7
        self.REG_SEL_CH = 7
        self.REG_FESPI_BASE = 592
        self.REG_ADCSPI_BASE = 512
        self.REG_FESPI_RDBACK_BASE = 632
        self.REG_ADCSPI_RDBACK_BASE = 552
        self.REG_HS = 17
        self.REG_LATCHLOC = 4
        self.REG_CLKPHASE = 6
        self.ADC_TESTPATTERN = [
            0x12, 0x345, 0x678, 0xf1f, 0xad, 0xc01, 0x234, 0x567, 0x89d, 0xeca,
            0xff0, 0x123, 0x456, 0x789, 0xabc, 0xdef
        ]
        self.NASICS = 1

        #initialize FEMB UDP object
        self.femb = FEMB_UDP()

    def resetBoard(self):
        #Reset system
        self.femb.write_reg(self.REG_RESET, 1)
        time.sleep(5.)

        #Reset registers
        self.femb.write_reg(self.REG_RESET, 2)
        time.sleep(1.)

        #Time stamp reset
        #femb.write_reg( 0, 4)
        #time.sleep(0.5)

        #Reset ADC ASICs
        self.femb.write_reg(self.REG_ASIC_RESET, 1)
        time.sleep(0.5)

    def initBoard(self):
        nRetries = 5
        for iRetry in range(nRetries):
            #set up default registers

            #Reset ADC ASICs
            self.femb.write_reg(self.REG_ASIC_RESET, 1)
            time.sleep(0.5)

            #Set ADC test pattern register
            self.femb.write_reg(3, 0x01230000)  # test pattern off
            #self.femb.write_reg( 3, 0x81230000) # test pattern on

            #Set ADC latch_loc
            self.femb.write_reg(self.REG_LATCHLOC, 0x66666667)
            #Set ADC clock phase
            self.femb.write_reg(self.REG_CLKPHASE, 0xfffc0054)

            #internal test pulser control
            self.femb.write_reg(5, 0x00000000)
            self.femb.write_reg(13, 0x0)  #enable

            #Set test and readout mode register
            self.femb.write_reg(
                7, 0x0000)  #11-8 = channel select, 3-0 = ASIC select

            #Set number events per header
            self.femb.write_reg(8, 0x0)

            #ADC ASIC SPI registers
            print("Config ADC ASIC SPI")
            print("ADCADC")
            self.femb.write_reg(self.REG_ADCSPI_BASE + 0, 0xC0C0C0C)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 1, 0xC0C0C0C)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 2, 0xC0C0C0C)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 3, 0xC0C0C0C)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 4, 0xC0C0C0C)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 5, 0xC0C0C0C)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 6, 0xC0C0C0C)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 7, 0xC0C0C0C)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 8, 0x18321832)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 9, 0x18181818)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 10, 0x18181818)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 11, 0x18181818)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 12, 0x18181818)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 13, 0x18181818)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 14, 0x18181818)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 15, 0x18181818)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 16, 0x64186418)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 17, 0x30303030)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 18, 0x30303030)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 19, 0x30303030)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 20, 0x30303030)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 21, 0x30303030)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 22, 0x30303030)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 23, 0x30303030)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 24, 0x30303030)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 25, 0x60c868c8)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 26, 0x60606868)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 27, 0x60606868)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 28, 0x60606868)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 29, 0x60606868)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 30, 0x60606868)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 31, 0x60606868)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 32, 0x60606868)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 33, 0x9060A868)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 34, 0x10001)

            #ADC ASIC sync
            self.femb.write_reg(17,
                                0x1)  # controls HS link, 0 for on, 1 for off
            self.femb.write_reg(17,
                                0x0)  # controls HS link, 0 for on, 1 for off

            #Write ADC ASIC SPI
            print("Program ADC ASIC SPI")
            self.femb.write_reg(self.REG_ASIC_SPIPROG, 1)
            time.sleep(0.1)
            self.femb.write_reg(self.REG_ASIC_SPIPROG, 1)
            time.sleep(0.1)

            print("Check ADC ASIC SPI")
            for regNum in range(self.REG_ADCSPI_RDBACK_BASE,
                                self.REG_ADCSPI_RDBACK_BASE + 34, 1):
                val = self.femb.read_reg(regNum)
                print(hex(val))

            #enable streaming
            #self.femb.write_reg( 9, 0x8)

            #LBNE_ADC_MODE
            self.femb.write_reg(16, 0x1)

            # Check that board streams data
            data = self.femb.get_data(1)
            if data == None:
                print("Board not streaming data, retrying initialization...")
                continue  # try initializing again
            print("FEMB_CONFIG--> Reset FEMB is DONE")
            return
        print(
            "Error: Board not streaming data after trying to initialize {} times. Exiting."
            .format(nRetries))
        sys.exit(1)

    def selectChannel(self, asic, chan, hsmode=None):
        asicVal = int(asic)
        if (asicVal < 0) or (asicVal >= self.NASICS):
            print(
                "femb_config_femb : selectChan - invalid ASIC number, only 0 to {} allowed"
                .format(self.NASICS - 1))
            return
        chVal = int(chan)
        if (chVal < 0) or (chVal > 15):
            print(
                "femb_config_femb : selectChan - invalid channel number, only 0 to 15 allowed"
            )
            return

        #print( "Selecting ASIC " + str(asicVal) + ", channel " + str(chVal))

        regVal = (chVal << 8) + asicVal
        self.femb.write_reg(self.REG_SEL_CH, regVal)

    def syncADC(self):
        #turn on ADC test mode
        print("Start sync ADC")
        reg3 = self.femb.read_reg(3)
        newReg3 = (reg3 | 0x80000000)
        self.femb.write_reg(3, newReg3)  #31 - enable ADC test pattern
        alreadySynced = True
        for a in range(0, self.NASICS, 1):
            print("Test ADC " + str(a))
            unsync = self.testUnsync(a)
            if unsync != 0:
                alreadySynced = False
                print("ADC not synced, try to fix")
                self.fixUnsync(a)
        LATCH = self.femb.read_reg(self.REG_LATCHLOC)
        PHASE = self.femb.read_reg(self.REG_CLKPHASE)
        print("Latch latency " + str(hex(LATCH)) + "\tPhase " +
              str(hex(PHASE)))
        print("End sync ADC")
        return not alreadySynced, LATCH, None, PHASE

    def testUnsync(self, adc):
        adcNum = int(adc)
        if (adcNum < 0) or (adcNum >= self.NASICS):
            print("femb_config_femb : testLink - invalid asic number")
            return

        #loop through channels, check test pattern against data
        badSync = 0
        for ch in range(0, 16, 1):
            self.selectChannel(adcNum, ch)
            time.sleep(0.1)
            for test in range(0, 1000, 1):
                data = self.femb.get_data(1)
                for samp in data:
                    chNum = ((samp >> 12) & 0xF)
                    sampVal = (samp & 0xFFF)
                    if sampVal != self.ADC_TESTPATTERN[ch]:
                        badSync = 1
                    if badSync == 1:
                        break
                if badSync == 1:
                    break
            if badSync == 1:
                break
        return badSync

    def fixUnsync(self, adc):
        adcNum = int(adc)
        if (adcNum < 0) or (adcNum >= self.NASICS):
            print("femb_config_femb : testLink - invalid asic number")
            return

        initLATCH = self.femb.read_reg(self.REG_LATCHLOC)
        initPHASE = self.femb.read_reg(self.REG_CLKPHASE)

        #loop through sync parameters
        for phase in range(0, 2, 1):
            clkMask = (0x1 << adcNum)
            testPhase = ((initPHASE & ~(clkMask)) | (phase << adcNum))
            self.femb.write_reg(self.REG_CLKPHASE, testPhase)
            for shift in range(0, 16, 1):
                shiftMask = (0xF << 4 * adcNum)
                testShift = ((initLATCH & ~(shiftMask)) |
                             (shift << 4 * adcNum))
                self.femb.write_reg(self.REG_LATCHLOC, testShift)
                #reset ADC ASIC
                self.femb.write_reg(self.REG_ASIC_RESET, 1)
                time.sleep(0.1)
                self.femb.write_reg(self.REG_ASIC_SPIPROG, 1)
                time.sleep(0.1)
                self.femb.write_reg(self.REG_ASIC_SPIPROG, 1)
                time.sleep(0.1)
                #test link
                unsync = self.testUnsync(adcNum)
                if unsync == 0:
                    print("ADC synchronized")
                    return
        #if program reaches here, sync has failed
        print("ADC SYNC process failed for ADC # " + str(adc))
Exemple #2
0
class FEMB_CONFIG(FEMB_CONFIG_BASE):
    def __init__(self, exitOnError=False):
        super().__init__(exitOnError=exitOnError)
        #declare board specific registers
        self.FEMB_VER = "adctestP1quad"

        self.REG_RESET = 0  # bit 0 system, 1 reg, 2 alg, 3 udp
        self.REG_PWR_CTRL = 1  # bit 0-3 pwr, 8-15 blue LEDs near buttons
        self.REG_ASIC_SPIPROG_RESET = 2  # bit 0 FE SPI, 1 ADC SPI, 4 FE ASIC RESET, 5 ADC ASIC RESET, 6 SOFT ADC RESET & SPI readback check
        self.REG_SEL_CH = 3  # bit 0-7 chip, 8-15 channel, 31 WIB mode

        self.REG_DAC1 = 4  # bit 0-15 DAC val, 16-19 tp mode select, 31 set dac
        self.REG_DAC2 = 5  # bit 0-15 tp period, 16-31 tp shift

        self.REG_FPGA_TST_PATT = 6  # bit 0-11 tst patt, 16 enable

        self.REG_ADC_CLK = 7  # bit 0-3 clk phase, 8 clk speed sel
        self.REG_LATCHLOC = 8  # bit 0-7 ADC1, 8-15 ADC2, 16-23 ADC3, 24-31 ADC4

        self.REG_STOP_ADC = 9  # bit 0 stops sending convert, read ADC HEADER redundant with reg 2

        self.REG_UDP_FRAME_SIZE = 63  # bits 0-11
        self.REG_FIRMWARE_VERSION = 0xFF  # 255 in decimal
        self.CONFIG_FIRMWARE_VERSION = 0x105  # this file is written for this

        self.REG_LATCHLOC_data_2MHz = 0x02010201
        self.REG_LATCHLOC_data_1MHz = 0x0

        self.REG_LATCHLOC_data_2MHz_cold = 0x02010201
        self.REG_LATCHLOC_data_1MHz_cold = 0x0

        self.REG_CLKPHASE_data_2MHz = 0x4
        self.REG_CLKPHASE_data_1MHz = 0x1
        self.REG_CLKPHASE_data_2MHz_cold = 0x0  #double check socket 1 value
        self.REG_CLKPHASE_data_1MHz_cold = 0x0

        self.REG_HDR_ERROR_RESET = 47
        self.REG_HDR_ERROR_BASES = [49, 51, 53, 55]  # for each chip

        self.DEFAULT_FPGA_TST_PATTERN = 0x12
        self.ADC_TESTPATTERN = [
            0x12, 0x345, 0x678, 0xf1f, 0xad, 0xc01, 0x234, 0x567, 0x89d, 0xeca,
            0xff0, 0x123, 0x456, 0x789, 0xabc, 0xdef
        ]

        # registers 64-88 are SPI to ASICs
        # 88 is last register besides 255 which is firmware version
        self.REG_FESPI_BASE = 84  # this configures all FE ASICs
        self.REG_ADCSPI_BASES = [64, 69, 74, 79]  # for each chip

        self.REG_EXTCLK_INV = 10
        self.REG_EXTCLK_BASES = [11, 20, 29, 38]  # for each chip
        self.FPGA_FREQ_MHZ = 200  # frequency of FPGA clock in MHz

        self.REG_PLL_BASES = [17, 26, 35, 44]  # for each chip

        self.NASICS = 4
        self.F2DEFAULT = 0
        self.CLKDEFAULT = "fifo"

        self.isExternalClock = True  #False = internal monostable, True = external
        self.is1MHzSAMPLERATE = False  #True = 1MHz, False = 2MHz
        self.COLD = False
        self.enableTest = 0
        self.doSpiWrite = True
        self.doReSync = True
        self.scanSyncSettings = True
        self.adcSyncStatus = False
        self.maxSyncAttempts = 10
        self.numSyncTests = 25

        #initialize FEMB UDP object
        self.femb = FEMB_UDP()

        #list of adc configuration register mappings
        self.adc_regs = ADC_ASIC_REG_MAPPING()

    def printParameters(self):
        print("External ADC Clocks    \t", self.isExternalClock)
        print("Cryogenic temperature  \t", self.COLD)
        print("Enable ADC test input  \t", self.enableTest)
        print("Enable SPI writing     \t", self.doSpiWrite)
        print("MAX SYNC ATTEMPTS      \t", self.maxSyncAttempts)
        print("Do resync              \t", self.doReSync)
        print("Try all sync settings  \t", self.scanSyncSettings)
        print("1MHz Sampling          \t", self.is1MHzSAMPLERATE)
        print("SYNC STATUS            \t", self.adcSyncStatus)

    def resetBoard(self):
        """
        Reset registers and state machines NOT udp
        Make sure to set reg 0 back to zero
            or there will be much sadness!
        """
        #Reset registers
        self.femb.write_reg(self.REG_RESET, 2)
        time.sleep(1.)

        #Reset state machines
        self.femb.write_reg(self.REG_RESET, 4)
        time.sleep(1.)

        #Reset reset register to 0
        self.femb.write_reg(self.REG_RESET, 0)
        time.sleep(0.2)

    def initBoard(self):
        # test readback
        readback = self.femb.read_reg(self.REG_FIRMWARE_VERSION)
        if readback is None:
            print("FEMB_CONFIG: Error reading register 0, Exiting.")
            return False
        if readback is False:
            if self.exitOnError:
                print("FEMB_CONFIG: Error reading register 0, Exiting.")
                sys.exit(1)
            else:
                raise ReadRegError("Couldn't read register 0")
                return False

        ##### Start Top-level Labview stacked sequence struct 0
        firmwareVersion = self.femb.read_reg(
            self.REG_FIRMWARE_VERSION) & 0xFFFF
        if firmwareVersion == None:
            print("FEMB_CONFIG: Error reading register 0, Exiting.")
            return False
        if firmwareVersion != self.CONFIG_FIRMWARE_VERSION:
            raise FEMBConfigError(
                "Board firmware version {} doesn't match configuration firmware version {}"
                .format(firmwareVersion, self.CONFIG_FIRMWARE_VERSION))
            return False
        print("Firmware Version: ", firmwareVersion)

        self.femb.write_reg(self.REG_UDP_FRAME_SIZE, 0x1FB)
        time.sleep(0.05)
        self.setFPGADac(0, 0, 0, 0)  # write regs 4 and 5
        #self.femb.write_reg(1,0) # pwr ctrl --disabled BK
        self.femb.write_reg(3, (5 << 8))  # chn sel
        self.femb.write_reg(6, self.DEFAULT_FPGA_TST_PATTERN)  #tst pattern
        self.femb.write_reg(7, 13)  #adc clk
        self.femb.write_reg(8, 0)  #latchloc
        ##### End Top-level Labview stacked sequence struct 0

        #Set FPGA test pattern register
        self.femb.write_reg(self.REG_FPGA_TST_PATT,
                            self.DEFAULT_FPGA_TST_PATTERN)  # test pattern off
        #self.femb.write_reg(self.REG_FPGA_TST_PATT, self.DEFAULT_FPGA_TST_PATTERN+(1 << 16)) # test pattern on

        #Set ADC latch_loc and clock phase and sample rate
        if self.is1MHzSAMPLERATE == True:
            if self.COLD:
                self.femb.write_reg(self.REG_LATCHLOC,
                                    self.REG_LATCHLOC_data_1MHz_cold)
                self.femb.write_reg(self.REG_ADC_CLK,
                                    (self.REG_CLKPHASE_data_1MHz_cold & 0xF) |
                                    (1 << 8))
            else:
                self.femb.write_reg(self.REG_LATCHLOC,
                                    self.REG_LATCHLOC_data_1MHz)
                self.femb.write_reg(self.REG_ADC_CLK,
                                    (self.REG_CLKPHASE_data_1MHz & 0xF) |
                                    (1 << 8))
        else:  # use 2 MHz values
            if self.COLD:
                self.femb.write_reg(self.REG_LATCHLOC,
                                    self.REG_LATCHLOC_data_2MHz_cold)
                self.femb.write_reg(self.REG_ADC_CLK,
                                    (self.REG_CLKPHASE_data_2MHz_cold & 0xF))
            else:
                self.femb.write_reg(self.REG_LATCHLOC,
                                    self.REG_LATCHLOC_data_2MHz)
                self.femb.write_reg(self.REG_ADC_CLK,
                                    (self.REG_CLKPHASE_data_2MHz & 0xF))

        #External timing config
        #self.writePLLs(0,0x20001,0)
        #writePLL(0,0x60017,0x5000B,0x801E0003)
        #writePLL(1,0x10000E,0x50009,0x801C0002)
        #writePLL(2,0x10000E,0x5000D,0x80180005)
        self.setExtClockRegs()

        #specify wib mode
        self.femb.write_reg_bits(self.REG_SEL_CH, 31, 1, 1)

        #turn ON ASICs when initializing board
        self.turnOnAsics()

        #turn OFF ASICs when initializing board
        #self.turnOffAsics()

        #test only, leave EXT TP mode ON
        #self.setFPGADac(0,1,0,0) # write regs 4 and 5
        return True

    def initAsic(self, asicNum=None):
        if asicNum == None:
            print(
                "FEMB_CONFIG: Invalid ASIC # defined, will not initialize ASIC."
            )
            return False
        asicNumVal = int(asicNum)
        if (asicNumVal < 0) or (asicNumVal >= self.NASICS):
            print(
                "FEMB_CONFIG: Invalid ASIC # defined, will not initialize ASIC."
            )
            return False

        #turn on ASIC
        #self.turnOnAsic(asicNumVal)

        #Reset ASICs
        self.femb.write_reg(self.REG_ASIC_SPIPROG_RESET, 0x0)  # zero out reg
        self.femb.write_reg(self.REG_ASIC_SPIPROG_RESET,
                            0x30)  # reset FE and ADC
        self.femb.write_reg(self.REG_ASIC_SPIPROG_RESET, 0x0)  # zero out reg
        time.sleep(1.)

        #specify ASIC for streaming data output
        self.selectAsic(asicNumVal)

        #Configure ADC ASIC registers (and external clock inside)
        if self.isExternalClock == True:
            self.configAdcAsic(asicNum=asicNumVal,
                               testInput=self.enableTest,
                               clockExternal=True)
        else:
            self.configAdcAsic(asicNum=asicNumVal,
                               testInput=self.enableTest,
                               clockMonostable=True)

        #acutally program the ADC using the default parameters
        self.doAdcAsicConfig(asicNumVal)

        #if sync fails, optionally attempt to try all other parameters to achieve sync
        if (self.adcSyncStatus == False) and (self.scanSyncSettings == True):
            self.fixUnsync(asicNumVal)

        #check SPI + SYNC status here
        syncStatus = self.getSyncStatus()
        if syncStatus == None:
            print("FEMB_CONFIG: ASIC initialization failed")
            return False
        adcSpi = syncStatus[1][asicNumVal]
        if adcSpi == False:
            print("FEMB_CONFIG: ADC ASIC SPI readback failed")
            return False

        #check sync here
        #self.printSyncRegister()
        print("SYNC STATUS:\t", self.adcSyncStatus)
        if self.adcSyncStatus == False:
            print("FEMB_CONFIG: ASIC NOT SYNCHRONIZED")
            return False
        return True

    def configAdcAsic(self,
                      asicNum=None,
                      enableOffsetCurrent=None,
                      offsetCurrent=None,
                      testInput=None,
                      freqInternal=None,
                      sleep=None,
                      pdsr=None,
                      pcsr=None,
                      clockMonostable=None,
                      clockExternal=None,
                      clockFromFIFO=None,
                      sLSB=None,
                      f0=None,
                      f1=None,
                      f2=None,
                      f3=None,
                      f4=None,
                      f5=None):
        """
        Configure ADCs
          enableOffsetCurrent: 0 disable offset current, 1 enable offset current
          offsetCurrent: 0-15, amount of current to draw from sample and hold
          testInput: 0 digitize normal input, 1 digitize test input
          freqInternal: internal clock frequency: 0 1MHz, 1 2MHz
          sleep: 0 disable sleep mode, 1 enable sleep mode
          pdsr: if pcsr=0: 0 PD is low, 1 PD is high
          pcsr: 0 power down controlled by pdsr, 1 power down controlled externally
          Only one of these can be enabled:
            clockMonostable: True ADC uses monostable clock
            clockExternal: True ADC uses external clock
            clockFromFIFO: True ADC uses digital generator FIFO clock
          sLSB: LSB current steering mode. 0 for full, 1 for partial (ADC7 P1)
          f0, f1, f2, f3, f4, f5: version specific
        """
        if asicNum == None:
            return None
        asicNumVal = int(asicNum)
        if (asicNumVal < 0) or (asicNumVal >= self.NASICS):
            return None

        #check requested clocks
        if clockMonostable and clockExternal:
            return None
        if clockMonostable and clockFromFIFO:
            return None
        if clockExternal and clockFromFIFO:
            return None

        if enableOffsetCurrent is None:
            enableOffsetCurrent = 0
        if offsetCurrent is None:
            offsetCurrent = 0
        else:
            offsetCurrent = int(
                "{:04b}".format(offsetCurrent)[::-1],
                2)  # need to reverse bits, use string/list tricks
        if testInput is None:
            testInput = 1
        if freqInternal is None:
            freqInternal = 1
        if sleep is None:
            sleep = 0
        if pdsr is None:
            pdsr = 0
        if pcsr is None:
            pcsr = 0
        if sLSB is None:
            sLSB = 0
        if f1 is None:
            f1 = 0
        if f2 is None:
            f2 = 0
        if f3 is None:
            f3 = 0
        if f4 is None:
            f4 = 1
        if f5 is None:
            f5 = 0
        if not (clockMonostable or clockExternal or clockFromFIFO):
            clockExternal = True
        # a bunch of things depend on the clock choice
        clk0 = 0
        clk1 = 0
        if clockExternal:
            clk0 = 1
            clk1 = 0
        elif clockFromFIFO:
            clk0 = 0
            clk1 = 1
        if f0 is None:
            if clockExternal:
                f0 = 1
            else:
                f0 = 0
        #moved external clock reg config to init function for now
        #if clockExternal:
        #    self.extClock(enable=True)
        #else:
        #    self.extClock(enable=False)

        #determine register values for requested config
        self.adc_regs.set_chip(en_gr=enableOffsetCurrent,
                               d=offsetCurrent,
                               tstin=testInput,
                               frqc=freqInternal,
                               slp=sleep,
                               pdsr=pdsr,
                               pcsr=pcsr,
                               clk0=clk0,
                               clk1=clk1,
                               f0=f0,
                               f1=f1,
                               f2=f2,
                               f3=f3,
                               f4=f4,
                               f5=f5,
                               slsb=sLSB)

        #write config registers
        self.femb.write_reg(self.REG_ASIC_SPIPROG_RESET, 0)
        for iReg in range(0, 5, 1):
            self.femb.write_reg(self.REG_ADCSPI_BASES[asicNumVal] + iReg,
                                self.adc_regs.REGS[iReg])
            #print("{:3}  {:#010x}".format(self.REG_ADCSPI_BASES[iChip]+iReg, chipRegs[iReg]))

    #function programs ADC SPI and does multiple tests to ensure sync is good, note uses recursion
    def doAdcAsicConfig(self, asicNum=None, syncAttempt=0):
        if asicNum == None:
            return None
        asicNumVal = int(asicNum)
        if (asicNumVal < 0) or (asicNumVal >= self.NASICS):
            return None

        #write ADC ASIC SPI, do on intial sync attempt ONLY
        if (self.doSpiWrite == True) and (syncAttempt == 0):
            print("Program ADC ASIC SPI")
            #self.REG_ASIC_SPIPROG_RESET = 2 # bit 0 FE SPI, 1 ADC SPI, 4 FE ASIC RESET, 5 ADC ASIC RESET, 6 SOFT ADC RESET & SPI readback check
            self.femb.write_reg(self.REG_ASIC_SPIPROG_RESET, 0x0)
            self.femb.write_reg(self.REG_ASIC_SPIPROG_RESET, 0x20)  #ADC reset
            time.sleep(0.01)
            self.femb.write_reg(self.REG_ASIC_SPIPROG_RESET, 0x0)
            self.femb.write_reg(self.REG_ASIC_SPIPROG_RESET,
                                0x2)  #ADC SPI write
            time.sleep(0.01)
            self.femb.write_reg(self.REG_ASIC_SPIPROG_RESET, 0x0)
            self.femb.write_reg(self.REG_ASIC_SPIPROG_RESET,
                                0x2)  #ADC SPI write
            time.sleep(0.01)

        #soft reset
        self.femb.write_reg(self.REG_ASIC_SPIPROG_RESET, 0)
        self.femb.write_reg(self.REG_ASIC_SPIPROG_RESET, 0x40)  # soft reset
        self.femb.write_reg(self.REG_ASIC_SPIPROG_RESET, 0)

        #optionally check the ADC sync
        if self.doReSync == False:
            return

        #self.checkAdcSyncBits(asicNumVal)
        self.checkAdcErrorCount(asicNumVal)

        #try again if sync not achieved, note recursion, stops after some maximum number of attempts
        if self.adcSyncStatus == False:
            if syncAttempt >= self.maxSyncAttempts:
                print(
                    "doAsicConfig: Could not sync ADC ASIC, giving up after " +
                    str(self.maxSyncAttempts) + " attempts.")
                return None
            else:
                self.doAdcAsicConfig(asicNumVal, syncAttempt + 1)

    #check ADC sync bits several times to ensure sync is stable
    def checkAdcSyncBits(self, asicNum):
        if asicNum == None:
            return None
        asicNumVal = int(asicNum)
        if (asicNumVal < 0) or (asicNumVal >= self.NASICS):
            return None

        self.adcSyncStatus = False  #assume sync is BAD initially
        isSync = True
        for syncTest in range(0, self.numSyncTests, 1):
            regVal = self.femb.read_reg(2)
            if regVal == None:
                print("doAdcAsicConfig: Could not check SYNC status, bad")
                self.adcSyncStatus = False
                return None

            syncVal = ((regVal >> 24) & 0xFF)
            syncVal = ((syncVal >> 2 * asicNumVal) & 0x3)
            if syncVal != 0x0:
                #bad sync detected
                isSync = False
                break
        self.adcSyncStatus = isSync

    def checkAdcErrorCount(self, asicNum):
        if asicNum == None:
            return None
        asicNumVal = int(asicNum)
        if (asicNumVal < 0) or (asicNumVal >= self.NASICS):
            return None

        #check header error count
        self.adcSyncStatus = False  #assume sync is BAD initially

        self.femb.write_reg(self.REG_HDR_ERROR_RESET, 0)
        self.femb.write_reg(self.REG_HDR_ERROR_RESET, 0x1)  #reset counters
        self.femb.write_reg(self.REG_HDR_ERROR_RESET, 0)

        time.sleep(0.05)  #optional delay

        errRegNum = self.REG_HDR_ERROR_BASES[asicNumVal]
        errorCount = self.femb.read_reg(errRegNum)
        if errorCount == None:
            print("doAdcAsicConfig: Could not check SYNC status, bad")
            return None

        if errorCount == 0:
            self.adcSyncStatus = True

    def fixUnsync(self, asicNum):
        if asicNum == None:
            return None
        asicNumVal = int(asicNum)
        if (asicNumVal < 0) or (asicNumVal >= self.NASICS):
            return None

        initLATCH = self.femb.read_reg(self.REG_LATCHLOC)
        initPHASE = self.femb.read_reg(
            self.REG_ADC_CLK)  # remember bit 16 sample rate

        phases = [0, 1]
        if self.COLD:
            phases = [0, 1, 0, 1, 0]

        #loop through sync parameters
        for shift in range(0, 6, 1):
            shiftMask = (0xFF << 8 * asicNum)
            testShift = ((initLATCH & ~(shiftMask)) | (shift << 8 * asicNum))
            self.femb.write_reg(self.REG_LATCHLOC, testShift)
            time.sleep(0.01)
            for phase in phases:
                clkMask = (0x1 << asicNum)
                testPhase = ((initPHASE & ~(clkMask)) | (phase << asicNum))
                self.femb.write_reg(self.REG_ADC_CLK, testPhase)
                time.sleep(0.01)
                print("try shift: {} phase: {} testingUnsync...".format(
                    shift, phase))
                #try ADC config with new
                self.doAdcAsicConfig(asicNumVal)

                if self.adcSyncStatus == True:
                    print("FEMB_CONFIG--> ADC synchronized")
                    return True
        #if program reaches here, sync has failed
        print("Error: FEMB_CONFIG--> ADC SYNC process failed for ADC # " +
              str(asicNumVal))
        print(
            "Setting back to original values: LATCHLOC: {:#010x}, PHASE: {:#010x}"
            .format(initLATCH, initPHASE & 0xF))
        self.femb.write_reg(self.REG_LATCHLOC, initLATCH)
        self.femb.write_reg(self.REG_ADC_CLK, initPHASE)
        self.adcSyncStatus = False

    def getSyncStatus(self):
        syncBits = None
        adc0 = None
        fe0 = None
        adc1 = None
        fe1 = None
        adc2 = None
        fe2 = None
        adc3 = None
        fe3 = None
        reg = self.femb.read_reg(self.REG_ASIC_SPIPROG_RESET)
        if reg is None:
            print("Error: can't read back sync register")
            return None
        else:
            print("Register 2: {:#010x}".format(reg))
            syncBits = reg >> 24
            reg = reg >> 16
            adc0 = ((reg >> 0) & 1) == 1
            fe0 = ((reg >> 1) & 1) == 1
            adc1 = ((reg >> 2) & 1) == 1
            fe1 = ((reg >> 3) & 1) == 1
            adc2 = ((reg >> 4) & 1) == 1
            fe2 = ((reg >> 5) & 1) == 1
            adc3 = ((reg >> 6) & 1) == 1
            fe3 = ((reg >> 7) & 1) == 1
        return (fe0, fe1, fe2, fe3), (adc0, adc1, adc2, adc3), syncBits

    def printSyncRegister(self):
        (fe0, fe1, fe2, fe3), (adc0, adc1, adc2,
                               adc3), syncBits = self.getSyncStatus()
        print("ASIC Readback Status:")
        print("  ADC 0:", adc0, "FE 0:", fe0)
        print("  ADC 1:", adc1, "FE 1:", fe1)
        print("  ADC 2:", adc2, "FE 2:", fe2)
        print("  ADC 3:", adc3, "FE 3:", fe3)
        print("ADC Sync Bits: {:#010b} (0 is good)".format(syncBits))

    def selectAsic(self, asic):
        """
        asic is chip number 0 to 7
        """
        asicVal = int(asic)
        if (asicVal < 0) or (asicVal >= self.NASICS):
            print(
                "femb_config_femb : selectChan - invalid ASIC number, only 0 to {} allowed"
                .format(self.NASICS - 1))
            return

        #self.femb.write_reg( self.REG_STOP_ADC, 1)
        #time.sleep(0.05)

        # in this firmware asic = 0 disables readout, so asics are 1,2,3,4
        self.femb.write_reg_bits(self.REG_SEL_CH, 0, 0x7, asicVal + 1)
        #self.femb.write_reg( self.REG_STOP_ADC, 0)

    def extClock(self,
                 enable=False,
                 period=500,
                 mult=1,
                 offset_rst=0,
                 offset_read=480,
                 offset_msb=230,
                 offset_lsb=480,
                 width_rst=50,
                 width_read=20,
                 width_msb=270,
                 width_lsb=20,
                 offset_lsb_1st_1=50,
                 width_lsb_1st_1=190,
                 offset_lsb_1st_2=480,
                 width_lsb_1st_2=20,
                 inv_rst=True,
                 inv_read=True,
                 inv_msb=False,
                 inv_lsb=False,
                 inv_lsb_1st=False):
        """
        Programs external clock. All non-boolean arguments except mult are in nanoseconds
        IDXM = msb
        IDXL = lsb
        IDL = lsb_1st
        """

        rd_off = 0
        rst_off = 0
        rst_wid = 0
        msb_off = 0
        msb_wid = 0
        lsb_fc_wid2 = 0
        lsb_fc_off1 = 0
        rd_wid = 0
        lsb_fc_wid1 = 0
        lsb_fc_off2 = 0
        lsb_wid = 0
        lsb_off = 0
        inv = 0

        if enable:
            clock = 1. / self.FPGA_FREQ_MHZ * 1000.  # clock now in ns
            denominator = clock / mult
            period_val = period // denominator

            rd_off = int(offset_read // denominator) & 0xFFFF
            rst_off = int(offset_rst // denominator) & 0xFFFF
            rst_wid = int(width_rst // denominator) & 0xFFFF
            msb_off = int(offset_msb // denominator) & 0xFFFF
            msb_wid = int(width_msb // denominator) & 0xFFFF
            lsb_fc_wid2 = int(width_lsb_1st_2 // denominator) & 0xFFFF
            lsb_fc_off1 = int(offset_lsb_1st_1 // denominator) & 0xFFFF
            rd_wid = int(width_read // denominator) & 0xFFFF
            lsb_fc_wid1 = int(width_lsb_1st_1 // denominator) & 0xFFFF
            lsb_fc_off2 = int(offset_lsb_1st_2 // denominator) & 0xFFFF
            lsb_wid = int(width_lsb // denominator) & 0xFFFF
            lsb_off = int(offset_lsb // denominator) & 0xFFFF

            if inv_rst:
                inv += 1 << 0
            if inv_read:
                inv += 1 << 1
            if inv_msb:
                inv += 1 << 2
            if inv_lsb:
                inv += 1 << 3
            if inv_lsb_1st:
                inv += 1 << 4

        def writeRegAndPrint(name, reg, val):
            #print("ExtClock Register {0:15} number {1:3} set to {2:10} = {2:#010x}".format(name,reg,val))
            #print("ExtClock Register {0:15} number {1:3} set to {2:#034b}".format(name,reg,val))
            self.femb.write_reg(reg, val)

        writeRegAndPrint("inv", self.REG_EXTCLK_INV, inv),
        for iChip, regBase in enumerate(self.REG_EXTCLK_BASES):
            iStr = str(iChip)
            asicRegs = [
                ("RST_ADC" + iStr, (rst_wid << 16) | rst_off),
                ("READ_ADC" + iStr, (rd_wid << 16) | rd_off),
                ("IDXM_ADC" + iStr, (msb_wid << 16) | msb_off),  # msb
                ("IDXL_ADC" + iStr, (lsb_wid << 16) | lsb_off),  # lsb
                ("IDL1_ADC" + iStr,
                 (lsb_fc_wid1 << 16) | lsb_fc_off1),  # lsb_fc_1
                ("IDL2_ADC" + iStr,
                 (lsb_fc_wid2 << 16) | lsb_fc_off2),  # lsb_fc_1
            ]
            for iReg, tup in enumerate(asicRegs):
                name = tup[0]
                val = tup[1]
                writeRegAndPrint(name, regBase + iReg, val)

    def setExtClockRegs(self):
        #Coarse Control
        self.femb.write_reg(10, 0x03030103)  #Invert/Disable all ADC Clocks

        #For ADC1
        self.femb.write_reg(11, 0x00090001)  #ADC1 - RST Offset and Width
        self.femb.write_reg(12, 0x0004005E)  #ADC1 - READ Offset and Width
        self.femb.write_reg(13, 0x0035002C)  #ADC1 - IDXM Offset and Width
        self.femb.write_reg(14, 0x0003005E)  #ADC1 - IDXL Offset and Width
        self.femb.write_reg(15, 0x00250008)  #ADC1 - IDL1 Offset and Width
        self.femb.write_reg(16, 0x0003005E)  #ADC1 - IDL2 Offset and Width

        #For ADC2
        self.femb.write_reg(20, 0x00090001)  #ADC2 - RST Offset and Width
        self.femb.write_reg(21, 0x0003005F)  #ADC2 - READ Offset and Width
        self.femb.write_reg(22, 0x0035002D)  #ADC2 - IDXM Offset and Width
        self.femb.write_reg(23, 0x0003005F)  #ADC2 - IDXL Offset and Width
        self.femb.write_reg(24, 0x00250008)  #ADC2 - IDL1 Offset and Width
        self.femb.write_reg(25, 0x0003005E)  #ADC2 - IDL2 Offset and Width

        #For ADC3
        self.femb.write_reg(29, 0x00090001)  #ADC2 - RST Offset and Width
        self.femb.write_reg(30, 0x0003005F)  #ADC3 - READ Offset and Width
        self.femb.write_reg(31, 0x0035002D)  #ADC3 - IDXM Offset and Width
        self.femb.write_reg(32, 0x0003005F)  #ADC3 - IDXL Offset and Width
        self.femb.write_reg(33, 0x00250008)  #ADC3 - IDL1 Offset and Width
        self.femb.write_reg(34, 0x0003005E)  #ADC3 - IDL2 Offset and Width

        #Fine Control
        #For ADC1
        self.femb.write_reg(17, 0x00060009)  #ADC1 - READ and IDXM Phase Shift
        self.femb.write_reg(18, 0x0005000B)  #ADC1 - IDXL and IDL1 Phase Shift
        self.femb.write_reg(19,
                            0x001F0003)  #ADC1 - IDL2 Phase Shift and inversion
        self.femb.write_reg(19, 0x801F0003)  #ADC1 - OK

        #For ADC2
        self.femb.write_reg(26, 0x0010000E)  #ADC2 - READ and IDXM Phase Shift
        self.femb.write_reg(27, 0x00030009)  #ADC2 - IDXL and IDL1 Phase Shift
        self.femb.write_reg(28,
                            0x001C0002)  #ADC2 - IDL2 Phase Shift and inversion
        self.femb.write_reg(28, 0x801C0002)  #ADC2 - OK
        time.sleep(0.1)
        #For ADC3
        self.femb.write_reg(35, 0x0010000E)  #ADC3 - READ and IDXM Phase Shift
        self.femb.write_reg(36, 0x0005000D)  #ADC3 - IDXL and IDL1 Phase Shift
        self.femb.write_reg(37,
                            0x00180004)  #ADC3 - IDL2 Phase Shift and inversion
        self.femb.write_reg(37, 0x80180004)  #ADC3 - OK

    def turnOffAsics(self):
        self.femb.write_reg_bits(self.REG_PWR_CTRL, 0, 0xF, 0x0)
        #pause after turning off ASICs
        time.sleep(2)

    def turnOnAsic(self, asic):
        if asic == None:
            return None
        asicVal = int(asic)
        if (asicVal < 0) or (asicVal >= self.NASICS):
            print(
                "femb_config_femb : turnOnAsics - invalid ASIC number, only 0 to {} allowed"
                .format(self.NASICS - 1))
            return None

        #check if ASIC is already on in attempt to save time
        regVal = self.femb.read_reg(self.REG_PWR_CTRL)
        if regVal == None:
            return None
        isAsicOn = int(regVal)
        isAsicOn = ((isAsicOn >> asicVal) & 0x1)
        #print("isAsicOn ", hex(regVal), isAsicOn)
        if isAsicOn == 0x1:
            return

        print("Turning on ASIC ", asicVal)
        self.femb.write_reg_bits(self.REG_PWR_CTRL, asicVal, 0x1, 0x1)
        time.sleep(5)  #pause after turn on

    def turnOnAsics(self):
        print("turnOnAsics 0-{}".format(int(self.NASICS - 1)))
        for iAsic in range(0, self.NASICS, 1):
            self.turnOnAsic(iAsic)

    def setFPGADac(self, amp, mode, freq, delay):
        """
        mode: 0 DAC only, 1 ext tp, 2 gnd, 3 1.8V, 4 test pulse, 5 1.8V FE, 6 ASIC TP DAC
        """
        ampRegVal = ((mode & 0xFFFF) << 16) | (amp & 0xFFFF)
        freqRegVal = ((delay & 0xFFFF) << 16) | (freq & 0xFFFF)

        self.femb.write_reg(self.REG_DAC2, freqRegVal)
        time.sleep(0.05)
        self.femb.write_reg(self.REG_DAC1, ampRegVal)
        time.sleep(0.05)
        self.femb.write_reg(self.REG_DAC1, ampRegVal & 0x80000000)
        time.sleep(0.05)
        self.femb.write_reg(self.REG_DAC1, ampRegVal)

    def writePLLs(self, step0, step1, step2):
        for iChip in range(0, 3, 1):
            self.writePLL(iChip, step0, step1, step2)

    def writePLL(self, asic, step0, step1, step2):
        if asic == None:
            return None
        asicVal = int(asic)
        if (asicVal < 0) or (asicVal >= self.NASICS):
            print(
                "femb_config_femb : writePLL - invalid ASIC number, only 0 to {} allowed"
                .format(self.NASICS - 1))
            return

        regBase = self.REG_PLL_BASES[asicVal]
        self.femb.write_reg(regBase + 0, step0)
        self.femb.write_reg(regBase + 1, step1)
        self.femb.write_reg(regBase + 2, step2)
Exemple #3
0
class FEMB_CONFIG:
    def __init__(self):
        self.NASICS = 4

        #declare board specific registers
        self.REG_RESET = 0
        self.REG_FEASIC_SPI = 5
        self.REG_FESPI_BASE = 20
        self.REG_FESPI_RDBACK_BASE = None
        self.REG_ADCSPI_RDBACK_BASE = None
        self.REG_HS = 17
        self.REG_TEST_PULSE = 99
        self.REG_TEST_PULSE_FREQ = 500
        self.REG_TEST_PULSE_DLY = 80
        self.REG_TEST_PULSE_AMPL = 0 % 32
        self.REG_EN_CALI = 16

        self.REG_RESET = 0
        self.REG_DAC_VALUE = 1
        self.REG_SET_DAC = 2
        self.REG_START = 3
        self.REG_SEL_ASIC = 4
        self.REG_SEL_CH = 4
        self.REG_ASIC_RESET = 5
        self.REG_ASIC_SPIPROG = 5
        self.REG_SAMPLE_STOP = 6
        self.REG_TP_PERIOD_P = 7
        self.REG_TP_PERIOD_N = 7
        self.REG_TP_MODE = 9
        self.REG_TST_SW = 12
        self.REG_LED_CNTL = 13
        self.REG_FESPI_BASE = 20
        self.REG_FRAME_SIZE = 40
        self.REG_DAC_ADC_EN = 60
        self.REG_TST_SW = 12

        self.plot = plot_functions()
        self.comm_settings = None

        #initialize FEMB UDP object
        self.femb = FEMB_UDP()
        self.fe_reg = FE_ASIC_REG_MAPPING()

        self.WIB_RESET = 1

        self.BPS = 13  #Bytes per sample
        self.selected_chip = None
        self.selected_chn = None

    def resetFEMBBoard(self):
        print("FEMB_CONFIG--> Reset FEMB (4 seconds)")
        sys.stdout.flush()
        #Reset FEMB system
        self.femb.write_reg(self.REG_RESET, 1)
        time.sleep(4)
        print("FEMB_CONFIG--> Reset FEMB is DONE")

    def initBoard(self):
        print("FEMB_CONFIG--> Initialize FEMB")
        #set up default registers

        #Tells the FPGA to turn on the ASICs
        self.femb.write_reg(12, 0x0)
        #self.turnOnAsics() #added

        #Tells the FPGA to turn off each DAC
        self.femb.write_reg(61, 0xF)

        #Set to Regular Mode (as opposed to Sampling Scope mode) and pick a chip/channel output
        self.femb.write_reg(10, 0)
        self.select_chip_chn(chip=2, chn=7)

        #Select TP FE Mode
        self.femb.write_reg(9, 8)

        #Give a default DAC value
        self.femb.write_reg(1, settings.default_DAC)
        self.femb.write_reg(2, 1)
        self.femb.write_reg(2, 0)

        #Give a default pulse timing
        self.femb.write_reg(7, (settings.default_TP_Shift << 16) +
                            settings.default_TP_Period)

        #Write the default ADC settings
        i = 0
        for reg in range(65, 69, 1):
            self.femb.write_reg(reg, settings.Latch_Settings[i])
            i = i + 1

        i = 0
        for reg in range(69, 73, 1):
            self.femb.write_reg(reg, settings.Phase_Settings[i])
            i = i + 1

        self.femb.write_reg(73, settings.test_ADC_Settings)

        self.femb.write_reg(74, settings.pre_buffer)

        #Write the frame size as a multiple of 16
        frame_size = settings.frame_size
        if (frame_size % 16 != 0):
            frame_size = 16 * (frame_size // 16)

        #Write the defaul ASIC settings
        self.fe_reg.set_fe_board(sts=1,
                                 snc=1,
                                 sg=1,
                                 st=1,
                                 smn=0,
                                 sbf=1,
                                 slk=0,
                                 stb=0,
                                 s16=0,
                                 slkh=0,
                                 sdc=0,
                                 sdacsw2=1,
                                 sdacsw1=0,
                                 sdac=settings.sync_peak_height)
        print("FEMB_CONFIG --> FE ASIC SPI")
        self.configFeAsic()

        print("FEMB_CONFIG--> Initialize FEMB is DONE")

    def turnOffAsics(self):
        self.femb.write_reg(self.REG_TST_SW, 0xF)
        #pause after turning off ASICs
        time.sleep(2)

    def turnOnAsic(self, asic):
        asicVal = int(asic)
        if (asicVal < 0) or (asicVal >= self.NASICS):
            print(
                "femb_config_femb : turnOnAsics - invalid ASIC number, only 0 to {} allowed"
                .format(self.NASICS - 1))
            return
        print("turnOnAsic " + str(asicVal))
        #self.femb.write_reg( self.REG_TST_SW, 0xF) #turn off all
        self.femb.write_reg_bits(self.REG_TST_SW, asicVal, 0x1, 0x0)
        time.sleep(2)  #pause after turn on

        #start ASICs
        self.femb.write_reg(self.REG_START, 1)
        self.configFeAsic()

    def turnOnAsics(self):
        #print( "Turn On Asics" )
        #self.femb.write_reg( self.REG_TST_SW, 0x0)
        #time.sleep(1) #pause after turn on
        #start ASICs
        #self.femb.write_reg( self.REG_START, 1)
        #self.configFeAsic()

        self.turnOnAsic(0)
        self.turnOnAsic(1)
        self.turnOnAsic(2)
        self.turnOnAsic(3)

        #pause after turning on ASICs
        time.sleep(2)

    def configFeAsic(self, to_print=False):
        #Grab ASIC settings from linked class
        Feasic_regs = self.fe_reg.REGS
        #Choose to output status updates or not
        if (to_print == True):
            print("FEMB_CONFIG--> Config FE ASIC SPI")
        #Try 10 times (ocassionally it wont work the first time)
        for k in range(10):
            i = 0
            for regNum in range(self.REG_FESPI_BASE,
                                self.REG_FESPI_BASE + len(Feasic_regs), 1):
                self.femb.write_reg(regNum, Feasic_regs[i])
                #                    print (hex(Feasic_regs[i]))
                i = i + 1

            #Write ADC ASIC SPI
            if (to_print == True):
                print("FEMB_CONFIG--> Program FE ASIC SPI")
            #Reset, then write twice
            self.femb.write_reg(self.REG_FEASIC_SPI, 2)
            self.femb.write_reg(self.REG_FEASIC_SPI, 0)
            time.sleep(.2)
            self.femb.write_reg(self.REG_FEASIC_SPI, 1)
            self.femb.write_reg(self.REG_FEASIC_SPI, 0)
            time.sleep(.2)
            self.femb.write_reg(self.REG_FEASIC_SPI, 1)
            self.femb.write_reg(self.REG_FEASIC_SPI, 0)
            time.sleep(.2)

            if (to_print == True):
                print("FEMB_CONFIG--> Check FE ASIC SPI")

            #The FPGA automatically compares the readback to check if it matches what was written.  That result is read back
            #A bit that's zero means the corresponding ASIC didn't write properly
            val = self.femb.read_reg(self.REG_FEASIC_SPI)
            wrong = False

            if (((val & 0x10000) >> 16) != 1 and (0 in settings.chips_to_use)):
                print(
                    "FEMB_CONFIG--> Something went wrong when programming FE 1"
                )
                wrong = True

            if (((val & 0x20000) >> 17) != 1 and (1 in settings.chips_to_use)):
                print(
                    "FEMB_CONFIG--> Something went wrong when programming FE 2"
                )
                wrong = True

            if (((val & 0x40000) >> 18) != 1 and (2 in settings.chips_to_use)):
                print(
                    "FEMB_CONFIG--> Something went wrong when programming FE 3"
                )
                wrong = True

            if (((val & 0x80000) >> 19) != 1 and (3 in settings.chips_to_use)):
                print(
                    "FEMB_CONFIG--> Something went wrong when programming FE 4"
                )
                wrong = True

            if (wrong == True and k == 9):
                print("FEMB_CONFIG--> SPI_Status is {}").format(hex(val))
                #sys.exit("FEMB_CONFIG--> femb_config_femb : Wrong readback. FE SPI failed")
                return

            elif (wrong == False):
                self.fe_reg.info.fe_regs_sent = Feasic_regs
                if (to_print == True):
                    print("FEMB_CONFIG--> FE ASIC SPI is OK")
                break

    #ASSUMES ASICS HAVE BEEN SET FOR THE INTERNAL PULSER
    def syncADC(self, chips):
        print("FEMB_CONFIG--> Start sync ADC")

        #Don't ask me why, but you need to set the channel at this point for it to output the right data
        self.select_chip_chn(chip=0, chn=1)
        for chip in chips:

            #Tells the FPGA to turn on each DAC
            self.femb.write_reg(61, 0x0)

            #Read from DATA output ADCs
            self.femb.write_reg(60, 0)

            #Set to Regular Mode (not sampling scope mode)
            self.femb.write_reg(10, 0)

            #Select the internal DAC readout
            self.femb.write_reg(9, 3)

            #Get the ASIC to send out pulses.  Bit 6 needs to be high for ASIC DAC
            self.reg_17_value = (settings.default_TP_Period << 16) + (
                settings.default_TP_Shift << 8) + (0b01000000)
            self.femb.write_reg(17, self.reg_17_value)

            print("FEMB_CONFIG--> Test ADC {}".format(chip))

            for chn in range(settings.channels):
                #Tests if it's synchronized, returns True if it is
                unsync = self.testUnsync(chip=chip, chn=chn)
                if unsync != True:
                    print(
                        "FEMB_CONFIG--> Chip {}, Chn {} not synced, try to fix"
                        .format(chip, chn))
                    response = self.fixUnsync_outputADC(chip=chip, chn=chn)
                    if (response != True):
                        print(
                            "FEMB_CONFIG--> Something is wrong with Chip {}, Chn {}"
                            .format(chip, chn))
#                        sys.exit ("FEMB_CONFIG--> ADC {} could not sync".format(chip))

            print("FEMB_CONFIG--> ADC {} synced!".format(chip))

            print("FEMB_CONFIG--> Trying to sync test ADC".format(chip))

            #Have one of the channels output its pulse to the test monitor pin
            self.fe_reg.set_fe_chn(chip=chip, chn=0, smn=1)
            self.configFeAsic()

            #Read from TEST output ADCs
            self.femb.write_reg(60, 1)

            #Select the monitor readout
            self.femb.write_reg(9, 3)

            unsync = self.testUnsync(chip=chip, chn=chn)
            if unsync != True:
                print(
                    "FEMB_CONFIG--> Chip {} (test ADC) not synced, try to fix".
                    format(chip))
                response = self.fixUnsync_testADC(chip=chip)
                if (response != True):
                    print(
                        "FEMB_CONFIG--> Something is wrong with Chip {} (test ADC)"
                        .format(chip))
            else:
                print("FEMB_CONFIG--> Chip {} (test ADC) synced!".format(chip))

        self.comm_settings = []
        print("FEMB_CONFIG--> Final Shift Settings: ")
        for reg in range(65, 69, 1):
            value = self.femb.read_reg(reg)
            print("Register {}: {}".format(reg, hex(value)))
            self.comm_settings.append(value)

        print("FEMB_CONFIG--> Final Phase Settings: ")
        for reg in range(69, 73, 1):
            value = self.femb.read_reg(reg)
            print("Register {}: {}".format(reg, hex(value)))
            self.comm_settings.append(value)

        value = self.femb.read_reg(73)
        print("Register {}: {}".format(73, hex(value)))
        self.comm_settings.append(value)

        self.femb.write_reg(17, 0)

        print("FEMB_CONFIG--> ADC passed Sync Test!")

    def testUnsync(self, chip, chn):
        #Get some packets of data
        self.select_chip_chn(chip=chip, chn=chn)
        data = self.get_data_chipXchnX(chip=chip,
                                       chn=chn,
                                       packets=25,
                                       data_format="counts")

        #Find some peaks
        peaks_index = detect_peaks(x=data, mph=settings.sync_peak_min, mpd=100)
        #Make sure you have more than 0 peaks (so it's not flat) and less than the maximum
        #(if there's no peak, the baseline will give hundreds of peaks)
        #If it's bad, print it, so we see (must enable inline printing for iPython)
        if (len(peaks_index)
                == 0) or (len(peaks_index) > settings.sync_peaks_max):
            print("Chip {}, Channel {} has {} peaks!".format(
                chip, chn, len(peaks_index)))
            #            print (peaks_index)
            figure_data = self.plot.quickPlot(data)
            ax = figure_data[0]
            for j in peaks_index:
                y_value = data[j]
                ax.scatter(j / 2, y_value, marker='x')

            ax.set_ylabel('mV')
            ax.set_title("Error")
            ax.title.set_fontsize(30)
            for item in ([ax.xaxis.label, ax.yaxis.label] +
                         ax.get_xticklabels() + ax.get_yticklabels()):
                item.set_fontsize(20)
            plt.show()
            plt.close()
            return False

        #So that function before only gives you the X locations of where the peaks are.  Let's get the Y values from that
        peaks_value = []
        for i in peaks_index:
            peaks_value.append(data[i])
#        print ("Chip {}, Channel {} has peak values {}".format(chip, chn, peaks_value))
#Check if the peak is at the wrong height (happens when it's not synced, the peak will be havled or doubled)
        for peak in peaks_value:
            if ((peak < settings.sync_peak_min)
                    or (peak > settings.sync_peak_max)):
                print("FEMB CONFIG--> Chip {}, Chn {} has a peak that's {}".
                      format(chip, chn, peak))
                figure_data = self.plot.quickPlot(data)
                ax = figure_data[0]
                for j in peaks_index:
                    y_value = data[j]
                    ax.scatter(j / 2, y_value, marker='x')

                ax.set_ylabel('mV')
                ax.set_title("Error")
                ax.title.set_fontsize(30)
                for item in ([ax.xaxis.label, ax.yaxis.label] +
                             ax.get_xticklabels() + ax.get_yticklabels()):
                    item.set_fontsize(20)
                plt.show()
                plt.close()
                return False
        #Check if the baseline is right (this also gets halved and doubled with unsynced ADCs) (avoid the peak to grab a middle section)
        try:
            baseline_area_start = peaks_index[0] + 200
            baseline_area_end = peaks_index[1] - 200
        except IndexError:
            baseline_area_start = 100
            baseline_area_end = 500
        baseline_data = data[baseline_area_start:baseline_area_end]
        baseline = np.mean(baseline_data)
        if ((baseline < settings.sync_baseline_min)
                or (baseline > settings.sync_baseline_max)):
            print("FEMB CONFIG--> Chip {}, Chn {} has a baseline that's {}".
                  format(chip, chn, baseline))
            figure_data = self.plot.quickPlot(data)
            ax = figure_data[0]
            for j in peaks_index:
                y_value = data[j]
                ax.scatter(j / 2, y_value, marker='x')

            ax.set_ylabel('mV')
            ax.set_title("Error")
            ax.title.set_fontsize(30)
            for item in ([ax.xaxis.label, ax.yaxis.label] +
                         ax.get_xticklabels() + ax.get_yticklabels()):
                item.set_fontsize(20)
            plt.show()
            plt.close()
            return False
        return True

    #Shifts through all possible delay and phase options, checking to see if each one fixed the issue
    def fixUnsync_outputADC(self, chip, chn):
        self.select_chip_chn(chip=chip, chn=chn)

        shift_reg = chip + 65
        phase_reg = chip + 69

        #Get the initial setting you don't disturb the other channels
        init_shift = self.femb.read_reg(shift_reg)
        init_phase = self.femb.read_reg(phase_reg)

        #Because Python can't have a friggin NAND or XOR function that works right at the bitwise level, this is how we get the bitmask
        #This will find the 2 bits in the register that correspond to the channel you want to change.  Say it's channel 2
        #In binary, it makes 0b00000000000000000000000000110000
        #Then inverts it to  0b11111111111111111111111111001111
        #Now you AND that with the initial result to get the initial values WITHOUT the channel you want to change
        #Now you can bitshift your desired setting to that empty space with zeroes, like say you wanted to write a 0b10,
        #It would be 0b100000.  Then add that to whatever the result of the initial values AND mask was.  Boom, you've done it.
        init_mask = (0x3 << (2 * chn))
        neg_mask = 0
        for i in range(32):
            FF_bit = (0xFFFFFFFF >> i) & 0x1
            test_bit = (init_mask >> i) & 0x1
            if (FF_bit != test_bit):
                result_bit = 1
            else:
                result_bit = 0
            neg_mask = neg_mask + (result_bit << i)

        #There are 16 possible sync permutations for every ADC
        for shift in range(4):
            for phase in range(4):

                shift_setting = shift << (2 * chn)
                init_shift_with_mask = init_shift & neg_mask
                final_shift = shift_setting + init_shift_with_mask

                #                print ("Shift is {}".format(shift))
                #                print ("phase is {}".format(phase))
                #                print ("Negative mask is {}".format(bin(neg_mask)))
                #                print ("Initial reading is {}".format(bin(init_shift)))
                #                print ("The new setting is {}".format(bin(shift_setting)))
                #                print ("Making space for the new setting is {}".format(bin(init_shift_with_mask)))
                #                print ("Adding together is {}".format(bin(final_shift)))

                self.femb.write_reg(shift_reg, final_shift)

                phase_setting = phase << (2 * chn)
                init_phase_with_mask = init_phase & neg_mask
                final_phase = phase_setting + init_phase_with_mask

                self.femb.write_reg(phase_reg, final_phase)

                #See if this new setting fixed it
                unsync = self.testUnsync(chip=chip, chn=chn)
                if unsync == True:
                    print("FEMB_CONFIG--> Chip {}, Chn {} fixed!".format(
                        chip, chn))
                    return True

        print("FEMB_CONFIG--> ADC SYNC process failed for Chip {}, Channel {}".
              format(chip, chn))
        self.femb.write_reg(shift_reg, init_shift)
        self.femb.write_reg(phase_reg, init_phase)
        return False

    #Same thing as above, except the timing register is different, so the bitmath has to be changed.
    def fixUnsync_testADC(self, chip):
        self.select_chip_chn(chip=chip, chn=2)
        init_mask = (0xF << (2 * chip))

        neg_mask = 0
        for i in range(32):
            FF_bit = (0xFFFFFFFF >> i) & 0x1
            test_bit = (init_mask >> i) & 0x1
            if (FF_bit != test_bit):
                result_bit = 1
            else:
                result_bit = 0
            neg_mask = neg_mask + (result_bit << i)

        init_shift = self.femb.read_reg(73)
        print("Init shift is {}".format(hex(init_shift)))
        print("Init mask is {}".format(bin(init_mask)))
        print("Negative mask is {}".format(bin(neg_mask)))
        for shift in range(4):
            for phase in range(4):
                setting = (phase << 2) + shift
                print("Setting is {}".format(bin(setting)))

                final_setting = setting << (chip * 4)
                print("Final Setting is {}".format(bin(setting)))

                init_shift_with_mask = init_shift & neg_mask
                print("Initshift with mask is {}".format(
                    hex(init_shift_with_mask)))

                really_final = init_shift_with_mask + final_setting
                print("Final setting to write is {}".format(bin(really_final)))

                #                init_shift_with_mask = init_shift & neg_mask
                #                final_shift = shift_setting + init_shift_with_mask

                #                print ("Shift is {}".format(shift))
                #                print ("phase is {}".format(phase))
                #                print ("Negative mask is {}".format(bin(neg_mask)))
                #                print ("Initial reading is {}".format(bin(init_shift)))
                #                print ("The new setting is {}".format(bin(shift_setting)))
                #                print ("Making space for the new setting is {}".format(bin(init_shift_with_mask)))
                #                print ("Adding together is {}".format(bin(final_shift)))

                unsync = self.testUnsync(chip=chip, chn=1)
                if unsync == True:
                    print(
                        "FEMB_CONFIG--> Chip {} test ADC fixed!".format(chip))
                    return True

        print("FEMB_CONFIG--> ADC SYNC process failed for Chip {} ADC".format(
            chip))
        return False

    #Select the chip and channel and read it out.  And check that it was actually set.  It's weirdly a problem
    def select_chip_chn(self, chip, chn):
        if (chip < 0) or (chip > settings.chip_num):
            print(
                "FEMB CONFIG -> Error in get_data_chipXchnX: Chip must be between 0 and {}"
                .format(settings.chip_num))
            return

        if (chn < 0) or (chn > 15):
            print(
                "FEMB CONFIG -> Error in get_data_chipXchnX: Channel must be between 0 and 15"
            )
            return

        chip_int = int(chip + 1)
        chn_int = int(chn)

        reg4_value = chn_int + (chip_int << 8)

        for i in range(10):
            self.femb.write_reg(4, reg4_value)
            time.sleep(0.1)
            reg4_read = self.femb.read_reg(4)
            try:
                if ((reg4_read & 0xF) != chn) or ((
                    (reg4_read >> 8) & 0xF) != chip + 1):
                    if (i == 9):
                        sys.exit(
                            "FEMB CONFIG--> Register response was {}, should have been {}, trying again..."
                            .format(hex(reg4_read), hex(reg4_value)))
                    else:
                        print(
                            "FEMB CONFIG--> Register response was {}, should have been {}, trying again..."
                            .format(hex(reg4_read), hex(reg4_value)))
                else:
                    self.selected_chip = chip
                    self.selected_chn = chn
                    break
            except TypeError:
                print("FEMB CONFIG--> No readback value, trying again...")

    #Get data in a variety of ways.  You can even pre-convert it to mV!
    def get_data_chipXchnX(self, chip, chn, packets=1, data_format="counts"):

        if (chn < -1) or (chn > 15):
            print(
                "FEMB CONFIG -> Error in get_data_chipXchnX: Channel must be between 0 and 15, or -1 for all channels"
            )
            return

        if (chip < 0) or (chip > settings.chip_num):
            print(
                "FEMB CONFIG -> Error in get_data_chipXchnX: Chip must be between 0 and {}, but it's {}"
                .format(settings.chip_num, chip))
            return

        if (self.selected_chip != chip) or (self.selected_chn != chn):
            self.select_chip_chn(chip=chip, chn=chn)

        if (data_format == "bin"):
            data = self.femb.get_data_packets(data_type="bin",
                                              num=packets,
                                              header=True)

        data = self.femb.get_data_packets(data_type="int",
                                          num=packets,
                                          header=False)

        if (data_format == "mV"):
            for i in range(len(data)):
                data[i] = data[i] * (0.0018 / 16384)

        elif (data_format == "V"):
            for i in range(len(data)):
                data[i] = data[i] * (1.8 / 16384)
        return data

    #Get a whole chip's worth of data
    def get_data_chipX(self, chip, packets=1, data_format="counts"):

        chip_data = [
            chip, [], [], [], [], [], [], [], [], [], [], [], [], [], [], [],
            []
        ]
        for chn in range(16):
            for i in range(packets):
                chip_data[chn + 1].extend(
                    list(
                        self.get_data_chipXchnX(chip,
                                                chn,
                                                packets=1,
                                                data_format=data_format)[1:]))
        return chip_data

    #Placeholder for the full chip readout
    def get_data_chipXchnX_SS(self, chip, chn, packets=1):

        if (chn < -1) or (chn > 15):
            print(
                "FEMB CONFIG -> Error in get_data_chipXchnX: Channel must be between 0 and 15, or -1 for all channels"
            )
            return

        if (chip < 0) or (chip > settings.chip_num):
            print(
                "FEMB CONFIG -> Error in get_data_chipXchnX: Chip must be between 0 and {}, but it's {}"
                .format(settings.chip_num, chip))
            return

        if (self.selected_chip != chip) or (self.selected_chn != chn):
            self.select_chip(chip=chip, chn=chn)

        k = 0
        for i in range(10):

            data = self.femb.get_data_packets(data_type="int",
                                              num=packets,
                                              header=False)

            try:
                if (k > 0):
                    print("FEMB CONFIG --> Now doing another test")
                    print(hex(data[0]))
                    print(data[0] == 0xFACE)
                    print(data[0] != 0xFACE)
                if (data[0] != 0xFACE):
                    #If FACE isn't the first 2 bytes, do the equivalent of turning WIB mode off and then on and try again
                    self.femb.write_reg(3, chip + 1)
                    self.select_chip(chip)

                    if (k > 8):
                        print(
                            "FEMB CONFIG --> Error in get_data_chipXchnX: Packet format error"
                        )
                        print(hex(data[0]))
                        print(data)
                        return None
                    else:
                        print(
                            "FEMB CONFIG --> Error in get_data_chipXchnX: Packet format error, trying again..."
                        )
                        print("k = {}".format(k))
                        print(data[0:13])
                        print(hex(data[0]))
                        print(data[0] == 0xFACE)
                        k += 1
                else:
                    break
            except IndexError:
                print(
                    "FEMB CONFIG --> Something was wrong with the incoming data"
                )
                print(data)

        test_length = len(data)

        #        if ((test_length % self.BPS) != 0):
        #            print ("FEMB CONFIG -> Error in get_data_chipXchnX: Irregular packet size")
        #            print (data)
        #            return None

        full_samples = test_length // self.BPS

        chn_data = []

        for i in range(full_samples):
            if (chn == 7):
                chn_data.append(data[(self.BPS * i) + 1] & 0x0FFF)
            if (chn == 6):
                chn_data.append(((data[(self.BPS * i) + 2] & 0x00FF) << 4) +
                                ((data[(self.BPS * i) + 1] & 0xF000) >> 12))
            if (chn == 5):
                chn_data.append(((data[(self.BPS * i) + 3] & 0x000F) << 8) +
                                ((data[(self.BPS * i) + 2] & 0xFF00) >> 8))
            if (chn == 4):
                chn_data.append(((data[(self.BPS * i) + 3] & 0xFFF0) >> 4))
            if (chn == 3):
                chn_data.append(data[(self.BPS * i) + 4] & 0x0FFF)
            if (chn == 2):
                chn_data.append(((data[(self.BPS * i) + 5] & 0x00FF) << 4) +
                                ((data[(self.BPS * i) + 4] & 0xF000) >> 12))
            if (chn == 1):
                chn_data.append(((data[(self.BPS * i) + 6] & 0x000F) << 8) +
                                ((data[(self.BPS * i) + 5] & 0xFF00) >> 8))
            if (chn == 0):
                chn_data.append(((data[(self.BPS * i) + 6] & 0xFFF0) >> 4))
            if (chn == 15):
                chn_data.append(data[(self.BPS * i) + 7] & 0x0FFF)
            if (chn == 14):
                chn_data.append(((data[(self.BPS * i) + 8] & 0x00FF) << 4) +
                                ((data[(self.BPS * i) + 7] & 0xF000) >> 12))
            if (chn == 13):
                chn_data.append(((data[(self.BPS * i) + 9] & 0x000F) << 8) +
                                ((data[(self.BPS * i) + 8] & 0xFF00) >> 8))
            if (chn == 12):
                chn_data.append(((data[(self.BPS * i) + 9] & 0xFFF0) >> 4))
            if (chn == 11):
                chn_data.append(data[(self.BPS * i) + 10] & 0x0FFF)
            if (chn == 10):
                chn_data.append(((data[(self.BPS * i) + 11] & 0x00FF) << 4) +
                                ((data[(self.BPS * i) + 10] & 0xF000) >> 12))
            if (chn == 9):
                chn_data.append(((data[(self.BPS * i) + 12] & 0x000F) << 8) +
                                ((data[(self.BPS * i) + 11] & 0xFF00) >> 8))
            if (chn == 8):
                chn_data.append(((data[(self.BPS * i) + 12] & 0xFFF0) >> 4))
            if (chn == -1):
                return (data)

        return chn_data
Exemple #4
0
class FEMB_CONFIG(FEMB_CONFIG_BASE):

    #__INIT__#
    def __init__(self):
        #declare basic system parameters
        self.NFEMBS = 4
        self.NASICS = 8
        self.NASICCH = 16

        #declare board specific registers
        self.FEMB_VER = "WIB_PROTODUNE"
        self.REG_RESET = 0
        self.REG_ASIC_RESET = 1
        self.REG_ASIC_SPIPROG = 2
        self.REG_SOFT_ADC_RESET = 1

        self.REG_LATCHLOC_3_TO_0 = 4
        self.REG_LATCHLOC_7_TO_4 = 14

        self.REG_FPGA_TP_EN = 16
        self.REG_ASIC_TP_EN = 16
        self.REG_DAC_SELECT = 16
        self.REG_TP = 5

        self.CLK_SELECT = 6
        self.CLK_SELECT2 = 15

        self.REG_SEL_ASIC = 7
        self.REG_SEL_ASIC_LSB = 8

        self.REG_WIB_MODE = 8
        self.REG_ADC_DISABLE = 8

        self.REG_HS_DATA = 9
        self.REG_HS = 17

        self.INT_TP_EN = 18
        self.EXT_TP_EN = 18

        self.REG_SPI_BASE = 0x200
        self.REG_SPI_RDBACK_BASE = 0x250

        #internal variables
        self.fembNum = 0
        self.useExtAdcClock = True
        self.isRoomTemp = False
        self.maxSyncAttempts = 100
        self.doReSync = True
        self.spiStatus = 0x0
        self.syncStatus = 0x0
        self.CLKSELECT_val_RT = 0xFF
        self.CLKSELECT2_val_RT = 0xFF
        self.CLKSELECT_val_CT = 0xEF
        self.CLKSELECT2_val_CT = 0xEF
        self.REG_LATCHLOC_3_TO_0_val = 0x04040404
        self.REG_LATCHLOC_7_TO_4_val = 0x04040404

        #initialize FEMB UDP object
        self.femb = FEMB_UDP()
        self.femb.UDP_IP = "192.168.121.1"
        self.femb.UDP_PORT_WREG = 32000  #WIB PORTS
        self.femb.UDP_PORT_RREG = 32001
        self.femb.UDP_PORT_RREGRESP = 32002
        self.femb.doReadBack = True  #WIB register interface is unreliable

        #ASIC config variables
        self.feasicLeakage = 0  #0 = 500pA, 1 = 100pA
        self.feasicLeakagex10 = 0  #0 = pA, 1 = pA*10
        self.feasicAcdc = 0  #AC = 0, DC = 1

        self.feasicEnableTestInput = 0  #0 = disabled, 1 = enabled
        self.feasicBaseline = 0  #0 = 200mV, 1 = 900mV
        self.feasicGain = 2  #4.7,7.8,14,25
        self.feasicShape = 1  #0.5,1,2,3
        self.feasicBuf = 0  #0 = OFF, 1 = ON

    def printParameters(self):
        print("FEMB #             \t", self.fembNum)
        print("External ADC Clocks\t", self.useExtAdcClock)
        print("Room temperature   \t", self.isRoomTemp)
        print("MAX SYNC ATTEMPTS  \t", self.maxSyncAttempts)
        print("Do resync          \t", self.doReSync)
        print("CLKSELECT RT       \t", str(hex(self.CLKSELECT_val_RT)))
        print("CLKSELECT2 RT      \t", str(hex(self.CLKSELECT2_val_RT)))
        print("CLKSELECT CT       \t", str(hex(self.CLKSELECT_val_CT)))
        print("CLKSELECT2 CT      \t", str(hex(self.CLKSELECT2_val_CT)))
        print("LATCHLOC_3_TO_0    \t", str(hex(self.REG_LATCHLOC_3_TO_0_val)))
        print("LATCHLOC_7_TO_4    \t", str(hex(self.REG_LATCHLOC_7_TO_4_val)))
        print("FE-ASIC leakage    \t", self.feasicLeakage)
        print("FE-ASIC leakage x10\t", self.feasicLeakagex10)
        print("FE-ASIC AD/DC      \t", self.feasicAcdc)
        print("FE-ASIC test input \t", self.feasicEnableTestInput)
        print("FE-ASIC baseline   \t", self.feasicBaseline)
        print("FE-ASIC gain       \t", self.feasicGain)
        print("FE-ASIC shape      \t", self.feasicShape)
        print("FE-ASIC buffer     \t", self.feasicBuf)

        print("FE-ASIC config")
        for regNum in range(self.REG_SPI_BASE, self.REG_SPI_BASE + 72, 1):
            regVal = self.femb.read_reg(regNum)
            if regVal == None:
                continue
            print(str(regNum) + "\t" + str(hex(regVal)))

    def resetBoard(self):
        print("Reset")

    def initBoard(self):
        self.initWib()
        for femb in range(0, 4, 1):
            self.selectFemb(femb)
            self.initFemb()

    def initWib(self):
        #WIB initialization

        #set UDP ports to WIB registers
        self.femb.UDP_PORT_WREG = 32000
        self.femb.UDP_PORT_RREG = 32001
        self.femb.UDP_PORT_RREGRESP = 32002
        self.femb.REG_SLEEP = 0.001

        #register 2, LED
        self.femb.write_reg_bits(2, 0, 0xFF, 0)

        #set jumbo size
        self.femb.write_reg(0x1F, 0xEFB)

        #clock select (firmware version dependent)
        #self.femb.write_reg_bits(4 , 2, 0x3, 2 )

        #initialize clock
        #self.initSI5338()

        #set external clock
        self.femb.write_reg(0x4, 8)
        self.femb.write_reg(16, 0x7F00)
        self.femb.write_reg(15, 0)

        #sync timestamp /WIB
        self.femb.write_reg(1, 0)
        self.femb.write_reg(1, 0)
        self.femb.write_reg(1, 2)
        self.femb.write_reg(1, 2)
        self.femb.write_reg(1, 0)
        self.femb.write_reg(1, 0)

        #Reset error /WIB
        self.femb.write_reg(18, 0x8000)
        self.femb.write_reg(18, 0x8000)

        #return register interface to FEMB
        self.selectFemb(self.fembNum)

    def initFemb(self):
        if (self.fembNum < 0) or (self.fembNum >= self.NFEMBS):
            return

        #FEMB power enable on WIB
        self.powerOnFemb(self.fembNum)
        time.sleep(4)

        #Make sure register interface is for correct FEMB
        self.selectFemb(self.fembNum)

        #check if FEMB register interface is working
        print("Checking register interface")
        regVal = self.femb.read_reg(6)
        if (regVal == None) or (regVal == -1):
            print("Error - FEMB register interface is not working.")
            print(" Will not initialize FEMB.")
            return

        checkFirmware = self.checkFirmwareVersion()
        if checkFirmware == False:
            print(
                "Error - invalid firmware, will not attempt to initialize board"
            )
            return

        #turn off pulser
        self.femb.write_reg_bits(self.REG_FPGA_TP_EN, 0, 0x1,
                                 0)  #test pulse enable
        self.femb.write_reg_bits(self.REG_ASIC_TP_EN, 1, 0x1,
                                 0)  #test pulse enable
        self.femb.write_reg_bits(self.REG_DAC_SELECT, 8, 0x1,
                                 0)  #test pulse enable
        self.femb.write_reg_bits(self.REG_TP, 0, 0x1F,
                                 0x00)  #test pulse amplitude
        self.femb.write_reg_bits(self.REG_TP, 16, 0xFFFF,
                                 0x100)  #test pulse frequency
        self.femb.write_reg_bits(self.REG_TP, 8, 0xFF, 0x00)  #test pulse delay

        #phase control
        if self.isRoomTemp == True:
            print("ADC clock phase:", self.CLKSELECT_val_RT,
                  self.CLKSELECT2_val_RT)
            self.femb.write_reg_bits(self.CLK_SELECT, 0, 0xFF,
                                     self.CLKSELECT_val_RT)  #clock select
            self.femb.write_reg_bits(self.CLK_SELECT2, 0, 0xFF,
                                     self.CLKSELECT2_val_RT)  #clock select 2
        else:
            print("Using cryogenic parameters, ADC clock phase:",
                  self.CLKSELECT_val_CT, self.CLKSELECT2_val_CT)
            self.femb.write_reg_bits(self.CLK_SELECT, 0, 0xFF,
                                     self.CLKSELECT_val_CT)  #clock select
            self.femb.write_reg_bits(self.CLK_SELECT2, 0, 0xFF,
                                     self.CLKSELECT2_val_CT)  #clock select 2
        self.femb.write_reg_bits(self.REG_LATCHLOC_3_TO_0, 0, 0xFFFFFFFF,
                                 self.REG_LATCHLOC_3_TO_0_val)  #datashift
        self.femb.write_reg_bits(self.REG_LATCHLOC_7_TO_4, 0, 0xFFFFFFFF,
                                 self.REG_LATCHLOC_7_TO_4_val)  #datashift

        #enable streaming
        self.femb.write_reg_bits(self.REG_HS_DATA, 0, 0x1,
                                 1)  #Enable streaming
        self.femb.write_reg_bits(self.REG_HS_DATA, 3, 0x1, 1)  #Enable ADC data

        #EXTERNAL CLOCK STUFF
        self.ext_clk_config_femb()

        #Set FE ASIC SPI configuration registers
        self.configFeAsic()

        #check ASIC SPI
        self.checkFembSpi()
        print("SPI STATUS", "\t", self.spiStatus)

        #check ADC SYNC
        self.checkSync()
        print("SYNC STATUS", "\t", self.syncStatus)

    #Test FEMB SPI working
    def checkFembSpi(self):
        print("Check ASIC SPI")

        self.spiStatus = 0
        for regNum in range(0, 72, 1):
            progVal = self.femb.read_reg(self.REG_SPI_BASE + regNum)
            if progVal == None:
                print("Error - FEMB register interface is not working.")
                return
            rdbckVal = self.femb.read_reg(self.REG_SPI_RDBACK_BASE + regNum)
            if rdbckVal == None:
                print("Error - FEMB register interface is not working.")
                return
            print(hex(progVal), "\t", hex(rdbckVal))
            if progVal != rdbckVal:
                print("SPI readback failed.")
                self.spiStatus = 1
                return

    def checkSync(self):
        print("Check ASIC SYNC")
        regVal = self.femb.read_reg(6)
        if regVal == None:
            print("doAsicConfigcheckFembSpi: Could not check SYNC status, bad")
            return

        syncVal = 0
        syncVal = ((regVal >> 16) & 0xFFFF)
        self.syncStatus = syncVal

    #FEMB power enable on WIB
    def powerOnFemb(self, femb):
        fembVal = int(femb)
        if (fembVal < 0) or (fembVal > 3):
            return

        #set UDP ports to WIB registers
        self.femb.UDP_PORT_WREG = 32000
        self.femb.UDP_PORT_RREG = 32001
        self.femb.UDP_PORT_RREGRESP = 32002

        regBase = int(fembVal * 4)

        #always enable extra DC converter
        self.femb.write_reg(8, 0x100000)

        #FEMB power enable
        self.femb.write_reg_bits(8, regBase + 0, 0x1, 1)  #3.6V
        self.femb.write_reg_bits(8, regBase + 1, 0x1, 1)  #2.8V
        self.femb.write_reg_bits(8, regBase + 2, 0x1, 1)  #2.5V
        self.femb.write_reg_bits(8, regBase + 3, 0x1, 1)  #1.5V
        self.femb.write_reg_bits(8, 16 + fembVal, 0x1, 1)  #BIAS enable

        regVal = self.femb.read_reg(8)
        if regVal == None:
            return
        print("FEMB Power on: ", hex(regVal))

        #set UDP ports back to normal
        self.selectFemb(self.fembNum)

    def powerOffFemb(self, femb):
        fembVal = int(femb)
        if (fembVal < 0) or (fembVal > 3):
            return

        #set UDP ports to WIB registers
        self.femb.UDP_PORT_WREG = 32000
        self.femb.UDP_PORT_RREG = 32001
        self.femb.UDP_PORT_RREGRESP = 32002

        regBase = int(fembVal * 4)

        #FEMB power disable
        self.femb.write_reg_bits(8, 16 + fembVal, 0x1, 0)  #BIAS
        self.femb.write_reg_bits(8, regBase + 0, 0x1, 0)  #3.6V
        self.femb.write_reg_bits(8, regBase + 1, 0x1, 0)  #2.8V
        self.femb.write_reg_bits(8, regBase + 2, 0x1, 0)  #2.5V
        self.femb.write_reg_bits(8, regBase + 3, 0x1, 0)  #1.5V

        regVal = self.femb.read_reg(8)
        if regVal == None:
            return
        print("FEMB Power off: ", hex(regVal))

        #set UDP ports back to normal
        self.selectFemb(self.fembNum)

    def selectChannel(self, asic, chan):
        #print("Select channel")
        asicVal = int(asic)
        if (asicVal < 0) or (asicVal > self.NASICS):
            return

        #set UDP ports to WIB
        self.femb.UDP_PORT_WREG = 32000
        self.femb.UDP_PORT_RREG = 32001
        self.femb.UDP_PORT_RREGRESP = 32002

        #select ASIC
        #print("Selecting ASIC " + str(asicVal) )
        self.femb.write_reg_bits(self.REG_SEL_ASIC, self.REG_SEL_ASIC_LSB, 0xF,
                                 asicVal)

        #Note: WIB data format streams all 16 channels, don't need to select specific channel

        #set UDP ports back to normal
        self.selectFemb(self.fembNum)

    def configFeAsic(self):
        print("CONFIG ASICs")

        #global config varibles
        feasicLeakageVal = int(self.feasicLeakage)  #0 = 500pA, 1 = 100pA
        feasicLeakagex10Val = int(self.feasicLeakagex10)  #0 = x1, 1 = x10
        acdcVal = int(self.feasicAcdc)  #DC = 0, AC = 1

        #channel specific variables
        testVal = int(self.feasicEnableTestInput)
        baseVal = int(self.feasicBaseline)  #0 = 900mV, 1 = 200mV
        gainVal = int(self.feasicGain)
        shapeVal = int(self.feasicShape)
        bufVal = int(self.feasicBuf)  #0 = OFF, 1 = ON

        if (testVal < 0) or (testVal > 1):
            return
        if (baseVal < 0) or (baseVal > 1):
            return
        if (gainVal < 0) or (gainVal > 3):
            return
        if (shapeVal < 0) or (shapeVal > 3):
            return
        if (acdcVal < 0) or (acdcVal > 1):
            return
        if (bufVal < 0) or (bufVal > 1):
            return
        if (feasicLeakageVal < 0) or (feasicLeakageVal > 1):
            return
        if (feasicLeakagex10Val < 0) or (feasicLeakagex10Val > 1):
            return

        chReg = 0
        #test capacitor, bit 7
        chReg = chReg + ((testVal & 0x01) << 7)

        #baseline control, bit 6
        baseVal = 1 - baseVal  #assign 0 = 200mV, 1 = 900mV
        chReg = chReg + ((baseVal & 0x01) << 6)

        #gain control, bits 4-5
        gainArray = [0, 2, 1, 3]
        chReg = chReg + ((gainArray[gainVal] & 0x03) << 4)

        #shape control, bits 2-3
        shapeArray = [2, 0, 3, 1]  #I don't know why
        chReg = chReg + ((shapeArray[shapeVal] & 0x03) << 2)

        #buffer control, bit 0
        chReg = chReg + ((bufVal & 0x01) << 0)

        #construct the channel word
        chWord = (chReg << 24) + (chReg << 16) + (chReg << 8) + chReg

        asicReg = int(0)
        #asicReg = int(0x0A00)

        #leakage control 1, bit 0
        asicReg = asicReg + ((feasicLeakageVal & 0x01) << 0)

        #leakage control 2, bit 4
        asicReg = asicReg + ((feasicLeakagex10Val & 0x01) << 4)

        #AC/DC control

        #monitor control, bits 1-2

        #internal DAC enable, bit 8

        #external DAC enable, bit 9

        #DAC OUTPUT bits 8-9 , 0xA00 = external DAC

        #ADC ASIC config
        adc_globalReg = 0x0000  #FRQC=1, all other general register bits are 0
        if self.useExtAdcClock == True:
            adc_globalReg = 0x8000  #CLK0=1,CLK1=0,FRQC=0,F0=0

        #turn off HS data before register writes
        self.femb.write_reg_bits(9, 0, 0x1, 0)
        print("HS link turned off")
        time.sleep(2)

        #write SPI regs - very rough version
        chWord = (chReg << 24) + (chReg << 16) + (chReg << 8) + chReg
        for asic in range(0, self.NASICS, 1):
            baseReg = self.REG_SPI_BASE + int(asic) * 9
            self.femb.write_reg_bits(baseReg + 4, 0, 0xFFFF,
                                     adc_globalReg)  #ADC ASIC global registers
            self.femb.write_reg_bits(baseReg + 4, 16, 0xFF, chReg)  #ch0
            self.femb.write_reg_bits(baseReg + 4, 24, 0xFF, chReg)  #ch1
            self.femb.write_reg(baseReg + 5, chWord)  #ch2-5
            self.femb.write_reg(baseReg + 6, chWord)  #ch6-9
            self.femb.write_reg(baseReg + 7, chWord)  #ch10-13
            self.femb.write_reg_bits(baseReg + 8, 0, 0xFF, chReg)  #ch14
            self.femb.write_reg_bits(baseReg + 8, 8, 0xFF, chReg)  #ch15
            self.femb.write_reg_bits(baseReg + 8, 16, 0xFFFF,
                                     asicReg)  #ASIC gen reg

        print("adc_globalReg ", "\t", adc_globalReg)
        print("chReg ", "\t", hex(chReg))
        print("chWord ", "\t", hex(chWord))
        print("asicReg ", "\t", hex(asicReg))

        # find a good phase, if necessary
        self.findADCPhase()

        #run the SPI programming
        self.doAsicConfig()

        #turn HS link back on
        print("HS link turned back on")
        time.sleep(2)
        self.femb.write_reg_bits(9, 0, 0x1, 1)

    def findADCPhase(self, trial=0):

        print("Find ADC phases that sync all ADCs")

        #Write ADC ASIC SPI
        if True:
            print("ADC reconfig")
            self.femb.write_reg(self.REG_RESET, 0x4)  #reset timestamp
            time.sleep(0.01)
            self.femb.write_reg(self.REG_ASIC_RESET, 1)  #reset ASIC SPI
            time.sleep(0.01)
            self.femb.write_reg(self.REG_ASIC_SPIPROG, 1)  #configure ASICs
            time.sleep(0.01)
            self.femb.write_reg(self.REG_ASIC_SPIPROG, 1)  #configure ASICs
            time.sleep(0.01)

        syncSuccess = False
        oldSyncVal = 0xFFFF

        # start with the default values for the configuration
        def_clksel_rt = self.CLKSELECT_val_RT
        def_clksel2_rt = self.CLKSELECT2_val_RT

        def_clksel_ct = self.CLKSELECT_val_CT
        def_clksel2_ct = self.CLKSELECT2_val_CT

        #first step will always go +1
        lastStep = 1

        didJump = False

        while (syncSuccess == False):

            # give up after 50 trials
            if trial == 50:
                print("Could not find good clock phase, SYNC STATUS:",
                      hex(syncVal))
                return

            #phase control
            if self.isRoomTemp == True:
                print("ADC clock phase:", self.CLKSELECT_val_RT,
                      self.CLKSELECT2_val_RT)
                self.femb.write_reg_bits(self.CLK_SELECT, 0, 0xFF,
                                         self.CLKSELECT_val_RT)  #clock select
                self.femb.write_reg_bits(
                    self.CLK_SELECT2, 0, 0xFF,
                    self.CLKSELECT2_val_RT)  #clock select 2
            else:
                print("Using cryogenic parameters, ADC clock phase:",
                      self.CLKSELECT_val_CT, self.CLKSELECT2_val_CT)
                self.femb.write_reg_bits(self.CLK_SELECT, 0, 0xFF,
                                         self.CLKSELECT_val_CT)  #clock select
                self.femb.write_reg_bits(
                    self.CLK_SELECT2, 0, 0xFF,
                    self.CLKSELECT2_val_CT)  #clock select 2

            # check sync
            regVal = self.femb.read_reg(6)
            if regVal == None:
                print("doAsicConfig: Could not check SYNC status, bad")
                return

            syncVal = 0
            syncVal = ((regVal >> 16) & 0xFFFF)
            self.syncStatus = syncVal
            print("SYNC ATTEMPT\t", trial, "\tSYNC VAL ", hex(syncVal))

            #try again if sync not achieved
            if syncVal != 0x0:

                if syncVal <= oldSyncVal:

                    # keep going this direction
                    if lastStep == 1:

                        if self.isRoomTemp == True:
                            if self.CLKSELECT_val_RT < 0xFF:
                                self.CLKSELECT_val_RT = self.CLKSELECT_val_RT + 1

                            if self.CLKSELECT2_val_RT < 0xFF:
                                self.CLKSELECT2_val_RT = self.CLKSELECT2_val_RT + 1

                        else:
                            if self.CLKSELECT_val_CT < 0xFF:
                                self.CLKSELECT_val_CT = self.CLKSELECT_val_CT + 1

                            if self.CLKSELECT2_val_CT < 0xFF:
                                self.CLKSELECT2_val_CT = self.CLKSELECT2_val_CT + 1

                        lastStep = 1

                    else:

                        if self.isRoomTemp == True:
                            if self.CLKSELECT_val_RT < 0xFF:
                                self.CLKSELECT_val_RT = self.CLKSELECT_val_RT - 1

                            if self.CLKSELECT2_val_RT < 0xFF:
                                self.CLKSELECT2_val_RT = self.CLKSELECT2_val_RT - 1

                        else:
                            if self.CLKSELECT_val_CT < 0xFF:
                                self.CLKSELECT_val_CT = self.CLKSELECT_val_CT - 1

                            if self.CLKSELECT2_val_CT < 0xFF:
                                self.CLKSELECT2_val_CT = self.CLKSELECT2_val_CT - 1

                        lastStep = -1

                    oldSyncVal = syncVal

                else:

                    # already jumped once
                    if didJump == True:
                        print("Could not find good clock phase, SYNC STATUS:",
                              hex(syncVal))
                        return

                    # jump back to start and switch directions
                    if self.isRoomTemp == True:
                        if self.CLKSELECT_val_RT < 0xFF:
                            self.CLKSELECT_val_RT = def_clksel_rt - 1

                        if self.CLKSELECT2_val_RT < 0xFF:
                            self.CLKSELECT2_val_RT = def_clksel2_rt - 1

                    else:
                        if self.CLKSELECT_val_CT < 0xFF:
                            self.CLKSELECT_val_CT = def_clksel_ct - 1

                        if self.CLKSELECT2_val_CT < 0xFF:
                            self.CLKSELECT2_val_CT = def_clksel2_ct - 1

                    lastStep = -1
                    didJump = True
                    oldSyncVal = 0xFFFF

                syncSuccess = False

            else:
                syncSuccess = True

                if self.isRoomTemp == True:
                    print("Found good RT clock phase:",
                          hex(self.CLKSELECT_val_RT),
                          hex(self.CLKSELECT2_val_RT))
                else:
                    print("Found good CT clock phase:",
                          hex(self.CLKSELECT_val_CT),
                          hex(self.CLKSELECT2_val_CT))

            trial = trial + 1

    def doAsicConfig(self, syncAttempt=0):
        if syncAttempt == 0:
            print("Program ASIC SPI")
        #for regNum in range(self.REG_SPI_BASE,self.REG_SPI_BASE+72,1):
        #    regVal = self.femb.read_reg( regNum)
        #    print( str(regNum) + "\t" + str(hex(regVal)) )

        #phase control
        """
        if self.isRoomTemp == True:
            self.femb.write_reg_bits(self.CLK_SELECT , 0, 0xFF, 0xDF ) #clock select
            self.femb.write_reg_bits(self.CLK_SELECT2 , 0, 0xFF, 0x20 ) #clock select 2
        else:
            print("Using cryogenic parameters")
            self.femb.write_reg_bits(self.CLK_SELECT , 0, 0xFF, 0x83 ) #clock select
            self.femb.write_reg_bits(self.CLK_SELECT2 , 0, 0xFF, 0xFF ) #clock select 2            
        self.femb.write_reg_bits(self.REG_LATCHLOC_3_TO_0 , 0, 0xFFFFFFFF, 0x00000000 ) #datashift
        self.femb.write_reg_bits(self.REG_LATCHLOC_7_TO_4 , 0, 0xFFFFFFFF, 0x00000000 ) #datashift
        """

        #Write ADC ASIC SPI
        #if syncAttempt == 0:
        if True:
            #print("ADC reconfig")
            self.femb.write_reg(self.REG_RESET, 0x4)  #reset timestamp
            time.sleep(0.01)
            self.femb.write_reg(self.REG_ASIC_RESET, 1)  #reset ASIC SPI
            time.sleep(0.01)
            self.femb.write_reg(self.REG_ASIC_SPIPROG, 1)  #configure ASICs
            time.sleep(0.01)
            self.femb.write_reg(self.REG_ASIC_SPIPROG, 1)  #configure ASICs
            time.sleep(0.01)
        #soft reset
        #self.femb.write_reg( self.REG_SOFT_ADC_RESET, 0x4)
        #time.sleep(0.01)

        #for regNum in range(self.REG_SPI_RDBACK_BASE,self.REG_SPI_RDBACK_BASE+72,1):
        #    regVal = self.femb.read_reg( regNum)
        #    print( str(regNum) + "\t" + str(hex(regVal)) )

        #check the sync
        if self.doReSync == False:
            return

        regVal = self.femb.read_reg(6)
        if regVal == None:
            print("doAsicConfig: Could not check SYNC status, bad")
            return

        syncVal = 0
        syncVal = ((regVal >> 16) & 0xFFFF)
        self.syncStatus = syncVal
        #print("SYNC ATTEMPT\t",syncAttempt,"\tSYNC VAL " , hex(syncVal) )

        #try again if sync not achieved, note recursion
        if syncVal != 0x0:
            if syncAttempt >= self.maxSyncAttempts:
                print("doAsicConfig: Could not sync ADC ASIC, sync val\t",
                      hex(syncVal))
                return
            else:
                self.doAsicConfig(syncAttempt + 1)

    def syncADC(self):
        print("Sync")

    def selectFemb(self, fembIn):
        fembVal = int(fembIn)
        if (fembVal < 0) or (fembVal > self.NFEMBS):
            print("Invalid FEMB # requested")
            return
        self.fembNum = fembVal

        #set data streaming for requested FEMB
        #set UDP ports to WIB
        self.femb.UDP_PORT_WREG = 32000
        self.femb.UDP_PORT_RREG = 32001
        self.femb.UDP_PORT_RREGRESP = 32002
        self.femb.write_reg_bits(7, 16, 0x3, self.fembNum)

        #set read/write ports
        if fembVal == 0:
            self.femb.UDP_PORT_WREG = 32016
            self.femb.UDP_PORT_RREG = 32017
            self.femb.UDP_PORT_RREGRESP = 32018

        if fembVal == 1:
            self.femb.UDP_PORT_WREG = 32032
            self.femb.UDP_PORT_RREG = 32033
            self.femb.UDP_PORT_RREGRESP = 32034

        if fembVal == 2:
            self.femb.UDP_PORT_WREG = 32048
            self.femb.UDP_PORT_RREG = 32049
            self.femb.UDP_PORT_RREGRESP = 32050

        if fembVal == 3:
            self.femb.UDP_PORT_WREG = 32064
            self.femb.UDP_PORT_RREG = 32065
            self.femb.UDP_PORT_RREGRESP = 32066

        #slow down register interface for FEMBs
        self.femb.REG_SLEEP = 0.05
        time.sleep(0.1)

    def initSI5338(self):
        #set UDP ports to WIB
        self.femb.UDP_PORT_WREG = 32000
        self.femb.UDP_PORT_RREG = 32001
        self.femb.UDP_PORT_RREGRESP = 32002
        self.femb.REG_SLEEP = 0.001

        #disable all outputs
        #i2c_reg_wr(i2c_bus_base_addr, si5338_i2c_addr, 230, 0x10);
        self.write_reg_SI5338(230, 0x10)

        #pause lol
        #i2c_reg_wr(i2c_bus_base_addr, si5338_i2c_addr, 241, 0xE5);
        self.write_reg_SI5338(241, 0xE5)

        import femb_python.configuration.femb_config_wib_sbnd_si5338_data
        for word in range(0, 349, 1):
            wordNum = int(word)
            addr = int(femb_python.configuration.
                       femb_config_wib_sbnd_si5338_data.data[3 * wordNum + 0])
            val = int(femb_python.configuration.
                      femb_config_wib_sbnd_si5338_data.data[3 * wordNum + 1])
            mask = int(femb_python.configuration.
                       femb_config_wib_sbnd_si5338_data.data[3 * wordNum + 2])

            if wordNum % 10 == 0:
                print("Writing SI5338 register # " + str(wordNum) +
                      " out of 349")

            if mask == 0:
                continue

            writeVal = val
            if mask != 0xFF:
                curr_val = self.read_reg_SI5338(addr)
                if curr_val == None:
                    print("Did not finish clock initialization")
                    return
                clear_curr_val = curr_val & (~mask)
                clear_new_val = val & mask
                writeVal = clear_curr_val | clear_new_val
            self.write_reg_SI5338(addr, writeVal)
            #print(str(addr) + "\t" + str(writeVal))

        #validate input clock status
#i2c_reg_rd(i2c_bus_base_addr, si5338_i2c_addr, 218);
        regVal = self.read_reg_SI5338(218)
        if regVal == None:
            print("Did not finish clock initialization")
            return
        clkStatus = (regVal & 0x04)
        count = 0
        while count < 100:
            regVal = self.read_reg_SI5338(218)
            if regVal == None:
                print("Did not finish clock initialization")
                return
            clkStatus = (regVal & 0x04)
            if clkStatus != 0x04:
                break
            count = count + 1
        if clkStatus == 0x04:
            print("Did not finish clock initialization")
            return

        #configure pll
        pllWord = int(
            femb_python.configuration.femb_config_wib_sbnd_si5338_data.data[
                3 * 49 + 1])
        self.write_reg_SI5338(49, (0x7F & pllWord))

        #reset the chip
        self.write_reg_SI5338(246, 0x02)

        time.sleep(0.1)

        #restart lol
        self.write_reg_SI5338(241, 0x65)

        #validate pll
        pllStatus = self.read_reg_SI5338(218)
        if pllStatus == None:
            print("Did not finish clock initialization")
            return
        count = 0
        while count < 100:
            pllStatus = self.read_reg_SI5338(218)
            if pllStatus == None:
                print("Did not finish clock initialization")
                return
            if pllStatus == 0:
                break
            count = count + 1
        if pllStatus != 0:
            print("Did not finish clock initialization")
            return

        #copy FCAL values to active registers
        fcalVal = self.read_reg_SI5338(235)
        if fcalVal == None:
            print("Did not finish clock initialization")
            return
        self.write_reg_SI5338(45, fcalVal)

        fcalVal = self.read_reg_SI5338(236)
        if fcalVal == None:
            print("Did not finish clock initialization")
            return
        self.write_reg_SI5338(46, fcalVal)

        fcalVal = self.read_reg_SI5338(237)
        if fcalVal == None:
            print("Did not finish clock initialization")
            return
        fcalVal = (0x14 | (fcalVal & 0x3))
        self.write_reg_SI5338(47, fcalVal)

        #set pll to use FCAL values
        #i2c_reg_wr(i2c_bus_base_addr, si5338_i2c_addr, 49, 0x80|SI5338Reg[49*3+1]);
        setPllWord = (0x80 | pllWord)
        self.write_reg_SI5338(49, setPllWord)

        #enable outputs
        self.write_reg_SI5338(230, 0x00)
        print("Done initalizing Si5338 clock")

        #set UDP ports back to normal
        self.selectFemb(self.fembNum)

    def read_reg_SI5338(self, addr):
        addrVal = int(addr)
        if (addrVal < 0) or (addrVal > 255):
            return None
        self.femb.write_reg(11, 0)
        self.femb.write_reg(12, addrVal)
        self.femb.write_reg(15, 0xE0)

        self.femb.write_reg(10, 1)
        self.femb.write_reg(10, 0)

        self.femb.write_reg(11, 1)

        self.femb.write_reg(10, 2)
        self.femb.write_reg(10, 0)

        regVal = self.femb.read_reg(14)
        if regVal == None:
            return None
        return regVal

    def write_reg_SI5338(self, addr, val):
        addrVal = int(addr)
        if (addrVal < 0) or (addrVal > 255):
            return
        regVal = int(val)
        if (regVal < 0) or (regVal > 255):
            return
        self.femb.write_reg(11, 1)
        self.femb.write_reg(12, addrVal)
        self.femb.write_reg(13, regVal)

        self.femb.write_reg(10, 1)
        self.femb.write_reg(10, 0)

    def setFpgaPulser(self, enable, dac):
        enableVal = int(enable)
        if (enableVal < 0) or (enableVal > 1):
            print("femb_config_femb : setFpgaPulser - invalid enable value")
            return
        dacVal = int(dac)
        if (dacVal < 0) or (dacVal > 0x3F):
            print("femb_config_femb : setFpgaPulser - invalid dac value")
            return

        self.femb.write_reg_bits(self.REG_FPGA_TP_EN, 0, 0x3,
                                 enableVal)  #test pulse enable
        self.femb.write_reg_bits(self.REG_FPGA_TP_EN, 8, 0x1,
                                 enableVal)  #test pulse enable
        self.femb.write_reg_bits(self.REG_TP, 0, 0x3F, dacVal)  #TP Amplitude
        self.femb.write_reg_bits(self.REG_TP, 8, 0xFF, 219)  #DLY
        self.femb.write_reg_bits(self.REG_TP, 16, 0xFFFF, 497)  #FREQ

        #set pulser enable bit
        if enableVal == 1:
            self.femb.write_reg(self.EXT_TP_EN,
                                0x2)  #this register is confusing, check
        else:
            self.femb.write_reg(self.EXT_TP_EN, 0x3)  #pulser disabled

        #connect channel test input to external pin
        for asic in range(0, self.NASICS, 1):
            baseReg = self.REG_SPI_BASE + int(asic) * 9
            if enableVal == 1:
                self.femb.write_reg_bits(baseReg + 8, 24, 0x3,
                                         0x2)  #ASIC gen reg
            else:
                self.femb.write_reg_bits(baseReg + 8, 24, 0x3,
                                         0x0)  #ASIC gen reg

        self.doAsicConfig()

    def setInternalPulser(self, enable, dac):
        enableVal = int(enable)
        if (enableVal < 0) or (enableVal > 1):
            print(
                "femb_config_femb : setInternalPulser - invalid enable value")
            return
        dacVal = int(dac)
        if (dacVal < 0) or (dacVal > 0x3F):
            print("femb_config_femb : setInternalPulser - invalid dac value")
            return

        self.femb.write_reg_bits(self.REG_DAC_SELECT, 8, 0x1,
                                 0)  #test pulse enable
        self.femb.write_reg_bits(self.REG_TP, 0, 0x3F, 0)  #TP Amplitude
        self.femb.write_reg_bits(self.REG_TP, 8, 0xFF, 219)  #DLY
        self.femb.write_reg_bits(self.REG_TP, 16, 0xFFFF, 497)  #FREQ

        #set pulser enable bit
        if enableVal == 1:
            self.femb.write_reg(self.INT_TP_EN,
                                0x2)  #this register is confusing, check
        else:
            self.femb.write_reg(self.INT_TP_EN, 0x3)  #pulser disabled

        dacVal = (dacVal & 0x3F)
        newDacVal = int('{:08b}'.format(dacVal)[::-1], 2)

        asicWord = ((newDacVal << 8) & 0xFFFF)
        if enableVal == 1:
            asicWord = asicWord + (0x1 << 8)

        #connect channel test input to external pin
        for asic in range(0, self.NASICS, 1):
            baseReg = self.REG_SPI_BASE + int(asic) * 9
            if enableVal == 1:
                self.femb.write_reg_bits(baseReg + 8, 24, 0xFF, newDacVal)
                self.femb.write_reg_bits(baseReg + 8, 24, 0x3,
                                         0x1)  #ASIC gen reg
            else:
                self.femb.write_reg_bits(baseReg + 8, 24, 0xFF,
                                         0x0)  #ASIC gen reg

        self.doAsicConfig()

        if enableVal == 1:
            self.femb.write_reg_bits(
                self.REG_ASIC_TP_EN, 0, 0x3,
                0x2)  #NOTE, also disabling FPGA pulser here
        else:
            self.femb.write_reg_bits(self.REG_ASIC_TP_EN, 0, 0x3, 0x0)

    def selectPulserChannels(self, setchannels):
        # attach test cap to a set of channels given by the array testchannels
        testchannels = []

        # check channel list
        for i in range(0, len(setchannels), 1):
            if (setchannels[i] >= 0 and setchannels[i] <= 127):
                testchannels.append(setchannels[i])
            else:
                print("Invalid channel:", setchannels[i], "removed from list")

        print("Selecting channels for pulser:", testchannels)

        ch = [[-1 for i in range(self.NASICCH)] for j in range(self.NASICS)]

        for asic in range(0, self.NASICS, 1):
            baseReg = self.REG_SPI_BASE + int(asic) * 9

            # read back current state of channel regs
            ch[asic][15] = (self.femb.read_reg(baseReg + 4) & 0xFF0000) >> 16
            ch[asic][14] = (self.femb.read_reg(baseReg + 4) & 0xFF000000) >> 24
            ch[asic][13] = (self.femb.read_reg(baseReg + 5) & 0xFF)
            ch[asic][12] = (self.femb.read_reg(baseReg + 5) & 0xFF00) >> 8
            ch[asic][11] = (self.femb.read_reg(baseReg + 5) & 0xFF0000) >> 16
            ch[asic][10] = (self.femb.read_reg(baseReg + 5) & 0xFF000000) >> 24
            ch[asic][9] = (self.femb.read_reg(baseReg + 6) & 0xFF)
            ch[asic][8] = (self.femb.read_reg(baseReg + 6) & 0xFF00) >> 8
            ch[asic][7] = (self.femb.read_reg(baseReg + 6) & 0xFF0000) >> 16
            ch[asic][6] = (self.femb.read_reg(baseReg + 6) & 0xFF000000) >> 24
            ch[asic][5] = (self.femb.read_reg(baseReg + 7) & 0xFF)
            ch[asic][4] = (self.femb.read_reg(baseReg + 7) & 0xFF00) >> 8
            ch[asic][3] = (self.femb.read_reg(baseReg + 7) & 0xFF0000) >> 16
            ch[asic][2] = (self.femb.read_reg(baseReg + 7) & 0xFF000000) >> 24
            ch[asic][1] = (self.femb.read_reg(baseReg + 8) & 0xFF)
            ch[asic][0] = (self.femb.read_reg(baseReg + 8) & 0xFF00) >> 8

            # 0 test cap all channels
            for i in range(0, self.NASICCH, 1):
                ch[asic][i] = ch[asic][i] & 0x7F

        # 1 test cap if channel is in list
        for tc in range(0, len(testchannels), 1):
            thisasic = int(testchannels[tc] / self.NASICCH)
            thischan = int(
                (testchannels[tc] / self.NASICCH - thisasic) * self.NASICCH)

            ch[thisasic][thischan] = ch[thisasic][thischan] + 0x80

        #write channel regs
        for asic in range(0, self.NASICS, 1):
            baseReg = self.REG_SPI_BASE + int(asic) * 9
            self.femb.write_reg_bits(baseReg + 4, 16, 0xFF, ch[asic][15])
            self.femb.write_reg_bits(baseReg + 4, 24, 0xFF, ch[asic][14])
            self.femb.write_reg_bits(baseReg + 5, 0, 0xFF, ch[asic][13])
            self.femb.write_reg_bits(baseReg + 5, 8, 0xFF, ch[asic][12])
            self.femb.write_reg_bits(baseReg + 5, 16, 0xFF, ch[asic][11])
            self.femb.write_reg_bits(baseReg + 5, 24, 0xFF, ch[asic][10])
            self.femb.write_reg_bits(baseReg + 6, 0, 0xFF, ch[asic][9])
            self.femb.write_reg_bits(baseReg + 6, 8, 0xFF, ch[asic][8])
            self.femb.write_reg_bits(baseReg + 6, 16, 0xFF, ch[asic][7])
            self.femb.write_reg_bits(baseReg + 6, 24, 0xFF, ch[asic][6])
            self.femb.write_reg_bits(baseReg + 7, 0, 0xFF, ch[asic][5])
            self.femb.write_reg_bits(baseReg + 7, 8, 0xFF, ch[asic][4])
            self.femb.write_reg_bits(baseReg + 7, 16, 0xFF, ch[asic][3])
            self.femb.write_reg_bits(baseReg + 7, 24, 0xFF, ch[asic][2])
            self.femb.write_reg_bits(baseReg + 8, 0, 0xFF, ch[asic][1])
            self.femb.write_reg_bits(baseReg + 8, 8, 0xFF, ch[asic][0])

        self.doAsicConfig()

    def checkFirmwareVersion(self):
        #set UDP ports to WIB
        self.femb.UDP_PORT_WREG = 32000
        self.femb.UDP_PORT_RREG = 32001
        self.femb.UDP_PORT_RREGRESP = 32002

        #check WIB fw version reg
        wibVerReg = self.femb.read_reg(255)
        if wibVerReg == None:
            return False
        wibVerReg = (wibVerReg & 0xFFF)

        #set UDP ports back to normal
        self.selectFemb(self.fembNum)
        fembVerReg = self.femb.read_reg(257)
        if fembVerReg == None:
            return False
        fembVerReg = (fembVerReg & 0xFFF)
        #print( "FEMB Firmware Version HERE : " + str(hex(fembVerReg)) )
        if wibVerReg != 0x116:
            print("Invalid WIB firmware version detected " +
                  str(hex(wibVerReg)) +
                  ", this configuration requires version 0x116")
            return False
        if fembVerReg != 0x323:
            print("Invalid FEMB firmware version detected " +
                  str(hex(fembVerReg)) +
                  ", this configuration requires version 0x323")
            return False

        print("WIB Firmware Version : " + str(hex(wibVerReg)))
        print("FEMB Firmware Version : " + str(hex(fembVerReg)))

        #good firmware id
        return True

    def readCurrent(self):

        self.femb.UDP_PORT_WREG = 32000  #WIB PORTS
        self.femb.UDP_PORT_RREG = 32001
        self.femb.UDP_PORT_RREGRESP = 32002

        for j in range(0, 100):
            self.femb.write_reg(5, 0)
            self.femb.write_reg(5, 0x10000)
            self.femb.write_reg(5, 0)
            time.sleep(0.01)

        results = []
        for pwrSel in range(1, 25):
            self.femb.write_reg(5, pwrSel)
            time.sleep(0.1)
            regVal = self.femb.read_reg(6)
            if regVal == None:
                results.append(0)
                continue
                #return None
            val = regVal & 0xFFFFFFFF
            results.append(val)

        self.selectFemb(0)
        return results

    def ext_clk_config_femb(self):
        #EXTERNAL CLOCK VARIABLES
        ####################external clokc timing
        clk_period = 5  #ns
        self.clk_dis = 0  #0 --> enable, 1 disable
        self.d14_rst_oft = 0 // clk_period
        self.d14_rst_wdt = (45 // clk_period)
        self.d14_rst_inv = 1
        self.d14_read_oft = 480 // clk_period
        self.d14_read_wdt = 20 // clk_period
        self.d14_read_inv = 1
        self.d14_idxm_oft = 230 // clk_period
        self.d14_idxm_wdt = 270 // clk_period
        self.d14_idxm_inv = 0
        self.d14_idxl_oft = 480 // clk_period
        self.d14_idxl_wdt = 20 // clk_period
        self.d14_idxl_inv = 0
        self.d14_idl0_oft = 50 // clk_period
        self.d14_idl0_wdt = (190 // clk_period) - 1
        self.d14_idl1_oft = 480 // clk_period
        self.d14_idl1_wdt = 20 // clk_period
        self.d14_idl_inv = 0

        self.d58_rst_oft = 0 // clk_period
        self.d58_rst_wdt = (45 // clk_period)
        self.d58_rst_inv = 1
        self.d58_read_oft = 480 // clk_period
        self.d58_read_wdt = 20 // clk_period
        self.d58_read_inv = 1
        self.d58_idxm_oft = 230 // clk_period
        self.d58_idxm_wdt = 270 // clk_period
        self.d58_idxm_inv = 0
        self.d58_idxl_oft = 480 // clk_period
        self.d58_idxl_wdt = 20 // clk_period
        self.d58_idxl_inv = 0
        self.d58_idl0_oft = 50 // clk_period
        self.d58_idl0_wdt = (190 // clk_period) - 1
        self.d58_idl1_oft = 480 // clk_period
        self.d58_idl1_wdt = 20 // clk_period
        self.d58_idl_inv = 0
        ####################external clock phase for V323 firmware
        self.d14_read_step = 11
        self.d14_read_ud = 0
        self.d14_idxm_step = 9
        self.d14_idxm_ud = 0
        self.d14_idxl_step = 7
        self.d14_idxl_ud = 0
        self.d14_idl0_step = 12
        self.d14_idl0_ud = 0
        self.d14_idl1_step = 10
        self.d14_idl1_ud = 0
        self.d14_phase_en = 1

        self.d58_read_step = 0
        self.d58_read_ud = 0
        self.d58_idxm_step = 5
        self.d58_idxm_ud = 0
        self.d58_idxl_step = 4
        self.d58_idxl_ud = 1
        self.d58_idl0_step = 3
        self.d58_idl0_ud = 0
        self.d58_idl1_step = 4
        self.d58_idl1_ud = 0
        self.d58_phase_en = 1

        #END EXTERNAL CLOCK VARIABLES

        #config timing
        d14_inv = (self.d14_rst_inv << 0) + (self.d14_read_inv << 1) + (
            self.d14_idxm_inv << 2) + (self.d14_idxl_inv << 3) + (
                self.d14_idl_inv << 4)
        d58_inv = (self.d58_rst_inv << 0) + (self.d58_read_inv << 1) + (
            self.d58_idxm_inv << 2) + (self.d58_idxl_inv << 3) + (
                self.d58_idl_inv << 4)
        d_inv = d58_inv + (d14_inv << 5)

        addr_data = self.clk_dis + (d_inv << 16)
        #self.ext_clk_reg_wr_femb( femb_addr, 21, addr_data)
        self.femb.write_reg(21, addr_data)

        addr_data = self.d58_rst_oft + (self.d14_rst_oft << 16)
        #self.ext_clk_reg_wr_femb( femb_addr, 22, addr_data)
        self.femb.write_reg(22, addr_data)

        addr_data = self.d58_rst_wdt + (self.d14_rst_wdt << 16)
        #self.ext_clk_reg_wr_femb( femb_addr, 23, addr_data)
        self.femb.write_reg(23, addr_data)

        addr_data = self.d58_read_oft + (self.d14_read_oft << 16)
        #self.ext_clk_reg_wr_femb( femb_addr, 24, addr_data)
        self.femb.write_reg(24, addr_data)

        addr_data = self.d58_read_wdt + (self.d14_read_wdt << 16)
        #self.ext_clk_reg_wr_femb( femb_addr, 25, addr_data)
        self.femb.write_reg(25, addr_data)

        addr_data = self.d58_idxm_oft + (self.d14_idxm_oft << 16)
        #self.ext_clk_reg_wr_femb( femb_addr, 26, addr_data)
        self.femb.write_reg(26, addr_data)

        addr_data = self.d58_idxm_wdt + (self.d14_idxm_wdt << 16)
        #self.ext_clk_reg_wr_femb( femb_addr, 27, addr_data)
        self.femb.write_reg(27, addr_data)

        addr_data = self.d58_idxl_oft + (self.d14_idxl_oft << 16)
        #self.ext_clk_reg_wr_femb( femb_addr, 28, addr_data)
        self.femb.write_reg(28, addr_data)

        addr_data = self.d58_idxl_wdt + (self.d14_idxl_wdt << 16)
        #self.ext_clk_reg_wr_femb( femb_addr, 29, addr_data)
        self.femb.write_reg(29, addr_data)

        addr_data = self.d58_idl0_oft + (self.d14_idl0_oft << 16)
        #self.ext_clk_reg_wr_femb( femb_addr, 30, addr_data)
        self.femb.write_reg(30, addr_data)

        addr_data = self.d58_idl0_wdt + (self.d14_idl0_wdt << 16)
        #self.ext_clk_reg_wr_femb( femb_addr, 31, addr_data)
        self.femb.write_reg(31, addr_data)

        addr_data = self.d58_idl1_oft + (self.d14_idl1_oft << 16)
        #self.ext_clk_reg_wr_femb( femb_addr, 32, addr_data)
        self.femb.write_reg(32, addr_data)

        addr_data = self.d58_idl1_wdt + (self.d14_idl1_wdt << 16)
        #self.ext_clk_reg_wr_femb( femb_addr, 33, addr_data)
        self.femb.write_reg(33, addr_data)

        #config phase
        for i in range(4):
            addr_data = self.d14_read_step + (self.d14_idxm_step << 16)
            #self.ext_clk_reg_wr_femb( femb_addr, 35, addr_data)
            self.femb.write_reg(35, addr_data)

            addr_data = self.d14_idxl_step + (self.d14_idl0_step << 16)
            #self.ext_clk_reg_wr_femb( femb_addr, 36, addr_data)
            self.femb.write_reg(36, addr_data)

            self.d14_phase_en = self.d14_phase_en ^ 1
            d14_ud = self.d14_read_ud + (self.d14_idxm_ud << 1) + (
                self.d14_idxl_ud << 2) + (self.d14_idl0_ud << 3) + (
                    self.d14_idl1_ud << 4) + (self.d14_phase_en << 15)
            addr_data = self.d14_idl1_step + (d14_ud << 16)
            #self.ext_clk_reg_wr_femb( femb_addr, 37, addr_data)
            self.femb.write_reg(37, addr_data)

            addr_data = self.d58_read_step + (self.d58_idxm_step << 16)
            #self.ext_clk_reg_wr_femb( femb_addr, 38, addr_data)
            self.femb.write_reg(38, addr_data)

            addr_data = self.d58_idxl_step + (self.d58_idl0_step << 16)
            #self.ext_clk_reg_wr_femb( femb_addr, 39, addr_data)
            self.femb.write_reg(39, addr_data)

            self.d58_phase_en = self.d58_phase_en ^ 1
            d58_ud = self.d58_read_ud + (self.d58_idxm_ud << 1) + (
                self.d58_idxl_ud << 2) + (self.d58_idl0_ud << 3) + (
                    self.d58_idl1_ud << 4) + (self.d58_phase_en << 15)
            addr_data = self.d58_idl1_step + (d58_ud << 16)
            #self.ext_clk_reg_wr_femb( femb_addr, 40, addr_data)
            self.femb.write_reg(40, addr_data)
class FEMB_CONFIG(FEMB_CONFIG_BASE):

    #__INIT__#
    def __init__(self):
        #declare basic system parameters
        self.NFEMBS = 4
        self.NASICS = 8
        self.NASICCH = 16

        #declare board specific registers
        self.FEMB_VER = "WIB_SBND"
        self.REG_RESET = 0
        self.REG_ASIC_RESET = 1
        self.REG_ASIC_SPIPROG = 2
        self.REG_SOFT_ADC_RESET = 1

        self.REG_LATCHLOC_3_TO_0 = 4
        self.REG_LATCHLOC_7_TO_4 = 14

        self.REG_FPGA_TP_EN = 16
        self.REG_ASIC_TP_EN = 16
        self.REG_DAC_SELECT = 16
        self.REG_TP = 5

        self.CLK_SELECT = 6
        self.CLK_SELECT2 = 15

        self.REG_SEL_ASIC = 7
        self.REG_SEL_ASIC_LSB = 8

        self.REG_WIB_MODE = 8
        self.REG_ADC_DISABLE = 8

        self.REG_HS_DATA = 9
        self.REG_HS = 17

        self.INT_TP_EN = 18
        self.EXT_TP_EN = 18

        self.REG_SPI_BASE = 0x200
        self.REG_SPI_RDBACK_BASE = 0x250
        
        self.REG_TEST_PAT = 3
        self.REG_TEST_PAT_DATA = 0x01230000

        #internal variables
        self.fembNum = 0
        self.wibNum = 0
        self.useExtAdcClock = True
        self.isRoomTemp = False
        self.doReSync = True
        self.spiStatus = 0x0
        self.syncStatus = 0x0
        self.CLKSELECT_val_RT = 0xFF
        self.CLKSELECT2_val_RT = 0xFF
        self.CLKSELECT_val_CT = 0xEF
        self.CLKSELECT2_val_CT = 0xEF
        self.REG_LATCHLOC_3_TO_0_val = 0x04040404
        self.REG_LATCHLOC_7_TO_4_val = 0x04040404
        self.fe_regs = [0x00000000]*(16+2)*8*8
        self.fe_REGS = [0x00000000]*(8+1)*4
        self.useLArIATmap = False #True

        #COTS shifts
        self.fe1_sft_RT = 0x00000000
        self.fe2_sft_RT = 0x00000000
        self.fe3_sft_RT = 0x00000000
        self.fe4_sft_RT = 0x00000000
        self.fe5_sft_RT = 0x00000000
        self.fe6_sft_RT = 0x00000000
        self.fe7_sft_RT = 0x00000000
        self.fe8_sft_RT = 0x00000000

        self.fe1_sft_CT = 0x00000000
        self.fe2_sft_CT = 0x00000000
        self.fe3_sft_CT = 0x00000000
        self.fe4_sft_CT = 0x00000000
        self.fe5_sft_CT = 0x00000000
        self.fe6_sft_CT = 0x00000000
        self.fe7_sft_CT = 0x00000000
        self.fe8_sft_CT = 0x00000000
            
        #COTS phases
        self.fe1_pha_RT = 0x00000000
        self.fe2_pha_RT = 0x00000000
        self.fe3_pha_RT = 0x00000000
        self.fe4_pha_RT = 0x00000000
        self.fe5_pha_RT = 0x00000000
        self.fe6_pha_RT = 0x00000000
        self.fe7_pha_RT = 0x00000000
        self.fe8_pha_RT = 0x00000000

        self.fe1_pha_CT = 0x00000000
        self.fe2_pha_CT = 0x00000000
        self.fe3_pha_CT = 0x00000000
        self.fe4_pha_CT = 0x00000000
        self.fe5_pha_CT = 0x00000000
        self.fe6_pha_CT = 0x00000000
        self.fe7_pha_CT = 0x00000000
        self.fe8_pha_CT = 0x00000000
        
        #initialize FEMB UDP object
        self.femb = FEMB_UDP()
        self.femb.UDP_PORT_WREG = 32000 #WIB PORTS
        self.femb.UDP_PORT_RREG = 32001
        self.femb.UDP_PORT_RREGRESP = 32002
        self.femb.doReadBack = False #WIB register interface is unreliable

        #ASIC config variables
        self.feasicLeakage = 0 #0 = 500pA, 1 = 100pA
        self.feasicLeakagex10 = 0 #0 = pA, 1 = pA*10
        self.feasicAcdc = 0 #AC = 0, DC = 1
        self.feasicBaseline = 0 #0 = 900mV, 1 = 200mV        
        self.feasicEnableTestInput = 0 #0 = disabled, 1 = enabled
        self.feasicGain = 2 #4.7,7.8,14,25
        self.feasicShape = 1 #0.5,1,2,3
        self.feasicBuf = 1 #0 = OFF, 1 = ON

        #Read in LArIAT mapping if desired

        if self.useLArIATmap:
            self.cppfr = CPP_FILE_RUNNER()            
            with open(self.cppfr.filename('configuration/configs/LArIAT_pin_mapping.map'), "rb") as fp:
                self.lariatMap = pickle.load(fp)
                
            #APA Mapping
            va = self.lariatMap
            va_femb = []
            for vb in va:
                if int(vb[9]) in (0,1,2,3,4) :
                    va_femb.append(vb)
            apa_femb_loc = []
            for chn in range(128):
                for vb in va_femb:
                    if int(vb[8]) == chn:
                        if (vb[1].find("Co")) >= 0 :#collection wire
                            chninfo = [ "X" + vb[0], vb[8], int(vb[6]), int(vb[7]), int(vb[9]), int(vb[10])]
                        elif (vb[1].find("In")) >= 0 : #induction wire
                            chninfo = [ "U" + vb[0], vb[8], int(vb[6]), int(vb[7]), int(vb[9]), int(vb[10])]
                        apa_femb_loc.append(chninfo)
            for chn in range(128):
                fl_w = True
                fl_i = 0
                for tmp in apa_femb_loc:
                    if int(tmp[1]) == chn:
                        fl_w = False
                        break
                if (fl_w):
                    chninfo = [ "V" + format(fl_i, "03d"), format(chn, "03d"), chn//16 , format(chn%15, "02d"), apa_femb_loc[0][4], apa_femb_loc[0][5]]
                    apa_femb_loc.append(chninfo)
                    fl_i = fl_i + 1

            self.All_sort = []
            self.X_sort = []
            self.V_sort = []
            self.U_sort = []
            for i in range(128):
                for chn in apa_femb_loc:
                    if int(chn[1][0:3]) == i :
                        self.All_sort.append(chn)
    
                    for chn in apa_femb_loc:
                        if chn[0][0] == "X" and int(chn[0][1:3]) == i :
                            self.X_sort.append(chn)
                    for chn in apa_femb_loc:
                        if chn[0][0] == "V" and int(chn[0][1:3]) == i :
                            self.V_sort.append(chn)
                    for chn in apa_femb_loc:
                        if chn[0][0] == "U" and int(chn[0][1:3]) == i :
                            self.U_sort.append(chn)

            self.WireDict = {}
            for line in self.All_sort:
                key = "wib{:d}_femb{:d}_chip{:d}_chan{:02d}".format(line[5],line[4],line[2],line[3])
                self.WireDict[key] = line[0]
                
    def printParameters(self):
        print("FEMB #             \t",self.fembNum)
        print("Room temperature   \t",self.isRoomTemp)
        print("Do resync          \t",self.doReSync)
        print("FE-ASIC leakage    \t",self.feasicLeakage)
        print("FE-ASIC leakage x10\t",self.feasicLeakagex10)
        print("FE-ASIC AD/DC      \t",self.feasicAcdc)
        print("FE-ASIC test input \t",self.feasicEnableTestInput)
        print("FE-ASIC baseline   \t",self.feasicBaseline)
        print("FE-ASIC gain       \t",self.feasicGain)
        print("FE-ASIC shape      \t",self.feasicShape)
        print("FE-ASIC buffer     \t",self.feasicBuf)

        print("FE-ASIC config")
        for regNum in range(self.REG_SPI_BASE,self.REG_SPI_BASE+72,1):
            regVal = self.femb.read_reg( regNum)
            if regVal == None:
                continue
            print( str(regNum) + "\t" + str(hex(regVal)) )

    def resetBoard(self):
        print("Reset")

    def initBoard(self):
        self.initWib()
        for femb in range(0,4,1):
            self.selectFemb(femb)
            self.initFemb()
        
    def initWib(self):
        #WIB initialization

        self.wib_switch()
        
        #set UDP ports to WIB registers
        self.wib_reg_enable()

        #register 2, LED
        #self.femb.write_reg_bits(2 , 0, 0xFF, 0 )

        #set jumbo size
        #self.femb.write_reg(0x1F,0xEFB)

        #set external clock
        self.femb.write_reg(0x4, 8)
        self.femb.write_reg(16,0x7F00)
        self.femb.write_reg(15,0)

        #sync timestamp /WIB
        self.femb.write_reg(1,0)
        self.femb.write_reg(1,0)
        self.femb.write_reg(1,2)
        self.femb.write_reg(1,2)                
        self.femb.write_reg(1,0)
        self.femb.write_reg(1,0)

        #Reset error /WIB
        self.femb.write_reg(18, 0x8000)
        self.femb.write_reg(18, 0x8000)

        #return register interface to FEMB
        self.selectFemb(self.fembNum)

    def initFemb(self):
        fembVal = self.fembNum - 4*(self.wibNum)
            
        if (fembVal < 0) or (fembVal >= self.NFEMBS ):
            return

        print("Initialize FEMB",fembVal)

        #FEMB power enable on WIB
        self.powerOnFemb(fembVal)

        #Make sure register interface is for correct FEMB
        self.selectFemb(fembVal)

        #check if FEMB register interface is working
        print("Checking register interface")
        regVal = self.femb.read_reg(6)
        if (regVal == None) or (regVal == -1):

            # try again
            self.powerOffFemb(fembVal)
            time.sleep(10)
            self.powerOnFemb(fembVal)

            newregVal = self.femb.read_reg(6)
            if (newregVal == None) or (newregVal == -1):
                
                print("Error - FEMB register interface is not working.")
                print(" Will not initialize FEMB.")
                return

        checkFirmware = self.checkFirmwareVersion()
        if checkFirmware == False:

            # try again
            self.powerOffFemb(fembVal)
            time.sleep(10)
            self.powerOnFemb(fembVal)

            newcheckFirmware = self.checkFirmwareVersion()
            if newcheckFirmware == False:

                print("Error - invalid firmware, will not attempt to initialize board")
                return

        #turn off pulser
        self.femb.write_reg_bits( self.REG_FPGA_TP_EN, 0,0x1,0) #test pulse enable
        self.femb.write_reg_bits( self.REG_ASIC_TP_EN, 1,0x1,0) #test pulse enable
        self.femb.write_reg_bits( self.REG_DAC_SELECT, 8,0x1,0) #test pulse enable
        self.femb.write_reg_bits( self.REG_TP, 0,0x1F,0x00) #test pulse amplitude
        self.femb.write_reg_bits( self.REG_TP, 16,0xFFFF,0x100) #test pulse frequency
        self.femb.write_reg_bits( self.REG_TP, 8,0xFF,0x00) #test pulse delay

        #Timestamp reset
        self.femb.write_reg(0, 4)
        self.femb.write_reg(0, 4)

        #Reset SPI
        self.femb.write_reg(self.REG_ASIC_RESET,1)
        self.femb.write_reg(self.REG_ASIC_RESET,1)
        self.femb.write_reg(self.REG_ASIC_RESET,2)
        self.femb.write_reg(self.REG_ASIC_RESET,2)

        self.femb.write_reg(self.REG_TEST_PAT, self.REG_TEST_PAT_DATA)

        #Turn off readback checking for ASIC config
        self.femb.doReadBack = False

        #ADC Setup
        self.set_cots_shift()
        
        #Set ASIC SPI configuration registers
        self.configFeAsic()

        #check ASIC SPI
        self.checkFembSpi()
        print("SPI STATUS","\t",self.spiStatus)

        #Enable Streaming
        self.femb.write_reg(9,9)
        self.femb.write_reg(9,9)
        time.sleep(0.1)

        # write to WIB
        self.wib_reg_enable()

        # check link status
        retry_links = False
        link_status = self.femb.read_reg(0x21)
        if (fembVal == 0):
            femb_link = link_status & 0xFF
        elif (fembVal == 1):
            femb_link = (link_status & 0xFF00)>>8
        elif (fembVal == 2):
            femb_link = (link_status & 0xFF0000)>>16
        elif (fembVal == 3):
            femb_link = (link_status & 0xFF000000)>>24
        if (not femb_link):
            retry_links = True
        else:
            print("HS links enabled")
            
        if (retry_links):
            #Enable Streaming
            self.selectFemb(fembVal)
            self.femb.write_reg(9,9)
            self.femb.write_reg(9,9)
            time.sleep(2)

            self.wib_reg_enable()
            print("linkstatus",hex(link_status))
            if (fembVal == 0):
                femb_link = link_status & 0xFF
            elif (fembVal == 1):
                femb_link = (link_status & 0xFF00)>>8
            elif (fembVal == 2):
                femb_link = (link_status & 0xFF0000)>>16
            elif (fembVal == 3):
                femb_link = (link_status & 0xFF000000)>>24
            if (not femb_link):
                print("HS link error, LINK STATUS:",hex(femb_link))
                return
            else:
                print("HS links enabled on retry")
                
        #reset the error counters
        self.femb.write_reg(20,3)
        self.femb.write_reg(20,3)
        time.sleep(0.001)
        self.femb.write_reg(20,0)
        self.femb.write_reg(20,0)
        time.sleep(0.001)

        # start streaming data from ASIC 0 in initialization
        self.femb.write_reg(7, 0x80000000)
        self.femb.write_reg(7, 0x80000000)
        femb_asic = 0 & 0x0F
        wib_asic = (((fembVal << 16)&0x000F0000) + ((femb_asic << 8) &0xFF00))
        self.femb.write_reg(7, wib_asic | 0x80000000)
        self.femb.write_reg(7, wib_asic | 0x80000000)
        self.femb.write_reg(7, wib_asic)
        self.femb.write_reg(7, wib_asic)

        # return to FEMB control
        self.selectFemb(fembVal)

        #Print some happy messages for shifters
        print("Finished initializing ASICs for WIB{:d} FEMB{:d}".format(self.wibNum,fembVal)) 

    #Test FEMB SPI working
    def checkFembSpi(self):
        print("Check ASIC SPI")
         
        self.spiStatus = 0

        for k in range(2):
            #Disable streaming
            self.femb.write_reg(9, 0x0)
            time.sleep(0.01)

            i = 0
            for regNum in range(self.REG_SPI_BASE,self.REG_SPI_BASE+len(self.fe_REGS),1):
                self.femb.write_reg( regNum, self.fe_REGS[i])
                i += 1
            time.sleep(0.01)
            self.femb.write_reg( self.REG_ASIC_SPIPROG, 1)

            if (k==1):
                for j in range(3):
                    self.spiStatus = 0
                    time.sleep(0.01)
                    readvals = []
                    for regNum in range(self.REG_SPI_RDBACK_BASE,self.REG_SPI_RDBACK_BASE+len(self.fe_REGS),1):
                        rdbckVal = self.femb.read_reg( regNum)
                        if rdbckVal == None :
                            print("Error - FEMB register interface is not working.")
                            return
                        else:
                            readvals.append(rdbckVal)
                        time.sleep(0.001)

                    for i in range(len(self.fe_REGS)):
                        if self.fe_REGS[i] != readvals[i] :
                            print(hex(self.fe_REGS[i]),"\t",hex(readvals[i]))
                            print("SPI readback failed.")
                            self.spiStatus = 1
                if self.spiStatus == 1:            
                    return
        self.femb.write_reg(9, 9)
        self.femb.write_reg(9, 9)
        time.sleep(0.1)
        

    def checkSync(self):
        print("Check ASIC SYNC")
        regVal = self.femb.read_reg(6)
        if regVal == None:
            print("doAsicConfigcheckFembSpi: Could not check SYNC status, bad")
            return
        syncVal = ((regVal >> 16) & 0xFFFF)
        self.syncStatus = syncVal

    #Setup talking to WIB
    def wib_switch(self):
        #Set IP addresses based in wib number:
        #For SBND-LArIAT
        #iplist = ["131.225.150.203","131.225.150.206"]
        #For BNL testing
        iplist = ["192.168.121.1"]
        self.femb.UDP_IP = iplist[self.wibNum]
    
    def wib_reg_enable(self):
        
        self.femb.UDP_PORT_WREG = 32000
        self.femb.UDP_PORT_RREG = 32001
        self.femb.UDP_PORT_RREGRESP = 32002
        self.femb.REG_SLEEP = 0.001

 

    #COTS Shift and Phase Settings
    def set_cots_shift(self):
        if self.isRoomTemp:
            print("Setting COTS Shifts for RT")
            self.femb.write_reg(21,self.fe1_sft_RT)
            self.femb.write_reg(29,self.fe1_pha_RT)
            self.femb.write_reg(22,self.fe2_sft_RT)
            self.femb.write_reg(30,self.fe2_pha_RT)
            self.femb.write_reg(23,self.fe3_sft_RT)
            self.femb.write_reg(31,self.fe3_pha_RT)
            self.femb.write_reg(24,self.fe4_sft_RT)
            self.femb.write_reg(32,self.fe4_pha_RT)
            self.femb.write_reg(25,self.fe5_sft_RT)
            self.femb.write_reg(33,self.fe5_pha_RT)
            self.femb.write_reg(26,self.fe6_sft_RT)
            self.femb.write_reg(34,self.fe6_pha_RT)
            self.femb.write_reg(27,self.fe7_sft_RT)
            self.femb.write_reg(35,self.fe7_pha_RT)
            self.femb.write_reg(28,self.fe8_sft_RT)
            self.femb.write_reg(36,self.fe8_pha_RT)
        else:
            print("Setting COTS Shifts for CT")
            self.femb.write_reg(21,self.fe1_sft_CT)
            self.femb.write_reg(29,self.fe1_pha_CT)
            self.femb.write_reg(22,self.fe2_sft_CT)
            self.femb.write_reg(30,self.fe2_pha_CT)
            self.femb.write_reg(23,self.fe3_sft_CT)
            self.femb.write_reg(31,self.fe3_pha_CT)
            self.femb.write_reg(24,self.fe4_sft_CT)
            self.femb.write_reg(32,self.fe4_pha_CT)
            self.femb.write_reg(25,self.fe5_sft_CT)
            self.femb.write_reg(33,self.fe5_pha_CT)
            self.femb.write_reg(26,self.fe6_sft_CT)
            self.femb.write_reg(34,self.fe6_pha_CT)
            self.femb.write_reg(27,self.fe7_sft_CT)
            self.femb.write_reg(35,self.fe7_pha_CT)
            self.femb.write_reg(28,self.fe8_sft_CT)
            self.femb.write_reg(36,self.fe8_pha_CT)
            
        self.femb.write_reg(8,0)
        self.femb.write_reg(8,0)
        time.sleep(0.02)
        self.femb.write_reg(8,0x10)
        self.femb.write_reg(8,0x10)

        
    #FEMB power enable on WIB
    def powerOnFemb(self,femb):
        fembVal = int(femb)
        if (fembVal < 0) or (fembVal > 3 ):
            return

        #set UDP ports to WIB registers
        self.wib_reg_enable()

        # read back existing power setting
        oldVal = self.femb.read_reg(8)
        #print("oldVal",oldVal)
        
        #FEMB power enable
        if(fembVal == 0):
            regVal = 0x31000F
        if(fembVal == 1):
            regVal = 0x5200F0
        if(fembVal == 2):
            regVal = 0x940F00
        if(fembVal == 3):
            regVal = 0x118F000

        pwrVal = regVal | oldVal

        self.femb.write_reg(8,0)
        time.sleep(1)
        self.femb.write_reg(8, pwrVal)
        time.sleep(2)
        
        regVal = self.femb.read_reg(8)
        if regVal == None:
            return
        print("FEMB Power on: ", hex(regVal))

        #set UDP ports back to normal
        self.selectFemb(self.fembNum)

    def powerOffFemb(self,femb):
        fembVal = int(femb)
        if (fembVal < 0) or (fembVal > 3 ):
            return

        #set UDP ports to WIB registers
        self.wib_reg_enable()

        # read back existing power setting
        oldVal = self.femb.read_reg(8)
                
        regVal = 0
        #FEMB power disable
        if(fembVal == 0 and (oldVal & 0xF) != 0):
            regVal = 0x31000F
        if(fembVal == 1 and (oldVal & 0xF0)>>4 != 0):
            regVal = 0x5200F0
        if(fembVal == 2 and (oldVal & 0xF00)>>8 != 0):
            regVal = 0x940F00
        if(fembVal == 3 and (oldVal & 0xF000)>>16 != 0):
            regVal = 0x118F000
        
        pwrVal = 0x100000 | (regVal ^ oldVal)
        
        self.femb.write_reg(8, pwrVal)
        
        regVal = self.femb.read_reg(8)
        if regVal == None:
            return
        print("FEMB Power off: ", hex(regVal))        
        
        #set UDP ports back to normal
        self.selectFemb(self.fembNum)
        
    def selectChannel(self,asic,chan):
        #print("Select channel")
        asicVal = int(asic)
        if (asicVal < 0 ) or (asicVal > self.NASICS):
            return

        #print("asicVal",asicVal)
        fembVal = self.fembNum
        #print("fembVal",fembVal)
        
        #set UDP ports to WIB
        self.wib_reg_enable()

        # start streaming data from ASIC 0 in initialization
        self.femb.write_reg(7, 0x80000000)
        self.femb.write_reg(7, 0x80000000)
        femb_asic = asicVal & 0x0F
        wib_asic = (((fembVal << 16)&0x000F0000) + ((femb_asic << 8) &0xFF00))
        self.femb.write_reg(7, wib_asic | 0x80000000)
        self.femb.write_reg(7, wib_asic | 0x80000000)
        self.femb.write_reg(7, wib_asic)
        self.femb.write_reg(7, wib_asic)
        
        #select ASIC
        #print("Selecting ASIC " + str(asicVal) )
        self.femb.write_reg_bits(self.REG_SEL_ASIC , self.REG_SEL_ASIC_LSB, 0xF, asicVal )

        #Note: WIB data format streams all 16 channels, don't need to select specific channel

        # return to FEMB control
        self.selectFemb(fembVal)

        #Enable Streaming
        self.femb.write_reg(9,9)
        self.femb.write_reg(9,9)
        time.sleep(0.1)
        
    def configFeAsic(self):
        print("CONFIG ASICs")

        # #global config varibles
        feasicLeakageVal = int( self.feasicLeakage ) #0 = 500pA, 1 = 100pA
        feasicLeakagex10Val = int( self.feasicLeakagex10 ) #0 = x1, 1 = x10
        acdcVal = int( self.feasicAcdc ) #DC = 0, AC = 1
        
        #channel specific variables
        testVal = int( self.feasicEnableTestInput )
        baseVal = int( self.feasicBaseline ) #0 = 900mV, 1 = 200mV
        gainVal = int( self.feasicGain )
        shapeVal = int( self.feasicShape )
        bufVal = int( self.feasicBuf ) #0 = OFF, 1 = ON

        if (testVal < 0 ) or (testVal > 1):
            return
        if (baseVal < 0 ) or (baseVal > 1):
            return
        if (gainVal < 0 ) or (gainVal > 3):
            return
        if (shapeVal < 0 ) or (shapeVal > 3):
            return
        if (acdcVal < 0 ) or (acdcVal > 1):
            return
        if (bufVal < 0 ) or (bufVal > 1):
            return
        if (feasicLeakageVal < 0 ) or (feasicLeakageVal > 1 ):
            return
        if (feasicLeakagex10Val < 0) or (feasicLeakagex10Val > 1):
            return

        #gain
        gainArray = [0,2,1,3] #00=4.7, 10=7.8, 01=14, 11=25
        gainValCorrect = gainArray[gainVal]
        
        #shape
        shapeArray = [2,0,3,1] #00=1.0, 10=0.5, 01=3.0, 11=2.0
        shapeValCorrect = shapeArray[shapeVal]

        #datashift
        if self.isRoomTemp == True:
            self.femb.write_reg_bits(self.CLK_SELECT , 0, 0xFF, self.CLKSELECT_val_RT ) #clock select
            self.femb.write_reg_bits(self.CLK_SELECT2 , 0, 0xFF, self.CLKSELECT2_val_RT ) #clock select 2
        else:
            self.femb.write_reg_bits(self.CLK_SELECT , 0, 0xFF, self.CLKSELECT_val_CT ) #clock select
            self.femb.write_reg_bits(self.CLK_SELECT2 , 0, 0xFF,  self.CLKSELECT2_val_CT ) #clock select 2                    
        self.femb.write_reg_bits(self.REG_LATCHLOC_3_TO_0 , 0, 0xFFFFFFFF, self.REG_LATCHLOC_3_TO_0_val )
        self.femb.write_reg_bits(self.REG_LATCHLOC_7_TO_4 , 0, 0xFFFFFFFF, self.REG_LATCHLOC_7_TO_4_val )
        
        #COTS Register Settings
        sts = testVal
        snc = baseVal
        sg = gainValCorrect
        st = shapeValCorrect
        smn = 0 #Output monitor enabled: not currently an option in femb_python so keep at 0 for now
        sdf = bufVal
        chn_reg = ((sts&0x01)<<7) + ((snc&0x01)<<6) + ((sg&0x03)<<4) + ((st&0x03)<<2)  + ((smn&0x01)<<1) + ((sdf&0x01)<<0)

        #COTS Global Register Settings
        slk0 = feasicLeakageVal
        stb1 = 0 #Monitors not currently used in femb_python
        stb = 0 #Monitors not currently used in femb_python
        s16 = 0 #High filter in channel 16 disabled
        slk1 = feasicLeakagex10Val
        sdc = acdcVal
        swdac = 0 #For pulser, set elsewhere
        dac = 0 #For pulser, set elsewhere
        global_reg = ((slk0&0x01)<<0) + ((stb1&0x01)<<1) + ((stb&0x01)<<2)+ ((s16&0x01)<<3) + ((slk1&0x01)<<4) + ((sdc&0x01)<<5) +((00&0x03)<<6)
        dac_reg = (((dac&0x01)//0x01)<<7)+(((dac&0x02)//0x02)<<6)+\
                  (((dac&0x04)//0x04)<<5)+(((dac&0x08)//0x08)<<4)+\
                  (((dac&0x10)//0x10)<<3)+(((dac&0x20)//0x20)<<2)+\
                  (((swdac&0x03))<<0)
        
        for chip in range(self.NASICS):
            for chn in range(self.NASICCH):
                if self.useLArIATmap:
                    key = "wib{:d}_femb{:d}_chip{:d}_chan{:02d}".format(self.wibNum,self.fembNum,chip+1,chn) #Note map has chips 1-8, not 0-7
                    if self.WireDict[key][0] == "X":
                        snc = 0 #set baseline for collection
                    elif self.WireDict[key][0] == "U":
                        snc = 1 #set baseline for induction

                chn_reg = ((sts&0x01)<<7) + ((snc&0x01)<<6) + ((sg&0x03)<<4) + ((st&0x03)<<2)  + ((smn&0x01)<<1) + ((sdf&0x01)<<0)
                #print("chip",chip,"channel",chn,"chn_reg",hex(chn_reg))
                chn_reg_bool = []
                for j in range(8):
                    chn_reg_bool.append ( bool( (chn_reg>>j)%2 ))
                start_pos = (8*16+16)*chip + (16-chn)*8
                self.fe_regs[start_pos-8 : start_pos] = chn_reg_bool

            global_reg_bool = []
            for j in range(8):
                global_reg_bool.append ( bool( (global_reg>>j)%2 ) )
            for j in range(8):
                global_reg_bool.append ( bool( (dac_reg>>j)%2 ) )

            start_pos = (8*16+16)*chip + 16*8
            self.fe_regs[start_pos : start_pos+16] = global_reg_bool

        #Convert bits to 36 32-bit register words
        for chip in [0,2,4,6]:
            chip_bits_len = 8*(16+2)
            chip_fe_regs0 = self.fe_regs[   chip*chip_bits_len: (chip+1)* chip_bits_len]
            chip_fe_regs1 = self.fe_regs[   (chip+1)*chip_bits_len: (chip+2)* chip_bits_len]
            chip_regs = []
            for onebit in chip_fe_regs0:
                chip_regs.append(onebit)
            for onebit in chip_fe_regs1:
                chip_regs.append(onebit)
            len32 = len(chip_regs)//32
            if (len32 != 9):
                print("ERROR FE register mapping")
            else:
                for i in range(len32):
                    if ( i*32 <= len(chip_regs) ):
                        bits32 = chip_regs[i*32: (i+1)*32]
                        self.fe_REGS[int(chip/2*len32 + i) ] = (sum(v<<j for j, v in enumerate(bits32)))


        #turn off HS data before register writes
        self.femb.write_reg_bits(9 , 0, 0x1, 0 )
        print("HS link turned off")
        time.sleep(1)

        #run the SPI programming
        self.doAsicConfig()

        #turn HS link back on
        print("HS link turned back on")
        self.femb.write_reg_bits(9 , 0, 0x1, 1 )
        time.sleep(1)


    def doAsicConfig(self):
        print("Program ASIC SPI")

        for k in range(2):
            #Disable streaming
            self.femb.write_reg(9, 0x0)
            time.sleep(0.01)

            i = 0
            for regNum in range(self.REG_SPI_BASE,self.REG_SPI_BASE+len(self.fe_REGS),1):
                self.femb.write_reg( regNum, self.fe_REGS[i])
                self.femb.write_reg( regNum+36, self.fe_REGS[i])
                i += 1
            time.sleep(0.01)
            self.femb.write_reg( self.REG_ASIC_SPIPROG, 1)

        #self.printParameters()
        
        #Enable streaming
        self.femb.write_reg(9, 9)
        self.femb.write_reg(9, 9)
        time.sleep(0.1)

    def selectFemb(self, fembIn):
        fembVal = int( fembIn)
        if (fembVal < 0) or (fembVal > self.NFEMBS ):
            print("Invalid FEMB # requested")
            return
        self.fembNum = fembVal
        #print("selecting FEMB",fembVal)

        #set read/write ports
        if fembVal == 0:
            self.femb.UDP_PORT_WREG = 32016
            self.femb.UDP_PORT_RREG = 32017
            self.femb.UDP_PORT_RREGRESP = 32018

        if fembVal == 1:
            self.femb.UDP_PORT_WREG = 32032
            self.femb.UDP_PORT_RREG = 32033
            self.femb.UDP_PORT_RREGRESP = 32034

        if fembVal == 2:
            self.femb.UDP_PORT_WREG = 32048
            self.femb.UDP_PORT_RREG = 32049
            self.femb.UDP_PORT_RREGRESP = 32050

        if fembVal == 3:
            self.femb.UDP_PORT_WREG = 32064
            self.femb.UDP_PORT_RREG = 32065
            self.femb.UDP_PORT_RREGRESP = 32066

        #self.femb.write_reg(0, femb_asic)
        self.femb.write_reg(self.REG_HS,1)
        

        #slow down register interface for FEMBs
        self.femb.REG_SLEEP = 0.05
        time.sleep(0.1)

    def setFpgaPulser(self,enable,dac):

        enableVal = int(enable)
        print("set FPGA pulser",enableVal)

        if (enableVal < 0 ) or (enableVal > 1 ) :
            print( "femb_config_femb : setFpgaPulser - invalid enable value")
            return
        dacVal = int(dac)
        if ( dacVal < 0 ) or ( dacVal > 0x3F ) :
            print( "femb_config_femb : setFpgaPulser - invalid dac value")
            return

        self.femb.write_reg_bits( self.REG_FPGA_TP_EN, 0,0x3,enableVal) #test pulse enable
        self.femb.write_reg_bits( self.REG_FPGA_TP_EN, 8,0x1,enableVal) #test pulse enable
        self.femb.write_reg_bits( self.REG_TP , 0, 0x3F, dacVal ) #TP Amplitude
        self.femb.write_reg_bits( self.REG_TP , 8, 0xFF, 219 ) #DLY
        self.femb.write_reg_bits( self.REG_TP , 16, 0xFFFF, 497 ) #FREQ

        #set pulser enable bit
        if enableVal == 1 :
            self.femb.write_reg( self.EXT_TP_EN, 0x2) #this register is confusing, check
        else :
            self.femb.write_reg( self.EXT_TP_EN, 0x3) #pulser disabled

        #connect channel test input to external pin
        firstfourasics = int(self.NASICS/2)
        for asic in range(0,firstfourasics,1):
            baseReg = int(asic)*9

            self.fe_REGS[baseReg+4] = self.fe_REGS[baseReg+4] & 0xFFFF0000
            self.fe_REGS[baseReg+8] = self.fe_REGS[baseReg+8] & 0x0000FFFF
                
            if enableVal == 1:
                self.fe_REGS[baseReg+4] = self.fe_REGS[baseReg+4] | 0x2<<8
                self.fe_REGS[baseReg+8] = self.fe_REGS[baseReg+8] | 0x2<<24

        self.doAsicConfig()

    def setInternalPulser(self,enable,dac):

        enableVal = int(enable)
        print("set ASIC pulser",enableVal)
        
        if (enableVal < 0 ) or (enableVal > 1 ) :
            print( "femb_config_femb : setInternalPulser - invalid enable value")
            return
        dacVal = int(dac)
        if ( dacVal < 0 ) or ( dacVal > 0x3F ) :
            print( "femb_config_femb : setInternalPulser - invalid dac value")
            return

        self.femb.write_reg_bits( self.REG_DAC_SELECT, 8, 0x1, 0) #test pulse enable
        self.femb.write_reg_bits( self.REG_TP , 0, 0x3F, 0 ) #TP Amplitude
        self.femb.write_reg_bits( self.REG_TP , 8, 0xFF, 219 ) #DLY
        self.femb.write_reg_bits( self.REG_TP , 16, 0xFFFF, 497 ) #FREQ

        #set pulser enable bit
        if enableVal == 1 :
            self.femb.write_reg( self.INT_TP_EN, 0x2) #this register is confusing, check
        else :
            self.femb.write_reg( self.INT_TP_EN, 0x3) #pulser disabled

        dacVal = (dacVal & 0x3F)
        newDacVal = int('{:08b}'.format(dacVal)[::-1], 2)

        asicWord = ((newDacVal << 8 ) & 0xFFFF)
        if enableVal == 1 :
            asicWord = asicWord + (0x1 << 8)
        #print(hex(asicWord))
            
        #connect channel test input to external pin
        firstfourasics = int(self.NASICS/2)
        for asic in range(0,firstfourasics,1):
            baseReg = int(asic)*9

            self.fe_REGS[baseReg+4] = self.fe_REGS[baseReg+4] & 0xFFFF0000
            self.fe_REGS[baseReg+8] = self.fe_REGS[baseReg+8] & 0x0000FFFF
                
            if enableVal == 1:
                self.fe_REGS[baseReg+4] = self.fe_REGS[baseReg+4] | asicWord
                self.fe_REGS[baseReg+8] = self.fe_REGS[baseReg+8] | asicWord<<16

        self.doAsicConfig()

        if enableVal == 1:
            self.femb.write_reg_bits( self.REG_ASIC_TP_EN , 0, 0x3, 0x2 ) #NOTE, also disabling FPGA pulser here
        else:
            self.femb.write_reg_bits( self.REG_ASIC_TP_EN , 0, 0x3, 0x0 )

    def checkFirmwareVersion(self):
        #set UDP ports to WIB
        self.wib_reg_enable()

        #check WIB fw version reg
        wibVerReg = self.femb.read_reg(255)
        if wibVerReg == None :
            return False
        wibVerReg = (wibVerReg & 0xFFF)

        #set UDP ports back to normal
        self.selectFemb(self.fembNum)

        fembVerReg = self.femb.read_reg(257)

        if fembVerReg == None :
            return False
        fembVerReg = (fembVerReg & 0xFFF)

        if wibVerReg != 0x122 and wibVerReg != 0x111:
            print("Invalid WIB firmware version detected",hex(wibVerReg),"this configuration requires version 0x108 or 0x111")
            return False
        if fembVerReg != 0x501 :
            print("Invalid FEMB firmware version detected",hex(fembVerReg),"this configuration requires version 0x501")
            return False
        
        print( "WIB Firmware Version: " + str(hex(wibVerReg)) )
        print( "FEMB Firmware Version: " + str(hex(fembVerReg)) )

        #good firmware id
        return True

    def readCurrent(self):

        self.femb.UDP_PORT_WREG = 32000 #WIB PORTS
        self.femb.UDP_PORT_RREG = 32001
        self.femb.UDP_PORT_RREGRESP = 32002

        for j in range(0,100):
            self.femb.write_reg(5,0)
            self.femb.write_reg(5,0x10000)
            self.femb.write_reg(5,0)
            time.sleep(0.01)

        results = []
        for pwrSel in range(1,25):
            self.femb.write_reg(5,pwrSel)
            time.sleep(0.1)
            regVal = self.femb.read_reg(6)
            if regVal == None:
                 results.append(0)
                 continue
                 #return None
            val = regVal & 0xFFFFFFFF
            results.append(val)

        self.selectFemb(0)
        return results
Exemple #6
0
class FEMB_CONFIG(FEMB_CONFIG_BASE):

    #__INIT__#
    def __init__(self):
        #set up UDP interface
        self.femb = FEMB_UDP()

    def initBoard(self):
        print("\nInitializing board and checking register interface\n")

        #check if FEMB register interface is working
        regVal = self.femb.read_reg(257)
        if regVal == None:
            print(
                "Error!! FEMB register interface is not working, will not initialize FEMB."
            )
            print("Is the power supply turned on?\n")
            sys.exit(1)

        #check WIB fw version reg
        firmwareVerReg = (regVal & 0xFFF)
        if firmwareVerReg != 0x102:
            print(
                'Error initializing board!! Invalid firmware and/or register read error.\n'
            )
            sys.exit(1)

    def readStatus(self, epcsNum=0):
        #EPCS OP Code
        op_reg = 1 + 3 * epcsNum
        #EPCS Status
        status_reg = 3 + 3 * epcsNum

        #set status op code
        self.femb.write_reg(op_reg, 0x5)
        #start EPCS operation
        self.femb.write_reg(op_reg, 0x105)
        time.sleep(0.1)
        #stop EPCS operation
        self.femb.write_reg(op_reg, 0x5)

        #read status bit
        statusVal = self.femb.read_reg(status_reg)
        if statusVal == None:
            print("Error!! Status is None.")
            return

        return statusVal

    def readFlash(self, epcsNum=0, pageNum=0):
        #EPCS OP Code
        op_reg = 1 + 3 * epcsNum
        #EPCS address
        addr_reg = 2 + 3 * epcsNum

        read_base = 512 + 256 * epcsNum

        #set page to read
        self.femb.write_reg(addr_reg, 256 * pageNum)  #1 page = 256 byte

        #read bytes op code
        self.femb.write_reg(op_reg, 0x3)

        #start EPCS operation
        self.femb.write_reg(op_reg, 0x103)
        time.sleep(0.1)
        self.femb.write_reg(op_reg, 0x3)

        #read the value
        outputData = []
        for reg in range(read_base + 64, read_base + 64 + 64, 1):
            regVal = self.femb.read_reg(reg)
            if regVal == None:
                print("Error!! Read value is None, will continue")
                continue
            outputData.append(regVal)
        return outputData

    def eraseFlash(self, epcsNum=0):
        print("Erasing flash %s" % (epcsNum))
        #EPCS OP Code
        op_reg = 1 + 3 * epcsNum

        #write enable
        self.femb.write_reg(op_reg, 0x6)
        self.femb.write_reg(op_reg, 0x106)
        time.sleep(0.1)
        self.femb.write_reg(op_reg, 0x6)

        #erase bulk
        self.femb.write_reg(op_reg, 0xC7)
        self.femb.write_reg(op_reg, 0x1C7)
        time.sleep(0.1)
        self.femb.write_reg(op_reg, 0xC7)

    def programFlash(self, epcsNum=0, pageNum=0, inputData=None):
        #EPCS OP Code
        op_reg = 1 + 3 * epcsNum

        #EPCS address
        addr_reg = 2 + 3 * epcsNum

        #EPCS Status
        status_reg = 3 + 3 * epcsNum

        write_base = 512 + 256 * epcsNum

        count = 0
        for reg in range(write_base, write_base + 64, 1):
            self.femb.write_reg(reg, inputData[count])
            count += 1

        #Set write enable
        self.femb.write_reg(op_reg, 0x6)
        self.femb.write_reg(op_reg, 0x106)
        time.sleep(0.1)
        self.femb.write_reg(op_reg, 0x6)

        #set page to write
        self.femb.write_reg(addr_reg, 256 * pageNum)

        #write bytes
        self.femb.write_reg(op_reg, 0x2)
        self.femb.write_reg(op_reg, 0x102)
        time.sleep(0.1)
        self.femb.write_reg(op_reg, 0x2)
class FEMB_CONFIG(FEMB_CONFIG_BASE):
    def __init__(self):
        super().__init__()
        #declare board specific registers
        self.FEMB_VER = "adctest"
        self.REG_RESET = 0
        self.REG_ASIC_RESET = 1
        self.REG_ASIC_SPIPROG = 2
        self.REG_SEL_ASIC = 7
        self.REG_SEL_CH = 7
        self.REG_FESPI_BASE = 592
        self.REG_ADCSPI_BASE = 512
        self.REG_FESPI_RDBACK_BASE = 632
        self.REG_ADCSPI_RDBACK_BASE = 552
        self.REG_HS = 17
        self.REG_LATCHLOC = 4
        self.REG_CLKPHASE = 6
        #Latch latency 0x6666666f    Phase 0xffff0055
        self.ADC_TESTPATTERN = [
            0x12, 0x345, 0x678, 0xf1f, 0xad, 0xc01, 0x234, 0x567, 0x89d, 0xeca,
            0xff0, 0x123, 0x456, 0x789, 0xabc, 0xdef
        ]
        self.NASICS = 1
        self.FUNCGENINTER = RigolDG4000("/dev/usbtmc1", 1)
        self.POWERSUPPLYINTER = RigolDP800("/dev/usbtmc0", ["CH1"])
        self.F2DEFAULT = 0
        self.CLKDEFAULT = "fifo"

        #initialize FEMB UDP object
        self.femb = FEMB_UDP()
        self.adc_reg = ADC_ASIC_REG_MAPPING()

    def resetBoard(self):
        #Reset system
        self.femb.write_reg(self.REG_RESET, 1)
        time.sleep(5.)

        #Reset registers
        self.femb.write_reg(self.REG_RESET, 2)
        time.sleep(1.)

        #Time stamp reset
        #femb.write_reg( 0, 4)
        #time.sleep(0.5)

        #Reset ADC ASICs
        self.femb.write_reg(self.REG_ASIC_RESET, 1)
        time.sleep(0.5)

    def initBoard(self):
        nRetries = 5
        for iRetry in range(nRetries):
            #set up default registers

            #Reset ADC ASICs
            self.femb.write_reg(self.REG_ASIC_RESET, 1)
            time.sleep(0.5)

            #Set ADC test pattern register
            self.femb.write_reg(3, 0x01230000)  # test pattern off
            #self.femb.write_reg( 3, 0x81230000) # test pattern on

            #Set ADC latch_loc
            self.femb.write_reg(self.REG_LATCHLOC, 0x66666667)
            #Set ADC clock phase
            self.femb.write_reg(self.REG_CLKPHASE, 0xfffc0054)

            #internal test pulser control
            self.femb.write_reg(5, 0x00000000)
            self.femb.write_reg(13, 0x0)  #enable

            #Set test and readout mode register
            self.femb.write_reg(
                7, 0x0000)  #11-8 = channel select, 3-0 = ASIC select

            #Set number events per header
            self.femb.write_reg(8, 0x0)

            #ADC ASIC SPI registers
            print("Config ADC ASIC SPI")
            print("ADCADC")
            ## Corresponds to f0=f1=0, clk=0,clk1=1, pcsr=pdsr=frqc=1, tstin=1
            ## en_gr=0, slp=0
            ## Clocks from digital generator of FIFO
            regs = [
                0xC0C0C0C,
                0xC0C0C0C,
                0xC0C0C0C,
                0xC0C0C0C,
                0xC0C0C0C,
                0xC0C0C0C,
                0xC0C0C0C,
                0xC0C0C0C,
                0x18321832,
                0x18181819,
                0x18181818,
                0x18181818,
                0x18181818,
                0x18181818,
                0x18181818,
                0x18181818,
                0x64186418,
                0x30303030,
                0x30303030,
                0x30303030,
                0x30303030,
                0x30303030,
                0x30303030,
                0x30303030,
                0x30303030,
                0x60c868c8,
                0x60606868,
                0x60606868,
                0x60606868,
                0x60606868,
                0x60606868,
                0x60606868,
                0x60606868,
                0x9060A868,
                0x10001,
            ]

            self.adc_reg.set_sbnd_board(frqc=1,
                                        pdsr=1,
                                        pcsr=1,
                                        clk0=0,
                                        clk1=1,
                                        f1=0,
                                        f2=0,
                                        tstin=1)
            regs = self.adc_reg.REGS
            for iReg, val in enumerate(regs):
                #print("{:032b}".format(val))
                print("{:08x}".format(val))
                self.femb.write_reg(self.REG_ADCSPI_BASE + iReg, val)

            #ADC ASIC sync
            self.femb.write_reg(17,
                                0x1)  # controls HS link, 0 for on, 1 for off
            self.femb.write_reg(17,
                                0x0)  # controls HS link, 0 for on, 1 for off

            #Write ADC ASIC SPI
            print("Program ADC ASIC SPI")
            self.femb.write_reg(self.REG_ASIC_SPIPROG, 1)
            time.sleep(0.1)
            self.femb.write_reg(self.REG_ASIC_SPIPROG, 1)
            time.sleep(0.1)

            print("Check ADC ASIC SPI")
            for regNum in range(self.REG_ADCSPI_RDBACK_BASE,
                                self.REG_ADCSPI_RDBACK_BASE + 34, 1):
                val = self.femb.read_reg(regNum)
                #print("{:08x}".format(val))

            #enable streaming
            #self.femb.write_reg( 9, 0x8)

            #LBNE_ADC_MODE
            self.femb.write_reg(16, 0x1)

            # Check that board streams data
            data = self.femb.get_data(1)
            if data == None:
                print("Board not streaming data, retrying initialization...")
                continue  # try initializing again
            print("FEMB_CONFIG--> Reset FEMB is DONE")
            return
        print(
            "Error: Board not streaming data after trying to initialize {} times. Exiting."
            .format(nRetries))
        sys.exit(1)

    def configAdcAsic_regs(self, Adcasic_regs):
        #ADC ASIC SPI registers
        print("FEMB_CONFIG--> Config ADC ASIC SPI")
        for k in range(10):
            i = 0
            for regNum in range(self.REG_ADCSPI_BASE,
                                self.REG_ADCSPI_BASE + len(Adcasic_regs), 1):
                #print("{:032b}".format(Adcasic_regs[i]))
                print("{:08x}".format(Adcasic_regs[i]))
                self.femb.write_reg(regNum, Adcasic_regs[i])
                time.sleep(0.05)
                i = i + 1

            #print("  ADC ASIC write : ",Adcasic_regs)
            #ADC ASIC sync
            self.femb.write_reg(17,
                                0x1)  # controls HS link, 0 for on, 1 for off
            self.femb.write_reg(17,
                                0x0)  # controls HS link, 0 for on, 1 for off

            #Write ADC ASIC SPI
            print("FEMB_CONFIG--> Program ADC ASIC SPI")
            self.femb.write_reg(self.REG_ASIC_SPIPROG, 1)
            time.sleep(0.1)
            self.femb.write_reg(self.REG_ASIC_SPIPROG, 1)
            time.sleep(0.1)

            #self.femb.write_reg ( 18, 0x0)
            #time.sleep(0.1)

            #LBNE_ADC_MODE
            self.femb.write_reg(16, 0x1)

            print("FEMB_CONFIG--> Check ADC ASIC SPI")
            adcasic_rb_regs = []
            for regNum in range(
                    self.REG_ADCSPI_RDBACK_BASE,
                    self.REG_ADCSPI_RDBACK_BASE + len(Adcasic_regs), 1):
                val = self.femb.read_reg(regNum)
                #print("{:08x}".format(val))
                adcasic_rb_regs.append(val)

            #print("  ADC ASIC read back: ",adcasic_rb_regs)
            #if (adcasic_rb_regs !=Adcasic_regs  ) :
            #    #if ( k == 1 ):
            #    #    sys.exit("femb_config : Wrong readback. ADC SPI failed")
            #    #    return
            #    print("FEMB_CONFIG--> ADC ASIC Readback didn't match, retrying...")
            #else:
            if True:
                print("FEMB_CONFIG--> ADC ASIC SPI is OK")
                break
        #enable streaming
        #self.femb.write_reg ( 9, 0x8)
        #LBNE_ADC_MODE

    def configAdcAsic(self,
                      enableOffsetCurrent=None,
                      offsetCurrent=None,
                      testInput=None,
                      freqInternal=None,
                      sleep=None,
                      pdsr=None,
                      pcsr=None,
                      clockMonostable=None,
                      clockExternal=None,
                      clockFromFIFO=None,
                      sLSB=None,
                      f0=0,
                      f1=0,
                      f2=0,
                      f3=None,
                      f4=None,
                      f5=None):
        """
        Configure ADCs
          enableOffsetCurrent: 0 disable offset current, 1 enable offset current
          offsetCurrent: 0-15, amount of current to draw from sample and hold
          testInput: 0 digitize normal input, 1 digitize test input
          freqInternal: internal clock frequency: 0 1MHz, 1 2MHz
          sleep: 0 disable sleep mode, 1 enable sleep mode
          pdsr: if pcsr=0: 0 PD is low, 1 PD is high
          pcsr: 0 power down controlled by pdsr, 1 power down controlled externally
          Only one of these can be enabled:
            clockMonostable: True ADC uses monostable clock
            clockExternal: True ADC uses external clock
            clockFromFIFO: True ADC uses digital generator FIFO clock
          sLSB: LSB current steering mode. 0 for full, 1 for partial (ADC7 P1)
          f0, f1, f2, f3, f4, f5: version specific
        """
        FEMB_CONFIG_BASE.configAdcAsic(self,
                                       clockMonostable=clockMonostable,
                                       clockExternal=clockExternal,
                                       clockFromFIFO=clockFromFIFO)
        if enableOffsetCurrent is None:
            enableOffsetCurrent = 0
        if offsetCurrent is None:
            offsetCurrent = 0
        else:
            offsetCurrent = int(
                "{:04b}".format(offsetCurrent)[::-1],
                2)  # need to reverse bits, use string/list tricks
        if testInput is None:
            testInput = 1
        if freqInternal is None:
            freqInternal = 1
        if sleep is None:
            sleep = 0
        if pdsr is None:
            pdsr = 1
        if pcsr is None:
            pcsr = 1
        if not (clockMonostable or clockExternal or clockFromFIFO):
            clockFromFIFO = True
        clk0 = 0
        clk1 = 0
        if clockExternal:
            clk0 = 1
            clk1 = 0
        elif clockFromFIFO:
            clk0 = 0
            clk1 = 1
        self.adc_reg.set_sbnd_board(en_gr=enableOffsetCurrent,
                                    d=offsetCurrent,
                                    tstin=testInput,
                                    frqc=freqInternal,
                                    slp=sleep,
                                    pdsr=pdsr,
                                    pcsr=pcsr,
                                    clk0=clk0,
                                    clk1=clk1,
                                    f1=f1,
                                    f2=f2,
                                    res2=0,
                                    res1=1,
                                    res0=1)
        self.configAdcAsic_regs(self.adc_reg.REGS)

        #regs = [
        # 0xC0C0C0C,
        # 0xC0C0C0C,
        # 0xC0C0C0C,
        # 0xC0C0C0C,
        # 0xC0C0C0C,
        # 0xC0C0C0C,
        # 0xC0C0C0C,
        # 0xC0C0C0C,
        # 0x18321832,
        # 0x18181818,
        # 0x18181818,
        # 0x18181818,
        # 0x18181818,
        # 0x18181818,
        # 0x18181818,
        # 0x18181818,
        # 0x64186418,
        # 0x30303030,
        # 0x30303030,
        # 0x30303030,
        # 0x30303030,
        # 0x30303030,
        # 0x30303030,
        # 0x30303030,
        # 0x30303030,
        # 0x60c868c8,
        # 0x60606868,
        # 0x60606868,
        # 0x60606868,
        # 0x60606868,
        # 0x60606868,
        # 0x60606868,
        # 0x60606868,
        # 0x9060A868,
        # 0x10001,
        #]
        #self.configAdcAsic_regs(regs)

    def selectChannel(self, asic, chan, hsmode=None):
        asicVal = int(asic)
        if (asicVal < 0) or (asicVal >= self.NASICS):
            print(
                "femb_config_femb : selectChan - invalid ASIC number, only 0 to {} allowed"
                .format(self.NASICS - 1))
            return
        chVal = int(chan)
        if (chVal < 0) or (chVal > 15):
            print(
                "femb_config_femb : selectChan - invalid channel number, only 0 to 15 allowed"
            )
            return

        #print( "Selecting ASIC " + str(asicVal) + ", channel " + str(chVal))

        regVal = (chVal << 8) + asicVal
        self.femb.write_reg(self.REG_SEL_CH, regVal)

    def syncADC(self):
        #turn on ADC test mode
        print("Start sync ADC")
        reg3 = self.femb.read_reg(3)
        newReg3 = (reg3 | 0x80000000)
        self.femb.write_reg(3, newReg3)  #31 - enable ADC test pattern
        alreadySynced = True
        for a in range(0, self.NASICS, 1):
            print("Test ADC " + str(a))
            unsync = self.testUnsync(a)
            if unsync != 0:
                alreadySynced = False
                print("ADC not synced, try to fix")
                self.fixUnsync(a)
        LATCH = self.femb.read_reg(self.REG_LATCHLOC)
        PHASE = self.femb.read_reg(self.REG_CLKPHASE)
        self.femb.write_reg(3, reg3)  # turn off adc test pattern
        self.femb.write_reg(3, reg3)  # turn off adc test pattern
        print("Latch latency {:#010x} Phase: {:#010x}".format(LATCH, PHASE))
        print("End sync ADC")
        return not alreadySynced, LATCH, None, PHASE

    def testUnsync(self, adc):
        adcNum = int(adc)
        if (adcNum < 0) or (adcNum >= self.NASICS):
            print("femb_config_femb : testLink - invalid asic number")
            return

        #loop through channels, check test pattern against data
        badSync = 0
        for ch in range(0, 16, 1):
            self.selectChannel(adcNum, ch)
            time.sleep(0.1)
            for test in range(0, 1000, 1):
                data = self.femb.get_data(1)
                for samp in data:
                    chNum = ((samp >> 12) & 0xF)
                    sampVal = (samp & 0xFFF)
                    if sampVal != self.ADC_TESTPATTERN[ch]:
                        badSync = 1
                    if badSync == 1:
                        break
                if badSync == 1:
                    break
            if badSync == 1:
                break
        return badSync

    def fixUnsync(self, adc):
        adcNum = int(adc)
        if (adcNum < 0) or (adcNum >= self.NASICS):
            print("femb_config_femb : testLink - invalid asic number")
            return

        initLATCH = self.femb.read_reg(self.REG_LATCHLOC)
        initPHASE = self.femb.read_reg(self.REG_CLKPHASE)

        #loop through sync parameters
        for phase in range(0, 2, 1):
            clkMask = (0x1 << adcNum)
            testPhase = ((initPHASE & ~(clkMask)) | (phase << adcNum))
            self.femb.write_reg(self.REG_CLKPHASE, testPhase)
            for shift in range(0, 16, 1):
                shiftMask = (0xF << 4 * adcNum)
                testShift = ((initLATCH & ~(shiftMask)) |
                             (shift << 4 * adcNum))
                self.femb.write_reg(self.REG_LATCHLOC, testShift)
                #reset ADC ASIC
                self.femb.write_reg(self.REG_ASIC_RESET, 1)
                time.sleep(0.1)
                self.femb.write_reg(self.REG_ASIC_SPIPROG, 1)
                time.sleep(0.1)
                self.femb.write_reg(self.REG_ASIC_SPIPROG, 1)
                time.sleep(0.1)
                #test link
                unsync = self.testUnsync(adcNum)
                if unsync == 0:
                    print("ADC synchronized")
                    return
        #if program reaches here, sync has failed
        print("ADC SYNC process failed for ADC # " + str(adc))
class FEMB_CONFIG(FEMB_CONFIG_BASE):

    def __init__(self,exitOnError=True):
        super().__init__(exitOnError=exitOnError)
        #declare board specific registers
        self.FEMB_VER = "adctestP1single"
        self.REG_RESET = 0
        self.REG_ASIC_RESET = 1
        self.REG_ASIC_SPIPROG = 2
        self.REG_SEL_CH = 7
        self.REG_HS = 17
        self.REG_FESPI_BASE = 0x250 # 592 in decimal
        self.REG_ADCSPI_BASE = 0x200 # 512 in decimal
        self.REG_FESPI_RDBACK_BASE = 0x278 # 632 in decimal
        self.REG_ADCSPI_RDBACK_BASE = 0x228 # 552 in decimal
        self.REG_LATCHLOC1_4 = 4
        self.REG_LATCHLOC5_8 = 14
        self.REG_CLKPHASE = 6

        self.REG_LATCHLOC1_4_data = 0x6
        self.REG_LATCHLOC5_8_data = 0x0
        self.REG_CLKPHASE_data = 0xfffc0000

        self.REG_LATCHLOC1_4_data_1MHz = 0x5
        self.REG_LATCHLOC5_8_data_1MHz = 0x0
        self.REG_CLKPHASE_data_1MHz = 0xffff0000

        self.REG_LATCHLOC1_4_data_cold = 0x6
        self.REG_LATCHLOC5_8_data_cold = 0x0
        self.REG_CLKPHASE_data_cold = 0xfffc0000

        self.REG_LATCHLOC1_4_data_1MHz_cold = 0x4
        self.REG_LATCHLOC5_8_data_1MHz_cold = 0x0
        self.REG_CLKPHASE_data_1MHz_cold = 0xfffc0001

        self.ADC_TESTPATTERN = [0x12, 0x345, 0x678, 0xf1f, 0xad, 0xc01, 0x234, 0x567, 0x89d, 0xeca, 0xff0, 0x123, 0x456, 0x789, 0xabc, 0xdef]

        ##################################
        # external clock control registers
        ##################################
        self.FPGA_FREQ_MHZ = 200 # frequency of FPGA clock in MHz
        self.REG_EXTCLK_RD_EN_OFF = 23
        self.REG_EXTCLK_ADC_OFF = 21
        self.REG_EXTCLK_ADC_WID = 22
        self.REG_EXTCLK_MSB_OFF = 25
        self.REG_EXTCLK_MSB_WID = 26
        self.REG_EXTCLK_PERIOD = 20
        self.REG_EXTCLK_LSB_FC_WID2 = 32
        self.REG_EXTCLK_LSB_FC_OFF1 = 29
        self.REG_EXTCLK_RD_EN_WID = 24
        self.REG_EXTCLK_LSB_FC_WID1 = 30
        self.REG_EXTCLK_LSB_FC_OFF2 = 31
        self.REG_EXTCLK_LSB_S_WID = 28
        self.REG_EXTCLK_LSB_S_OFF = 27
        self.REG_EXTCLK_INV = 33
        ##################################
        ##################################

        self.NASICS = 1
        self.FUNCGENINTER = Keysight_33600A("/dev/usbtmc1",1)
        self.POWERSUPPLYINTER = RigolDP800("/dev/usbtmc0",["CH2","CH3","CH1"]) # turn on CH2 first
        self.F2DEFAULT = 0
        self.CLKDEFAULT = "fifo"

        ## Firmware update related variables
        self.FIRMWAREPATH2MHZ = "/home/oper/Documents/CarlosForkedRepo/femb_python/femb_python/test_measurements/adc_clock_test/code/S_SKT_ADC_CHP_TST.sof"
        #self.FIRMWAREPATH1MHZ = "/opt/sw/releases/femb_firmware-0.1.0/adc_tester/S7_1M_SBND_FPGA.sof"
        self.FIRMWAREPROGEXE = "/opt/sw/intelFPGA/17.0/qprogrammer/bin/quartus_pgm"
        #self.FIRMWAREPROGCABLE = "USB-Blaster"
        self.FIRMWAREPROGCABLE = "USB-BlasterII"
        self.SAMPLERATE = 2e6

        #initialize FEMB UDP object
        self.femb = FEMB_UDP()
        self.adc_reg = ADC_ASIC_REG_MAPPING()

    def resetFEMBBoard(self): #cp 1/17/18
        #
        #   resetFEMBBoard()
        #       sends a 1 to register 1 for "instantaneous" ASIC reset.
        #       sends a 0 to register 1 to ensure reset is not locked in.
        #
        #       procedure:
        #           line 29: FPGA to ASIC reset
        #           line 30: wait 5 ms
        #           line 31: FPGA to ASIC disable reset
        #
        
        print (" FEMB_CONFIG--> Reset FEMB (10 seconds) --")
        #Reset FEMB system
        self.femb.write_reg ( self.REG_RESET, 1)
        time.sleep(5.)
        self.femb.write_reg ( self.REG_RESET, 0)
        time.sleep(2.)
        print (" FEMB_CONFIG--> Reset FEMB is DONE\n")
        

    def initBoard(self):
        """
        % initBoard()
        % 	set up default registers. 
        % 	first wave in setting clock settings.
        """
        
        print ("FEMB_CONFIG--> initBoard() -> Initialize FEMB --")
        
        # Frame size is multiple of 13, so 0xFACE is consistently the first 2 bytes.
        frame_size = settings.frame_size
        if (frame_size%13 != 0):
            frame_size = 13 * (frame_size//13)
            
        self.femb.write_reg(10, frame_size)
        time.sleep(0.1)
    
        if (self.femb.read_reg(10) != frame_size):
            sys.exit("FEMB_CONFIG--> initBoard() -> Frame Size not set correctly, something wrong with FPGA communication")
        
        print ("FEMB_CONFIG--> initBoard() -> Chip tester version {}".format(hex(self.femb.read_reg(0x101))))

        # Set to WIB Mode and start by reading out chip 1
        # Channel Setting is irrelevant in WIB mode
        self.femb.write_reg(8, 0x80000001)                  # WIB_MODE   <= reg8_p(0)
        self.femb.write_reg(7, 0x80000000)                  # CHN_select <= reg7_p(7 downto 0)

        """ SHIFT DATA BUS """
        # LATCH_LOC_0 <= reg4_p(7 downto 0)
        self.femb.write_reg(settings.LATCHLOC_reg, settings.LATCHLOC_data)
        
        """ SHIFT DATA BUS """
        # CLK_selectx <= reg6_p(7 downto 0)
        self.femb.write_reg(settings.CLKPHASE_reg, settings.CLKPHASE_data)
        
#        self.femb.write_reg( 9, 0)

        # Write Coarse Clock Control
        self.femb.write_reg(21, settings.reg21_value[0])
        self.femb.write_reg(21, settings.reg21_value[1])
        self.femb.write_reg(21, settings.reg21_value[2])
        self.femb.write_reg(21, settings.reg21_value[3])
        self.femb.write_reg(21, settings.reg21_value[4])
        self.femb.write_reg(21, settings.reg21_value[5])

        self.femb.write_reg(22, settings.reg22_value[0])    # RESET Offset      
        self.femb.write_reg(23, settings.reg23_value[0])    # RESET Width
        
        self.femb.write_reg(24, settings.reg24_value[0])    # READ Offset
        self.femb.write_reg(25, settings.reg25_value[0])    # READ Width
        
        self.femb.write_reg(26, settings.reg26_value[0])    # IDXM Offset
        self.femb.write_reg(27, settings.reg27_value[0])    # IDXM Width
        
        self.femb.write_reg(28, settings.reg28_value[0])    # IDXL Offset
        self.femb.write_reg(29, settings.reg29_value[0])    # IDXL Width
        
        self.femb.write_reg(30, settings.reg30_value[0])    # IDL1 Offset
        self.femb.write_reg(31, settings.reg31_value[0])    # IDL1 Width
                                                            
        self.femb.write_reg(32, settings.reg32_value[0])    # IDL2 Offset
        self.femb.write_reg(33, settings.reg33_value[0])    # IDL2 Width

        #Fine Control
        self.femb.write_reg(34, settings.reg34_value[0])    # C0 & C1 fine clock settings   
        self.femb.write_reg(35, settings.reg35_value[0])    # C2 & C3 fine clock settings
        self.femb.write_reg(36, settings.reg36_value[0])    # C2 & C3 fine clock settings

        #set default value to FEMB ADCs
        #clk = 2 is external
        #clk = 0 is internal
        self.adc_reg.set_adc_board( d=0, pcsr=0, pdsr=0, slp=0, tstin=1,
                 clk = 0, frqc = 1, en_gr = 0, f0 = 0, f1 = 0, 
                 f2 = 0, f3 = 0, f4 = 0, f5 = 1, slsb = 0) # set to internal 1/31/18 cp
                 
        self.femb.write_reg ( self.REG_ASIC_SPIPROG, 0x30)
        self.femb.write_reg ( self.REG_ASIC_SPIPROG, 0)
        self.configAdcAsic()
        
        print (" FEMB_CONFIG--> initBOARD() -> Initialize FEMB is DONE\n")
    """    
    def syncADC(self):
        #   syncADC()
        #
        #       procedures:
        #           1.  set channel/global registers ensure F5=1, so
        #               test data is selected and pipelined. Just as 
        #               in labview. 
        #           2.  configure ADC ASIC 
        #           3.

        
        print ("\n FEMB_CONFIG--> Start sync ADC--")


        self.select_chn(1) # channel 0 & write clocks
        self.select_chn(3) # channel 0 & write clocks
        self.select_chn(1) # channel 0 & write clocks
        self.adc_reg.set_adc_global(chip = settings.chip_num, f5 = 1) # Test DATA
        self.configAdcAsic()
            
        print (" FEMB_CONFIG --> syncADC() -> Test ADC {}".format(settings.chip_num))
        print (" FEMB_CONFIG --> syncADC() -> self.adc_reg.set_adc_global() \n")
#        for i in range(10,20,1):
#        print ("Register {} {}".format(i, hex(self.femb.read_reg(i))))
            
        unsync = self.testUnsyncNew(settings.chip_num)
        if unsync != True:
            print (" FEMB_CONFIG --> ADC {} not synced, try to fix".format(settings.chip_num))
            response = self.fixUnsyncNew(a)
            if (response != True):
                sys.exit (" FEMB_CONFIG --> ADC {} could not sync".format(settings.chip_num))
        elif (unsync == True):
            print ("FEMB_CONFIG--> ADC {} synced!".format(settings.chip_num))
                
            self.adc_reg.set_adc_global(chip = settings.chip_num, f5 = 1)
            self.configAdcAsic()
            
        self.REG_LATCHLOC1_4_data = self.femb.read_reg( settings.LATCHLOC_reg) 
        self.REG_CLKPHASE_data    = self.femb.read_reg( settings.CLKPHASE_reg)
        print ("FEMB_CONFIG--> Final Latch latency " + str(hex(self.REG_LATCHLOC1_4_data)))
        print ("FEMB_CONFIG--> Final Phase Shift " + str(hex(self.REG_CLKPHASE_data)))
        self.FinalSyncCheck()
        print ("FEMB_CONFIG--> ADC passed Sync Test!")
    """
    def syncADC(self,iASIC=None):
        #turn on ADC test mode
        print("FEMB_CONFIG--> Start sync ADC")
        reg3 = self.femb.read_reg (3)
        newReg3 = ( reg3 | 0x80000000 )

        self.femb.write_reg ( 3, newReg3 ) #31 - enable ADC test pattern
        time.sleep(0.1)                

        alreadySynced = True
        for a in range(0,self.NASICS,1):
            print("FEMB_CONFIG--> Test ADC " + str(a))
            unsync, syncDicts = self.testUnsync(a)
            if unsync != 0:
                alreadySynced = False
                print("FEMB_CONFIG--> ADC not synced, try to fix")
                self.fixUnsync(a)
        latchloc1_4 = self.femb.read_reg ( self.REG_LATCHLOC1_4 ) 
        latchloc5_8 = self.femb.read_reg ( self.REG_LATCHLOC5_8 )
        clkphase    = self.femb.read_reg ( self.REG_CLKPHASE )
        if self.SAMPLERATE == 1e6:
            if self.COLD:
                self.REG_LATCHLOC1_4_data_1MHz_cold = latchloc1_4
                self.REG_LATCHLOC5_8_data_1MHz_cold = latchloc5_8
                self.REG_CLKPHASE_data_1MHz_cold    = clkphase
            else:
                self.REG_LATCHLOC1_4_data_1MHz = latchloc1_4
                self.REG_LATCHLOC5_8_data_1MHz = latchloc5_8
                self.REG_CLKPHASE_data_1MHz    = clkphase
        else: # 2 MHz
            if self.COLD:
                self.REG_LATCHLOC1_4_data_cold = latchloc1_4
                self.REG_LATCHLOC5_8_data_cold = latchloc5_8
                self.REG_CLKPHASE_data_cold    = clkphase
            else:
                self.REG_LATCHLOC1_4_data = latchloc1_4
                self.REG_LATCHLOC5_8_data = latchloc5_8
                self.REG_CLKPHASE_data    = clkphase
        print("FEMB_CONFIG--> Latch latency {:#010x} {:#010x} Phase: {:#010x}".format(latchloc1_4,
                        latchloc5_8, clkphase))
        self.femb.write_reg ( 3, (reg3&0x7fffffff) )
        self.femb.write_reg ( 3, (reg3&0x7fffffff) )
        print("FEMB_CONFIG--> End sync ADC")
        return not alreadySynced,latchloc1_4,latchloc5_8 ,clkphase

    def testUnsync(self, adc, npackets=10):
        print("Starting testUnsync adc: ",adc)
        adcNum = int(adc)
        if (adcNum < 0 ) or (adcNum > 7 ):
                print("FEMB_CONFIG--> femb_config_femb : testLink - invalid asic number")
                return

        #loop through channels, check test pattern against data
        syncDataCounts = [{} for i in range(16)] #dict for each channel
        for ch in range(0,16,1):
                self.selectChannel(adcNum,ch, 1)
                time.sleep(0.05)                
                data = self.femb.get_data(npackets)
                if data == None:
                    continue
                for samp in data:
                        if samp == None:
                                continue
                        #chNum = ((samp >> 12 ) & 0xF)
                        sampVal = (samp & 0xFFF)
                        if sampVal in syncDataCounts[ch]:
                            syncDataCounts[ch][sampVal] += 1
                        else:
                            syncDataCounts[ch][sampVal] = 1
        # check jitter
        badSync = 0
        maxCodes = [None]*16
        syncDicts = [{}]*16
        for ch in range(0,16,1):
            sampSum = 0
            maxCode = None
            nMaxCode = 0
            for code in syncDataCounts[ch]:
                nThisCode = syncDataCounts[ch][code]
                sampSum += nThisCode
                if nThisCode > nMaxCode:
                    nMaxCode = nThisCode
                    maxCode = code
            maxCodes[ch] = maxCode
            syncDicts[ch]["maxCode"] = maxCode
            syncDicts[ch]["nSamplesMaxCode"] = nMaxCode
            syncDicts[ch]["nSamples"] = sampSum
            syncDicts[ch]["zeroJitter"] = True
            if len(syncDataCounts[ch]) > 1:
                syncDicts[ch]["zeroJitter"] = False
                badSync = 1
                diff = sampSum-nMaxCode
                frac = diff / float(sampSum)
                print("Sync Error: Jitter for Ch {:2}: {:8.4%} ({:5}/{:5})".format(ch,frac,diff,sampSum))
        for ch in range(0,16,1):
            maxCode = maxCodes[ch]
            correctCode = self.ADC_TESTPATTERN[ch]
            syncDicts[ch]["data"] = True
            syncDicts[ch]["maxCodeMatchesExpected"] = True
            if maxCode is None:
                syncDicts[ch]["data"] = False
                badSync = 1
                print("Sync Error: no data for ch {:2}".format(ch))
            elif maxCode != correctCode:
                syncDicts[ch]["maxCodeMatchesExpected"] = True
                badSync = 1
                print("Sync Error: mismatch for ch {:2}: expected {:#03x} observed {:#03x}".format(ch,correctCode,maxCode))
        return badSync, syncDicts

    def selectChannel(self,asic,chan,hsmode=1,singlechannelmode=None):
        """
        asic is chip number 0 to 7
        chan is channel within asic from 0 to 15
        hsmode: if 0 then streams all channels of a chip, if 1 only te selected channel. defaults to 1
        singlechannelmode: not implemented
        """
        hsmodeVal = int(hsmode) & 1;
        asicVal = int(asic)
        if (asicVal < 0 ) or (asicVal >= self.NASICS ) :
                print( "femb_config_femb : selectChan - invalid ASIC number, only 0 to {} allowed".format(self.NASICS-1))
                return
        chVal = int(chan)
        if (chVal < 0 ) or (chVal > 15 ) :
                print("femb_config_femb : selectChan - invalid channel number, only 0 to 15 allowed")
                return

        #print( "Selecting ASIC " + str(asicVal) + ", channel " + str(chVal))

        self.femb.write_reg ( self.REG_HS, hsmodeVal)
        regVal = (chVal << 8 ) + asicVal
        self.femb.write_reg( self.REG_SEL_CH, regVal)

    """
    def testUnsyncNew(self, chip):

        print("\n FEMB_CONFIG --> testUnsyncNew() -> chip = {} --".format(chip))        
        
        adcNum = int(chip)
        if (adcNum < 0 ) or (adcNum > 3 ):
            print (" FEMB_CONFIG --> testUnsyncNew() -> Invalid asic number, must be between 0 and 3")
            return
            
        for j in range(100):    #reset sync error
            self.femb.write_reg(11, 1) # ERROR_RESET <= reg11_p(0)
            time.sleep(0.01)
            self.femb.write_reg(11, 0) # ERROR_RESET <= reg11_p(0)
            time.sleep(0.01)
            
            if (chip == 0):
                conv_error = self.femb.read_reg(12)     # reg12_i => x"" & CONV_ERROR 
                header_error = self.femb.read_reg(13)   # reg13_i => HDR2_ERROR & HDR1_ERROR 
                
#            elif (chip == 1):
#                conv_error = self.femb.read_reg(50)    # reg50_i => x"0000" & CONV_ERROR_1
#                header_error = self.femb.read_reg(51)  # reg51_i => HDR2_ERROR_2 & HDR1_ERROR_2
#            elif (chip == 2):
#                conv_error = self.femb.read_reg(52)
#                header_error = self.femb.read_reg(53)
#            elif (chip == 3):
#                conv_error = self.femb.read_reg(54)
#                header_error = self.femb.read_reg(55)
            
            error = False
            
            if (conv_error != 0): # CONV_ERROR exists
                print (" FEMB_CONFIG --> testUnsyncNew() -> Convert error({})!  Trial {}".format(hex(conv_error),j))
                error = True
            else:
                print (" FEMB_CONFIG --> testUnsyncNew() -> No Convert Error ({})  Trial {}!".format(hex(conv_error),j)) #convert = 0
                
            if (header_error != 0): # HEADER_ERROR exists
                print (" FEMB_CONFIG --> testUnsyncNew() -> Header error ({})!".format(hex(header_error)))
                error = True
            else:
                print (" FEMB_CONFIG --> testUnsyncNew() -> No Header Error({})!  Trial {}".format(hex(header_error),j)) #not finding header
                
            if (error == False): #break loop if no error found
                print (" FEMB_CONFIG --> testUnsyncNew() -> Correct on Loop {}  Trial {}".format(j,j))
                break
                #return True
            elif (j > 30):
                print (" FEMB_CONFIG --> testUnsyncNew() -> Convert error({})!  Trial {}".format(hex(conv_error),j))
                print (" FEMB_CONFIG --> testUnsyncNew() -> Header error ({})!  Trial {}".format(hex(header_error),j))
                #sync_status = self.femb.read_reg(self.REG_ASIC_SPIPROG) >> 24
                #print ("FEMB_CONFIG--> Register 2 Sync status is {}".format(hex(sync_status)))
                return False
            else:
                self.configAdcAsic(False)
                #print ("Loop {}".format(j))
           
        for ch in range(0,16,1):
            for test in range(0,200,1):
                data = self.get_data_chipXchnX(chip = adcNum, chn = ch, packets = 1)#issue here?
                #print("unsyncNew data -> {}".format(data))
                if (len(data) == 0):
                    print (" FEMB_CONFIG--> testUnsyncNew() -> Sync response didn't come in")
                    return False
                for samp in data[0:len(data)]:
                    if samp != self.ADC_TESTPATTERN[ch]:
                        print (" FEMB_CONFIG --> testUnsyncNew() -> Chip {} chn {} looking for {} but found {}".format(
                                adcNum, ch, hex(self.ADC_TESTPATTERN[ch]), hex(samp)))
                        return False
                        
        return True
    """

    def initFunctionGenerator(self):
        """
        rm = visa.ResourceManager()
        try:
            #keysight = rm.open_resource('USB0::0x0957::0x5707::MY53802435::0::INSTR')
            keysight = rm.open_resource(u'USB0::2391::22279::MY53802422::0::INSTR')
            print ("Keysight Initialize--> Instrument Connected")
        except VisaIOError:
            print ("Keysight Initialize--> Exact system name not found")
            keysight = rm.open_resource(rm.list_resources()[0])
        
        #Sets the self.instrument object in "GPIB_FUNCTIONS" to the 
        #connected instrument, this makes it very easy to reference later
        time.sleep(0.1)
        keysight.write("Source1:Apply:Triangle {},{},{}".format(settings.frequency,settings.amplitude,settings.offset))
        keysight.write("Output1:Load 50")
        #keysight.write("Output1:Load INFINITY")
        keysight.write("Source1:Burst:Mode Triggered")
        keysight.write("Source1:Burst:Ncycles 1")
        keysight.write("Source1:Burst:Phase {}".format(settings.phase_start))
        keysight.write("Source1:Burst:State ON")
        keysight.write("Initiate1:Continuous OFF")
        
        return keysight
        """
    def configAdcAsic(self): #cp 1/29/18 #helper
        """
        config()
        
        """   
        print("FEMB_CONFIG -> configAdcAsic() --")    
    
        Adcasic_regs = self.adc_reg.REGS
        #ADC ASIC SPI registers
        self.femb.write_reg ( self.REG_ASIC_SPIPROG, 0x40)
        self.femb.write_reg ( self.REG_ASIC_SPIPROG, 0)
        time.sleep(0.01)
        self.femb.write_reg ( self.REG_ASIC_SPIPROG, 0x20)
        self.femb.write_reg ( self.REG_ASIC_SPIPROG, 0)
        time.sleep(0.01)
        self.femb.write_reg ( self.REG_ASIC_SPIPROG, 0x40)
        self.femb.write_reg ( self.REG_ASIC_SPIPROG, 0)
        time.sleep(0.01)

        self.select_chn(1) # channel 1 & write clocks
        self.select_chn(3) # channel 3 & write clocks
        self.select_chn(1) # channel 1 & write clocks

        for k in range(10):            
            i = 0
            for regNum in range(self.REG_ADCSPI_BASE,self.REG_ADCSPI_BASE+len(Adcasic_regs),1):
                    self.femb.write_reg( regNum, Adcasic_regs[i])
                    i = i + 1
                    
            # Program ADC ASIC SPI
            self.femb.write_reg ( self.REG_ASIC_SPIPROG, 0)
            time.sleep(.05)
            self.femb.write_reg ( self.REG_ASIC_SPIPROG, 1)
            time.sleep(.05)
            self.femb.write_reg ( self.REG_ASIC_SPIPROG, 1)
            time.sleep(.05)
            #self.femb.write_reg ( self.REG_ASIC_SPIPROG, 0)

            # Check ADC ASIC SPI
            readback_regs = []
            i = 0
            for regNum in range(self.REG_ADCSPI_RDBACK_BASE,self.REG_ADCSPI_RDBACK_BASE+len(Adcasic_regs),1):
                readback_regs.append(self.femb.read_reg(regNum))
                #print ( "femb_config_sbnd.py -> configAdcAsic() -> readback reg: " + str(hex(regNum)) + " from base: " + str(hex(Adcasic_regs[i])))
                i = i + 1
            
            i=0
            for i in range (0,len(readback_regs),1): #Why is this reading out extra adc asic registers?
                if (Adcasic_regs[i] != readback_regs[i]):
                    print ("\t FEMB_CONFIG --> CONFIG() ERROR -> configAdcAsic() -> Sent Adcasic_reg not correct = " + str(hex(Adcasic_regs[i])) + " , rb_reg = " + str(hex(readback_regs[i])) )
                else:
                    continue
                    #print ("femb_config_sbnd.py -> configAdcAsic() -> Adcasic_reg correct = " + str(hex(Adcasic_regs[i])) + " , rb_reg = " + str(hex(readback_regs[i])) )
            
#            val = self.femb.read_reg ( self.REG_ASIC_SPIPROG ) 
            wrong = False
#
#            if (((val & 0x10000) >> 16) != 1):
#                #print ("FEMB_CONFIG--> Something went wrong when programming ADC 1")
#                wrong = True
                
#            if (((val & 0x40000) >> 18) != 1):
#                #print ("FEMB_CONFIG--> Something went wrong when programming ADC 2")
#                wrong = True
                
#            if (((val & 0x100000) >> 20) != 1):
#                #print ("FEMB_CONFIG--> Something went wrong when programming ADC 3")
#                wrong = True
                
#            if (((val & 0x400000) >> 22) != 1):
#                #print ("FEMB_CONFIG--> Something went wrong when programming ADC 4")
#                wrong = True

            if (wrong == True and k == 9):
                print ("\tFEMB_CONFIG--> CONFIG() -> SPI_Status")
                print (hex(val))
                sys.exit("\tFEMB_CONFIG--> CONFIG() -> femb_config_femb : Wrong readback. ADC SPI failed")
                return
                
            elif (wrong == 0): 
                print ("\tFEMB_CONFIG--> CONFIG() -> ADC ASIC SPI is OK")
                break
            #else:
                #print ("FEMB_CONFIG--> Try {}, since SPI response was {}".format(k + 2, hex(val)))

    def FinalSyncCheck(self):
        #
        #   FinalSyncCheck()
        #
        #       procedures:   
        #           line 337: set chip [determine which chip is being sync'd]
        #           line 339: set global register [send test data from PC to FPGA (where is this data going?)  F5=1  
        #           line 340: write SPI (wtf is SPI?)
        
        print ("FEMB_CONFIG--> Final sync check to make sure")
        for a in settings.chips_to_use:
            
            self.adc_reg.set_adc_global(chip = a, f5 = 1)
            self.configAdcAsic()
            self.select_chn(1) # channel 0 & write clocks
            self.select_chn(3) # channel 0 & write clocks
            self.select_chn(1) # channel 0 & write clocks
            # quad board settings:
            #self.select_chip(chip = a)
            
            # single board settings:
            #select_chip is not necessary

            badSync = 0
            for ch in range(0,16,1):
                for test in range(0,100,1):
                    data = self.get_data_chipXchnX(chip = a, chn = ch, packets = 1)
                    if (len(data) == 0):
                        print ("FEMB_CONFIG--> Sync response bad.  Exiting...")
                        return 1
                    for samp in data[0:len(data)]:
                        if samp != self.ADC_TESTPATTERN[ch]:
                            badSync = 1 
                            print ("FEMB_CONFIG--> Chip {} chn {} looking for {} but found {}".format(
                                    a, ch, hex(self.ADC_TESTPATTERN[ch]), hex(samp)))
                        if badSync == 1:
                                break
                    #print("time after checking the sample {}".format(time.time()))
                    if badSync == 1:
                        break
                #print("time after checking 100 samples {}".format(time.time()))
                if badSync == 1:
                    self.femb.write_reg( 9, 1)
                    sys.exit("FEMB_CONFIG--> Failed final check looking at channel test values")

            self.configAdcAsic()
            print ("FEMB_CONFIG--> Chip {} recieved the correct test values!".format(a))
        
            resp = self.testUnsyncNew(a)
            if (resp == False):
                self.femb.write_reg(9, 1)
                sys.exit("FEMB_CONFIG--> Sync failed in the final check")                
                
        self.adc_reg.set_adc_global(chip = a, f5 = 0)
#        for i in range (100):
#            sync_status = self.femb.read_reg(self.REG_ASIC_SPIPROG) >> 24
#            if ((sync_status & 0x3F) != 0):
#
#                if (i == 99):
#                    print ("FEMB_CONFIG--> Register 2 giving back a sync error")
#                    print ("Sync status is {}".format(hex(sync_status)))
#                    sys.exit("Done with trying")
#
#                self.configAdcAsic(False)
#                
#            else:
#                print ("FEMB_CONFIG--> No Sync error through Register 2 check: ({})!".format(hex(sync_status)))
#                break

            
    def fixUnsyncNew(self, adc):

        print("\n femb_config_sbnd.py -> fixUnsyncNew() -> adc = " + str(adc) + "\n")        
        
        adcNum = int(adc)
        if (adcNum < 0) or (adcNum > 3):
                print ("FEMB_CONFIG--> femb_config_femb : testLink - invalid asic number")
                return

        initLATCH1_4 = self.femb.read_reg( settings.LATCHLOC_reg)
        initPHASE = self.femb.read_reg( settings.CLKPHASE_reg)
        
        #loop through sync parameters
        shiftMask = (0xFF << 8*adcNum)
        initSetting = (initLATCH1_4 & shiftMask) >> (8*adcNum)
        print ("FEMB_CONFIG--> First testing around default value of {}".format(initSetting))
        for phase in range(0,4,1):
            clkMask = (0x3 << (adcNum * 2))
            testPhase = ( (initPHASE & ~(clkMask)) | (phase << (adcNum * 2)) ) 
            self.femb.write_reg( settings.CLKPHASE_reg, testPhase)
            #print ("Init Setting is {}".format(hex(initSetting)))
            #print ("Will test {}, {}, and {}".format(initSetting - 1, initSetting, initSetting + 1))
            for shift in range(initSetting - 1,initSetting + 2,1):
                print ("\n\tfemb_config_sbnd.py -> fixUnsyncNew -> This time, we're testing {}\n".format(shift))
                testShift = ( (initLATCH1_4 & ~(shiftMask)) | (shift << 8*adcNum) )
                self.femb.write_reg( settings.LATCHLOC_reg, testShift)
                print ("FEMB_CONFIG--> Trying to sync Chip {} with Latch Lock:{} and Phase:{}".format(adcNum, 
                       hex(testShift), hex(testPhase)))
                       
                #test link
                unsync = self.testUnsyncNew(adcNum)
                if unsync == True :
                    print ("FEMB_CONFIG--> ADC {} synchronized".format(adc))
                    self.REG_LATCHLOC1_4_data = testShift
                    self.REG_CLKPHASE_data = testPhase
                    return True
            #Then try all settings
        print ("FEMB_CONFIG--> Now testing the rest of them")
        for phase in range(0,4,1):
            clkMask = (0x3 << (adcNum * 2))
            testPhase = ( (initPHASE & ~(clkMask)) | (phase << (adcNum * 2)) ) 
            self.femb.write_reg( settings.CLKPHASE_reg, testPhase)
            #First test latch lock settings close to default values
            shiftMask = (0xFF << 8*adcNum)
            initSetting = initLATCH1_4 & shiftMask
            for shift in range(0,16,1):
                testShift = ( (initLATCH1_4 & ~(shiftMask)) | (shift << 8*adcNum) )
                self.femb.write_reg( settings.LATCHLOC_reg, testShift)
                print ("FEMB_CONFIG--> Trying to sync Chip {} with Latch Lock:{} and Phase:{}".format(adcNum, 
                       hex(testShift), hex(testPhase)))
                       
                #test link
                unsync = self.testUnsyncNew(adcNum)
                if unsync == True :
                    print ("FEMB_CONFIG--> ADC {} synchronized".format(adc))
                    self.REG_LATCHLOC1_4_data = testShift
                    self.REG_CLKPHASE_data = testPhase
                    return True
        #if program reaches here, sync has failed
        print ("FEMB_CONFIG--> ADC SYNC process failed for ADC # " + str(adc))
        return False
    
    def select_chn(self, chn): 
        """
        % select_chn()
        %       This function is used in sending instructions to fpga to select chip.
        %       There is not an option to select a chip for single socket boards. 
        %       This function is mostly relevent for using on quad board.
        %       This function is used to output the desired clock settings correlated to the    
        %       'selected chip'
        %       Assume 16 channels
        %
        %       [quad board:]
        %           clock reg values [10-43]   
        % 
        %       [single board:]
        %           clock reg values [21-33]
        """
        print("\t FEMB_CONFIG//select_chn()")
        if (chn < 0 ) or (chn > settings.chn_num):
            print ("\t FEMB_CONFIG//select_chn()//Error: Chn must be between 0 and {}".format(self.chn_num))
            return
        """ quad board remains...
        self.femb.write_reg(9, 1)		  # STOP_ADC <= reg9_p (quad board)
        time.sleep(0.01)                          # WAIT
        self.femb.write_reg(3, 0x80000001 + chip) # CHP_SELECT <= reg3_p(7 downto 0) (quad board) 
        time.sleep(0.01)                          # WAIT
        self.femb.write_reg(9, 0)		  # STOP_ADC <= reg9_p (quad board)
        time.sleep(0.01)                          # WAIT
        self.femb.write_reg(47, 1)                # ERROR_RESET <= reg47_p (quad board)
        time.sleep(0.01)                          # WAIT
        self.femb.write_reg(47, 0)                # ERROR_RESET <= reg47_p (quad board)
        """
        # STOP ADC: TRUE ???
        time.sleep(0.01)                         # WAIT
        self.femb.write_reg(7, 0x00000001 + chn) # CHN_SELECT <= reg7(7 downto 0)
        time.sleep(0.01)                         # WAIT
	# STOP ADC: FALSE ???
	# WAIT
        self.femb.write_reg(11, 1)               # CHN_SELECT <= reg11(0)
        time.sleep(0.01)                         # WAIT
        self.femb.write_reg(11, 0)               # CHN_SELECT <= reg11(0)

        self.selected_chn = chn # originally self.selected_chip?? what is it used for?
        if (self.femb.read_reg(7) != 0x00000001 + chn):
            print ("\t FEMB CONFIG --> select_chn() -> Error - chip not chosen correctly!")
            print ("\t Should be ".format(hex(0x00000001 + chn)))
            print ("\t It is {}".format(hex(self.femb.read_reg(7))))
            
        to_add = 0
        """ quad board remains...
        if (settings.extended == True):
            to_add = 4

        self.femb.write_reg(10, settings.reg10_value[chip + to_add]) #INV_RST_ADC1 <= reg10_p(0)
							             #INV_READ_ADC1<= reg10_p(1)
        #Course Control Quad Board
        self.femb.write_reg(11, settings.reg11_value[chip + to_add])
        self.femb.write_reg(12, settings.reg12_value[chip + to_add])
        self.femb.write_reg(13, settings.reg13_value[chip + to_add])
        self.femb.write_reg(14, settings.reg14_value[chip + to_add])
        self.femb.write_reg(15, settings.reg15_value[chip + to_add])
        self.femb.write_reg(16, settings.reg16_value[chip + to_add])
        
        #Fine Control Quad Board
        self.femb.write_reg(17, settings.reg17_value[chip + to_add])
        self.femb.write_reg(18, settings.reg18_value[chip + to_add])
        self.femb.write_reg(19, settings.reg19_value[chip + to_add])
        self.femb.write_reg(19, settings.reg19_value[chip + to_add])
        self.femb.write_reg(19, 0x80000000 + settings.reg19_value[chip + to_add])
        """
        self.femb.write_reg( settings.CLKPHASE_reg, 0xFF)
        self.femb.write_reg( settings.CLKPHASE_reg, self.REG_CLKPHASE_data)
        
        time.sleep(0.01)


    def get_data_chipXchnX(self, chip, chn, packets = 1):
        
        if (chn < -1 ) or (chn > settings.chn_num ):
            print ("FEMB CONFIG --> get_data_chipXchnX() -> Error: Channel must be between 0 and 15, or -1 for all channels")
            return
        
        if (chip < 0 ) or (chip > settings.chip_num ):
            print ("FEMB CONFIG --> get_data_chipXchnX() -> Error: Chip must be between 0 and {}".format(self.chip_num))
            return

        k = 0
        for i in range(10):
            
            data = self.femb.get_data_packets(data_type = "int", num = packets, header = False)
            
            try:
                if (k > 0):
                    print ("FEMB CONFIG --> Now doing another test")
                    print (hex(data[0]))
                    print (data[0] == 0xFACE)
                    print (data[0] != 0xFACE)
                if (data[0] != 0xFACE):
                #If FACE isn't the first 2 bytes, turn WIB mode off and then on and try again
                    self.femb.write_reg(8,0) # Turn WIB Mode Off
                    time.sleep(0.01)
                    self.femb.write_reg(8,1) # Turn WIB Mode On
                    time.sleep(0.01)
                    
                # quad board settings:
#                    self.select_chip(chip) #tells fpga which chip information to provide
#                    self.femb.write_reg(3, chip+1)  # CHP_Select <= reg_3p(7-0)
#                                                    # CHN_Select <= reg_3p(15-0)
#                                                    # WIB_Mode <= reg_3p(31)
                # single board settings:
                    self.femb.write_reg(7, chn)      # CHN_select <= reg7_p(7-0)       
                    time.sleep(0.001)

                    if (k > 8):
                        print ("FEMB CONFIG --> Error in get_data_chipXchnX: Packet format error")
                        #print (hex(data[0]))
                        #print (data)
                        return None
                    else:
                        print ("FEMB CONFIG --> Error in get_data_chipXchnX: Packet format error, trying again...")
                        print ("k = {}".format(k))
                        print (data[0:13])
                        print (hex(data[0]))
                        print ("FEMB CONFIG --> Hey: {}".format(data[0] == 0xFACE))
                        k += 1
                else:
                    break
            except IndexError:
                print ("FEMB CONFIG --> Something was wrong with the incoming data")
                print (data)
            
        test_length = len(data)
        
#        if ((test_length % self.BPS) != 0):
#            print ("FEMB CONFIG -> Error in get_data_chipXchnX: Irregular packet size")
#            print (data)
#            return None
        
        full_samples = test_length // self.BPS
        
        chn_data = []
        
        for i in range (full_samples):
            if (chn == 7):
                chn_data.append(data[(self.BPS*i)+1] & 0x0FFF)
            if (chn == 6):
                chn_data.append(((data[(self.BPS*i)+2] & 0x00FF) << 4) + ((data[(self.BPS*i)+1] & 0xF000) >> 12))
            if (chn == 5):
                chn_data.append(((data[(self.BPS*i)+3] & 0x000F) << 8) + ((data[(self.BPS*i)+2] & 0xFF00) >> 8))
            if (chn == 4):
                chn_data.append(((data[(self.BPS*i)+3] & 0xFFF0) >> 4))
            if (chn == 3):
                chn_data.append(data[(self.BPS*i)+4] & 0x0FFF)
            if (chn == 2):
                chn_data.append(((data[(self.BPS*i)+5] & 0x00FF) << 4) + ((data[(self.BPS*i)+4] & 0xF000) >> 12))
            if (chn == 1):
                chn_data.append(((data[(self.BPS*i)+6] & 0x000F) << 8) + ((data[(self.BPS*i)+5] & 0xFF00) >> 8))
            if (chn == 0):
                chn_data.append(((data[(self.BPS*i)+6] & 0xFFF0) >> 4))
            if (chn == 15):
                chn_data.append(data[(self.BPS*i)+7] & 0x0FFF)
            if (chn == 14):
                chn_data.append(((data[(self.BPS*i)+8] & 0x00FF) << 4) + ((data[(self.BPS*i)+7] & 0xF000) >> 12))
            if (chn == 13):
                chn_data.append(((data[(self.BPS*i)+9] & 0x000F) << 8) + ((data[(self.BPS*i)+8] & 0xFF00) >> 8))
            if (chn == 12):
                chn_data.append(((data[(self.BPS*i)+9] & 0xFFF0) >> 4))
            if (chn == 11):
                chn_data.append(data[(self.BPS*i)+10] & 0x0FFF)
            if (chn == 10):
                chn_data.append(((data[(self.BPS*i)+11] & 0x00FF) << 4) + ((data[(self.BPS*i)+10] & 0xF000) >> 12))
            if (chn == 9):
                chn_data.append(((data[(self.BPS*i)+12] & 0x000F) << 8) + ((data[(self.BPS*i)+11] & 0xFF00) >> 8))
            if (chn == 8):
                chn_data.append(((data[(self.BPS*i)+12] & 0xFFF0) >> 4))
            if (chn == -1):
                return (data)
            
        return chn_data
class FEMB_CONFIG(FEMB_CONFIG_BASE):
    def __init__(self, exitOnError=True):
        super().__init__(exitOnError=exitOnError)
        #declare board specific registers
        self.FEMB_VER = "adctestP1quad"

        self.REG_RESET = 0  # bit 0 system, 1 reg, 2 alg, 3 udp
        self.REG_PWR_CTRL = 1  # bit 0-3 pwr, 8-15 blue LEDs near buttons
        self.REG_ASIC_SPIPROG_RESET = 2  # bit 0 FE SPI, 1 ADC SPI, 4 FE ASIC RESET, 5 ADC ASIC RESET, 6 SOFT ADC RESET & SPI readback check
        # I zero out REG_ASIC_SPIPROG_RESET a lot because only transitions from 0 to 1 do anything
        self.REG_SEL_CH = 3  # bit 0-7 chip, 8-15 channel, 31 WIB mode

        self.REG_DAC1 = 4  # bit 0-15 DAC val, 16-19 tp mode select, 31 set dac
        self.REG_DAC2 = 5  # bit 0-15 tp period, 16-31 tp shift

        self.REG_FPGA_TST_PATT = 6  # bit 0-11 tst patt, 16 enable

        self.REG_ADC_CLK = 7  # bit 0-3 clk phase, 8 clk speed sel
        self.REG_LATCHLOC = 8  # bit 0-7 ADC1, 8-15 ADC2, 16-23 ADC3, 24-31 ADC4

        self.REG_STOP_ADC = 9  # bit 0 stops sending convert, read ADC HEADER redundant with reg 2

        self.REG_UDP_FRAME_SIZE = 63  # bits 0-11
        self.REG_FIRMWARE_VERSION = 0xFF  # 255 in decimal
        self.CONFIG_FIRMWARE_VERSION = 259  # this file is written for this

        self.REG_LATCHLOC_data_2MHz = 0x02020202
        self.REG_LATCHLOC_data_1MHz = 0x0
        self.REG_LATCHLOC_data_2MHz_cold = 0x02020202
        self.REG_LATCHLOC_data_1MHz_cold = 0x0

        self.REG_CLKPHASE_data_2MHz = 0x4
        self.REG_CLKPHASE_data_1MHz = 0x0
        self.REG_CLKPHASE_data_2MHz_cold = 0x4
        self.REG_CLKPHASE_data_1MHz_cold = 0x0

        self.DEFAULT_FPGA_TST_PATTERN = 0x12
        self.ADC_TESTPATTERN = [
            0x12, 0x345, 0x678, 0xf1f, 0xad, 0xc01, 0x234, 0x567, 0x89d, 0xeca,
            0xff0, 0x123, 0x456, 0x789, 0xabc, 0xdef
        ]

        # registers 64-88 are SPI to ASICs
        # 88 is last register besides 255 which is firmware version
        self.REG_FESPI_BASE = 84  # this configures all FE ASICs
        self.REG_ADCSPI_BASES = [64, 69, 74, 79]  # for each chip

        self.REG_EXTCLK_INV = 10
        self.REG_EXTCLK_BASES = [11, 20, 29, 38]  # for each chip
        self.FPGA_FREQ_MHZ = 200  # frequency of FPGA clock in MHz

        self.REG_PLL_BASES = [17, 26, 35, 44]  # for each chip

        self.NASICS = 4
        #self.FUNCGENINTER = DummyFuncGen("","")
        self.FUNCGENINTER = Keysight_33600A("/dev/usbtmc0", 1)
        self.POWERSUPPLYINTER = DummyPowerSupply("", "")
        self.F2DEFAULT = 0
        self.CLKDEFAULT = "fifo"

        self.SAMPLERATE = 2e6

        #initialize FEMB UDP object
        self.femb = FEMB_UDP()

        self.adc_regs = []
        for i in range(self.NASICS):
            self.adc_regs.append(ADC_ASIC_REG_MAPPING())

        #self.defaultConfigFunc = lambda: self.configAdcAsic()
        self.defaultConfigFunc = lambda: self.configAdcAsic(clockMonostable=
                                                            True)
        #self.defaultConfigFunc = lambda: self.configAdcAsic(clockMonostable=True,freqInternal=0) # 1 MHz

    def resetBoard(self):
        """
        Reset registers and state machines NOT udp
        Make sure to set reg 0 back to zero
            or there will be much sadness!
        """
        #Reset registers
        self.femb.write_reg(self.REG_RESET, 2)
        time.sleep(1.)

        #Reset state machines
        self.femb.write_reg(self.REG_RESET, 4)
        time.sleep(1.)

        #Reset reset register to 0
        self.femb.write_reg(self.REG_RESET, 0)
        time.sleep(0.2)

    def initBoard(self):
        #set up default registers

        # test readback
        readback = self.femb.read_reg(1)
        if readback is None:
            if self.exitOnError:
                print("FEMB_CONFIG: Error reading register 0, Exiting.")
                sys.exit(1)
            else:
                raise ReadRegError("Couldn't read register 0")

        ##### Start Top-level Labview stacked sequence struct 0
        firmwareVersion = self.femb.read_reg(
            self.REG_FIRMWARE_VERSION) & 0xFFFF
        if firmwareVersion != self.CONFIG_FIRMWARE_VERSION:
            raise FEMBConfigError(
                "Board firmware version {} doesn't match configuration firmware version {}"
                .format(firmwareVersion, self.CONFIG_FIRMWARE_VERSION))
        print("Firmware Version: ", firmwareVersion)

        self.femb.write_reg(self.REG_UDP_FRAME_SIZE, 0x1FB)
        time.sleep(0.05)
        self.setFPGADac(0, 0, 0, 0)  # write regs 4 and 5
        self.femb.write_reg(1, 0)  # pwr ctrl
        self.femb.write_reg(3, (5 << 8))  # chn sel
        self.femb.write_reg(6, self.DEFAULT_FPGA_TST_PATTERN)  #tst pattern
        self.femb.write_reg(7, 13)  #adc clk
        self.femb.write_reg(8, 0)  #latchloc
        ##### End Top-level Labview stacked sequence struct 0

        self.turnOnAsics()

        nRetries = 1
        for iRetry in range(nRetries):

            #Reset ASICs
            self.femb.write_reg(self.REG_ASIC_SPIPROG_RESET,
                                0x0)  # zero out reg
            self.femb.write_reg(self.REG_ASIC_SPIPROG_RESET,
                                0x30)  # reset FE and ADC
            self.femb.write_reg(self.REG_ASIC_SPIPROG_RESET,
                                0x0)  # zero out reg
            time.sleep(0.1)

            #Set FPGA test pattern register
            self.femb.write_reg(
                self.REG_FPGA_TST_PATT,
                self.DEFAULT_FPGA_TST_PATTERN)  # test pattern off
            #self.femb.write_reg(self.REG_FPGA_TST_PATT, self.DEFAULT_FPGA_TST_PATTERN+(1 << 16)) # test pattern on
            #Set ADC latch_loc and clock phase and sample rate
            if self.SAMPLERATE == 1e6:
                if self.COLD:
                    self.femb.write_reg(self.REG_LATCHLOC,
                                        self.REG_LATCHLOC_data_1MHz_cold)
                    self.femb.write_reg(
                        self.REG_ADC_CLK,
                        (self.REG_CLKPHASE_data_1MHz_cold & 0xF) | (1 << 8))
                else:
                    self.femb.write_reg(self.REG_LATCHLOC,
                                        self.REG_LATCHLOC_data_1MHz)
                    self.femb.write_reg(self.REG_ADC_CLK,
                                        (self.REG_CLKPHASE_data_1MHz & 0xF) |
                                        (1 << 8))
            else:  # use 2 MHz values
                if self.COLD:
                    self.femb.write_reg(self.REG_LATCHLOC,
                                        self.REG_LATCHLOC_data_2MHz_cold)
                    self.femb.write_reg(
                        self.REG_ADC_CLK,
                        (self.REG_CLKPHASE_data_2MHz_cold & 0xF))
                else:
                    self.femb.write_reg(self.REG_LATCHLOC,
                                        self.REG_LATCHLOC_data_2MHz)
                    self.femb.write_reg(self.REG_ADC_CLK,
                                        (self.REG_CLKPHASE_data_2MHz & 0xF))
            self.writePLLs(0, 0x20001, 0)

            self.setFPGADac(0, 1, 0, 0)

            #Configure ADC (and external clock inside)
            try:
                #self.femb.write_reg(self.REG_FESPI_BASE,1)
                ##self.adc_regs[0].set_chip(frqc=1)
                #regsListOfLists = []
                #for chipRegConfig in self.adc_regs:
                #    chipRegConfig.set_chip(frqc=1)
                #    regsListOfLists.append(chipRegConfig.REGS)
                #self.configAdcAsic_regs(regsListOfLists)

                self.defaultConfigFunc()
            except ReadRegError:
                continue

            print("ADC Soft Reset...")
            self.femb.write_reg(self.REG_ASIC_SPIPROG_RESET,
                                1 << 6)  # ADC soft reset
            time.sleep(0.1)
            self.femb.write_reg(self.REG_ASIC_SPIPROG_RESET,
                                0x0)  # zero out reg
            time.sleep(0.1)

            #            self.printSyncRegister()
            #            self.syncADC()

            self.printSyncRegister()

            self.selectChannel(0, 0)  # not packed many channels

            #print("Stop ADC...")
            #self.femb.write_reg(self.REG_STOP_ADC,1)
            #time.sleep(0.1)
            #print("Start ADC...")
            #self.femb.write_reg(self.REG_STOP_ADC,0)
            #time.sleep(0.1)
            #self.printSyncRegister()

            # Check that board streams data
            data = self.femb.get_data(1)
            if data == None:
                print("Board not streaming data, retrying initialization...")
                continue  # try initializing again
            print("FEMB_CONFIG--> Reset FEMB is DONE")
            return
        print(
            "Error: Board not streaming data after trying to initialize {} times."
            .format(nRetries))
        if self.exitOnError:
            print("Exiting.")
            sys.exit(1)
        else:
            raise InitBoardError

    def configAdcAsic_regs(self, Adcasic_regs):
        """
        Takes a list NASICS long, each a list of 5 32 bit registers.
        """
        #ADC ASIC SPI registers
        assert (type(Adcasic_regs) == list)
        assert (len(Adcasic_regs) == self.NASICS)
        print("FEMB_CONFIG--> Config ADC ASIC SPI")
        for iTry in range(2):
            #print("  Try at writing SPI: ",iTry+1)
            self.femb.write_reg(self.REG_ASIC_SPIPROG_RESET, 0)
            for iChip, chipRegs in enumerate(Adcasic_regs):
                assert (len(chipRegs) == 5)
                for iReg in range(5):
                    self.femb.write_reg(self.REG_ADCSPI_BASES[iChip] + iReg,
                                        chipRegs[iReg])
                    #print("{:3}  {:#010x}".format(self.REG_ADCSPI_BASES[iChip]+iReg, chipRegs[iReg]))
                    time.sleep(0.05)

            self.femb.write_reg(self.REG_ASIC_SPIPROG_RESET, 3)
            time.sleep(0.1)
            self.femb.write_reg(self.REG_ASIC_SPIPROG_RESET, 2)
            time.sleep(0.1)
            self.femb.write_reg(self.REG_ASIC_SPIPROG_RESET, 0)
            time.sleep(0.1)
            self.femb.write_reg(self.REG_RESET, 0)
            time.sleep(0.1)

        self.femb.write_reg(self.REG_ASIC_SPIPROG_RESET, 1 << 6)  # soft reset

        self.printSyncRegister()

    def getSyncStatus(self):
        syncBits = None
        adc0 = None
        fe0 = None
        adc1 = None
        fe1 = None
        adc2 = None
        fe2 = None
        adc3 = None
        fe3 = None
        reg = self.femb.read_reg(self.REG_ASIC_SPIPROG_RESET)
        if reg is None:
            print("Error: can't read back sync register")
            if self.exitOnError:
                return
            else:
                raise ReadRegError
        else:
            print("Register 2: {:#010x}".format(reg))
            syncBits = reg >> 24
            reg = reg >> 16
            adc0 = ((reg >> 0) & 1) == 1
            fe0 = ((reg >> 1) & 1) == 1
            adc1 = ((reg >> 2) & 1) == 1
            fe1 = ((reg >> 3) & 1) == 1
            adc2 = ((reg >> 4) & 1) == 1
            fe2 = ((reg >> 5) & 1) == 1
            adc3 = ((reg >> 6) & 1) == 1
            fe3 = ((reg >> 7) & 1) == 1
        return (fe0, fe1, fe2, fe3), (adc0, adc1, adc2, adc3), syncBits

    def printSyncRegister(self):
        (fe0, fe1, fe2, fe3), (adc0, adc1, adc2,
                               adc3), syncBits = self.getSyncStatus()
        reg = self.femb.read_reg(self.REG_ASIC_SPIPROG_RESET)
        print("ASIC Readback Status:")
        print("  ADC 0:", adc0, "FE 0:", fe0)
        print("  ADC 1:", adc1, "FE 1:", fe1)
        print("  ADC 2:", adc2, "FE 2:", fe2)
        print("  ADC 3:", adc3, "FE 3:", fe3)
        print("ADC Sync Bits: {:#010b} (0 is good)".format(syncBits))

    def configAdcAsic(self,
                      enableOffsetCurrent=None,
                      offsetCurrent=None,
                      testInput=None,
                      freqInternal=None,
                      sleep=None,
                      pdsr=None,
                      pcsr=None,
                      clockMonostable=None,
                      clockExternal=None,
                      clockFromFIFO=None,
                      sLSB=None,
                      f0=None,
                      f1=None,
                      f2=None,
                      f3=None,
                      f4=None,
                      f5=None):
        """
        Configure ADCs
          enableOffsetCurrent: 0 disable offset current, 1 enable offset current
          offsetCurrent: 0-15, amount of current to draw from sample and hold
          testInput: 0 digitize normal input, 1 digitize test input
          freqInternal: internal clock frequency: 0 1MHz, 1 2MHz
          sleep: 0 disable sleep mode, 1 enable sleep mode
          pdsr: if pcsr=0: 0 PD is low, 1 PD is high
          pcsr: 0 power down controlled by pdsr, 1 power down controlled externally
          Only one of these can be enabled:
            clockMonostable: True ADC uses monostable clock
            clockExternal: True ADC uses external clock
            clockFromFIFO: True ADC uses digital generator FIFO clock
          sLSB: LSB current steering mode. 0 for full, 1 for partial (ADC7 P1)
          f0, f1, f2, f3, f4, f5: version specific
        """
        FEMB_CONFIG_BASE.configAdcAsic(self,
                                       clockMonostable=clockMonostable,
                                       clockExternal=clockExternal,
                                       clockFromFIFO=clockFromFIFO)
        if enableOffsetCurrent is None:
            enableOffsetCurrent = 0
        if offsetCurrent is None:
            offsetCurrent = 0
        else:
            offsetCurrent = int(
                "{:04b}".format(offsetCurrent)[::-1],
                2)  # need to reverse bits, use string/list tricks
        if testInput is None:
            testInput = 1
        if freqInternal is None:
            freqInternal = 1
        if sleep is None:
            sleep = 0
        if pdsr is None:
            pdsr = 0
        if pcsr is None:
            pcsr = 0
        if sLSB is None:
            sLSB = 0
        if f1 is None:
            f1 = 0
        if f2 is None:
            f2 = 0
        if f3 is None:
            f3 = 0
        if f4 is None:
            f4 = 1
        if f5 is None:
            f5 = 0
        if not (clockMonostable or clockExternal or clockFromFIFO):
            clockExternal = True
        # a bunch of things depend on the clock choice
        clk0 = 0
        clk1 = 0
        if clockExternal:
            clk0 = 1
            clk1 = 0
        elif clockFromFIFO:
            clk0 = 0
            clk1 = 1
        if f0 is None:
            if clockExternal:
                f0 = 1
            else:
                f0 = 0
        if clockExternal:
            self.extClock(enable=True)
        else:
            self.extClock(enable=False)

        regsListOfLists = []
        for chipRegConfig in self.adc_regs:
            chipRegConfig.set_chip(en_gr=enableOffsetCurrent,
                                   d=offsetCurrent,
                                   tstin=testInput,
                                   frqc=freqInternal,
                                   slp=sleep,
                                   pdsr=pdsr,
                                   pcsr=pcsr,
                                   clk0=clk0,
                                   clk1=clk1,
                                   f0=f0,
                                   f1=f1,
                                   f2=f2,
                                   f3=f3,
                                   f4=f4,
                                   f5=f5,
                                   slsb=sLSB)
            regsListOfLists.append(chipRegConfig.REGS)
        self.configAdcAsic_regs(regsListOfLists)

    def selectChannel(self, asic, chan, hsmode=1, singlechannelmode=0):
        """
        asic is chip number 0 to 7
        chan is channel within asic from 0 to 15
        hsmode: if 0 then WIB streaming mode, 
            if 1 then sends ch then adc, defaults to 1
        singlechannelmode: if 1 and hsmode = 0, then only 
            send a single channel of data instead of 16 in a row
        
        """
        hsmodeVal = int(hsmode) & 1  # only 1 bit
        hsmodeVal = (~hsmodeVal) & 1  # flip bit
        singlechannelmode = int(singlechannelmode) & 1
        asicVal = int(asic)
        if (asicVal < 0) or (asicVal >= self.NASICS):
            print(
                "femb_config_femb : selectChan - invalid ASIC number, only 0 to {} allowed"
                .format(self.NASICS - 1))
            return
        chVal = int(chan)
        if (chVal < 0) or (chVal > 15):
            print(
                "femb_config_femb : selectChan - invalid channel number, only 0 to 15 allowed"
            )
            return

        #print( "Selecting ASIC " + str(asicVal) + ", channel " + str(chVal))

        self.femb.write_reg(self.REG_STOP_ADC, 1)
        time.sleep(0.05)

        # bit 4 of chVal is the single channel mode bit
        chVal += (singlechannelmode << 4)

        # in this firmware asic = 0 disables readout, so asics are 1,2,3,4

        regVal = (asicVal + 1) + (chVal << 8) + (hsmodeVal << 31)
        self.femb.write_reg(self.REG_SEL_CH, regVal)
        time.sleep(0.05)

        self.femb.write_reg(self.REG_STOP_ADC, 0)

    def syncADC(self, iASIC=None):
        #return True, 0, 0
        #turn on ADC test mode
        print("FEMB_CONFIG--> Start sync ADC")

        self.configAdcAsic(clockMonostable=True, f4=0, f5=1)
        time.sleep(0.1)

        alreadySynced = True
        asicsToSync = [iASIC]
        if iASIC is None:
            # not actually getting for sync just for properly configured ADC chips
            feSPI, adcSPI, syncBits = self.getSyncStatus()
            asicsToSync = [i for i in range(self.NASICS) if adcSPI[i]]
        for a in asicsToSync:
            print("FEMB_CONFIG--> Test ADC " + str(a))
            unsync, syncDicts = self.testUnsync(a)
            if unsync != 0:
                alreadySynced = False
                print("FEMB_CONFIG--> ADC not synced, try to fix")
                self.fixUnsync(a)
        latchloc = None
        phase = None
        latchloc = self.femb.read_reg(self.REG_LATCHLOC)
        clkphase = self.femb.read_reg(self.REG_ADC_CLK) & 0b1111
        if self.SAMPLERATE == 1e6:
            if self.COLD:
                self.REG_LATCHLOC_data_1MHz_cold = latchloc
                self.REG_CLKPHASE_data_1MHz_cold = clkphase
            else:
                self.REG_LATCHLOC_data_1MHz = latchloc
                self.REG_CLKPHASE_data_1MHz = clkphase
        else:  # 2 MHz
            if self.COLD:
                self.REG_LATCHLOC_data_2MHZ_cold = latchloc
                self.REG_CLKPHASE_data_2MHZ_cold = clkphase
            else:
                self.REG_LATCHLOC_data_2MHZ = latchloc
                self.REG_CLKPHASE_data_2MHZ = clkphase
        print("FEMB_CONFIG--> Latch latency {:#010x} Phase: {:#010x}".format(
            latchloc, clkphase))
        self.defaultConfigFunc()
        print("FEMB_CONFIG--> End sync ADC")
        return not alreadySynced, latchloc, None, clkphase

    def testUnsync(self, adc, npackets=10):
        #return 0, []
        print("Starting testUnsync adc: ", adc)
        adcNum = int(adc)
        if (adcNum < 0) or (adcNum > 7):
            print(
                "FEMB_CONFIG--> femb_config_femb : testLink - invalid asic number"
            )
            return

#        syncBits = self.femb.read_reg(2) >> 24
#        theseSyncBits = (syncBits >> (2*adc)) & 0b11
#        if theseSyncBits == 0:
#            return 0, []
#        else:
#            return 1, []

#loop through channels, check test pattern against data
        syncDataCounts = [{} for i in range(16)]  #dict for each channel
        self.selectChannel(adcNum, 0, singlechannelmode=0)
        time.sleep(0.05)
        data = self.femb.get_data(npackets)
        if data == None:
            print("Error: Couldn't read data in testUnsync")
            if self.exitOnError:
                print("Exiting.")
                sys.exit(1)
            else:
                raise SyncADCError
        for samp in data:
            if samp == None:
                continue
            ch = ((samp >> 12) & 0xF)
            sampVal = (samp & 0xFFF)
            if sampVal in syncDataCounts[ch]:
                syncDataCounts[ch][sampVal] += 1
            else:
                syncDataCounts[ch][sampVal] = 1
        # check jitter
        print("Channel 0:")
        for key in sorted(syncDataCounts[0]):
            print(" {0:#06x} = {0:#018b} count: {1}".format(
                key, syncDataCounts[0][key]))
        badSync = 0
        maxCodes = [None] * 16
        syncDicts = [{}] * 16
        for ch in range(0, 16, 1):
            sampSum = 0
            maxCode = None
            nMaxCode = 0
            for code in syncDataCounts[ch]:
                nThisCode = syncDataCounts[ch][code]
                sampSum += nThisCode
                if nThisCode > nMaxCode:
                    nMaxCode = nThisCode
                    maxCode = code
            maxCodes[ch] = maxCode
            syncDicts[ch]["maxCode"] = maxCode
            syncDicts[ch]["nSamplesMaxCode"] = nMaxCode
            syncDicts[ch]["nSamples"] = sampSum
            syncDicts[ch]["zeroJitter"] = True
            if len(syncDataCounts[ch]) > 1:
                syncDicts[ch]["zeroJitter"] = False
                badSync = 1
                diff = sampSum - nMaxCode
                frac = diff / float(sampSum)
                print("Sync Error: Jitter for Ch {:2}: {:8.4%} ({:5}/{:5})".
                      format(ch, frac, diff, sampSum))
        for ch in range(0, 16, 1):
            maxCode = maxCodes[ch]
            correctCode = self.ADC_TESTPATTERN[ch]
            syncDicts[ch]["data"] = True
            syncDicts[ch]["maxCodeMatchesExpected"] = True
            if maxCode is None:
                syncDicts[ch]["data"] = False
                badSync = 1
                print("Sync Error: no data for ch {:2}".format(ch))
            elif maxCode != correctCode:
                syncDicts[ch]["maxCodeMatchesExpected"] = True
                badSync = 1
                print(
                    "Sync Error: mismatch for ch {:2}: expected {:#03x} observed {:#03x}"
                    .format(ch, correctCode, maxCode))
        return badSync, syncDicts

    def fixUnsync(self, adc):
        adcNum = int(adc)
        if (adcNum < 0) or (adcNum > 7):
            print(
                "FEMB_CONFIG--> femb_config_femb : testLink - invalid asic number"
            )
            return

        initLATCH = self.femb.read_reg(self.REG_LATCHLOC)
        initPHASE = self.femb.read_reg(
            self.REG_ADC_CLK)  # remember bit 16 sample rate

        phases = [0, 1]
        if self.COLD:
            phases = [0, 1, 0, 1, 0]

        #loop through sync parameters
        for shift in range(0, 16, 1):
            shiftMask = (0xFF << 8 * adcNum)
            testShift = ((initLATCH & ~(shiftMask)) | (shift << 8 * adcNum))
            self.femb.write_reg(self.REG_LATCHLOC, testShift)
            time.sleep(0.01)
            for phase in phases:
                clkMask = (0x1 << adcNum)
                testPhase = ((initPHASE & ~(clkMask)) | (phase << adcNum))
                self.femb.write_reg(self.REG_ADC_CLK, testPhase)
                time.sleep(0.01)
                print("try shift: {} phase: {} testingUnsync...".format(
                    shift, phase))
                #reset ADC ASIC
                self.femb.write_reg(self.REG_ASIC_SPIPROG_RESET,
                                    1 << 6)  # ADC soft reset
                time.sleep(0.1)
                self.femb.write_reg(self.REG_ASIC_SPIPROG_RESET,
                                    0x0)  # zero out reg
                time.sleep(0.1)
                #test link
                unsync, syncDicts = self.testUnsync(adcNum)
                if unsync == 0:
                    print("FEMB_CONFIG--> ADC synchronized")
                    return
        #if program reaches here, sync has failed
        print("Error: FEMB_CONFIG--> ADC SYNC process failed for ADC # " +
              str(adc))
        print(
            "Setting back to original values: LATCHLOC: {:#010x}, PHASE: {:#010x}"
            .format(initLATCH, initPHASE & 0xF))
        self.femb.write_reg(self.REG_LATCHLOC, initLATCH)
        self.femb.write_reg(self.REG_ADC_CLK, initPHASE)
        if self.exitOnError:
            sys.exit(1)
        else:
            raise SyncADCError

    def extClock(self,
                 enable=False,
                 period=500,
                 mult=1,
                 offset_rst=0,
                 offset_read=480,
                 offset_msb=230,
                 offset_lsb=480,
                 width_rst=50,
                 width_read=20,
                 width_msb=270,
                 width_lsb=20,
                 offset_lsb_1st_1=50,
                 width_lsb_1st_1=190,
                 offset_lsb_1st_2=480,
                 width_lsb_1st_2=20,
                 inv_rst=True,
                 inv_read=True,
                 inv_msb=False,
                 inv_lsb=False,
                 inv_lsb_1st=False):
        """
        Programs external clock. All non-boolean arguments except mult are in nanoseconds
        IDXM = msb
        IDXL = lsb
        IDL = lsb_1st
        """

        rd_off = 0
        rst_off = 0
        rst_wid = 0
        msb_off = 0
        msb_wid = 0
        lsb_fc_wid2 = 0
        lsb_fc_off1 = 0
        rd_wid = 0
        lsb_fc_wid1 = 0
        lsb_fc_off2 = 0
        lsb_wid = 0
        lsb_off = 0
        inv = 0

        if enable:
            clock = 1. / self.FPGA_FREQ_MHZ * 1000.  # clock now in ns
            denominator = clock / mult
            period_val = period // denominator
            #print("FPGA Clock freq: {} MHz period: {} ns".format(self.FPGA_FREQ_MHZ,clock))
            #print("ExtClock option mult: {}".format(mult))
            #print("ExtClock option period: {} ns".format(period))
            #print("ExtClock option offset_read: {} ns".format(offset_read))
            #print("ExtClock option offset_rst: {} ns".format(offset_rst))
            #print("ExtClock option offset_msb: {} ns".format(offset_msb))
            #print("ExtClock option offset_lsb: {} ns".format(offset_lsb))
            #print("ExtClock option offset_lsb_1st_1: {} ns".format(offset_lsb_1st_1))
            #print("ExtClock option offset_lsb_1st_2: {} ns".format(offset_lsb_1st_2))
            #print("ExtClock option width_read: {} ns".format(width_read))
            #print("ExtClock option width_rst: {} ns".format(width_rst))
            #print("ExtClock option width_msb: {} ns".format(width_msb))
            #print("ExtClock option width_lsb: {} ns".format(width_lsb))
            #print("ExtClock option width_lsb_1st_1: {} ns".format(width_lsb_1st_1))
            #print("ExtClock option width_lsb_1st_2: {} ns".format(width_lsb_1st_2))
            #print("ExtClock option inv_rst: {}".format(inv_rst))
            #print("ExtClock option inv_read: {}".format(inv_read))
            #print("ExtClock option inv_msb: {}".format(inv_msb))
            #print("ExtClock option inv_lsb: {}".format(inv_lsb))
            #print("ExtClock option inv_lsb_1st: {}".format(inv_lsb_1st))
            #print("ExtClock denominator: {} ns".format(denominator))
            #print("ExtClock period: {} ns".format(period_val))

            rd_off = int(offset_read // denominator) & 0xFFFF
            rst_off = int(offset_rst // denominator) & 0xFFFF
            rst_wid = int(width_rst // denominator) & 0xFFFF
            msb_off = int(offset_msb // denominator) & 0xFFFF
            msb_wid = int(width_msb // denominator) & 0xFFFF
            lsb_fc_wid2 = int(width_lsb_1st_2 // denominator) & 0xFFFF
            lsb_fc_off1 = int(offset_lsb_1st_1 // denominator) & 0xFFFF
            rd_wid = int(width_read // denominator) & 0xFFFF
            lsb_fc_wid1 = int(width_lsb_1st_1 // denominator) & 0xFFFF
            lsb_fc_off2 = int(offset_lsb_1st_2 // denominator) & 0xFFFF
            lsb_wid = int(width_lsb // denominator) & 0xFFFF
            lsb_off = int(offset_lsb // denominator) & 0xFFFF

            if inv_rst:
                inv += 1 << 0
            if inv_read:
                inv += 1 << 1
            if inv_msb:
                inv += 1 << 2
            if inv_lsb:
                inv += 1 << 3
            if inv_lsb_1st:
                inv += 1 << 4

        def writeRegAndPrint(name, reg, val):
            #print("ExtClock Register {0:15} number {1:3} set to {2:10} = {2:#010x}".format(name,reg,val))
            #print("ExtClock Register {0:15} number {1:3} set to {2:#034b}".format(name,reg,val))
            self.femb.write_reg(reg, val)

        writeRegAndPrint("inv", self.REG_EXTCLK_INV, inv),
        for iChip, regBase in enumerate(self.REG_EXTCLK_BASES):
            iStr = str(iChip)
            asicRegs = [
                ("RST_ADC" + iStr, (rst_wid << 16) | rst_off),
                ("READ_ADC" + iStr, (rd_wid << 16) | rd_off),
                ("IDXM_ADC" + iStr, (msb_wid << 16) | msb_off),  # msb
                ("IDXL_ADC" + iStr, (lsb_wid << 16) | lsb_off),  # lsb
                ("IDL1_ADC" + iStr,
                 (lsb_fc_wid1 << 16) | lsb_fc_off1),  # lsb_fc_1
                ("IDL2_ADC" + iStr,
                 (lsb_fc_wid2 << 16) | lsb_fc_off2),  # lsb_fc_1
            ]
            for iReg, tup in enumerate(asicRegs):
                name = tup[0]
                val = tup[1]
                writeRegAndPrint(name, regBase + iReg, val)

    def turnOffAsics(self):
        oldReg = self.femb.read_reg(self.REG_PWR_CTRL)
        newReg = oldReg & 0xFFFFFFF0
        self.femb.write_reg(self.REG_PWR_CTRL, newReg)
        #pause after turning off ASICs
        time.sleep(2)
        #self.femb.write_reg(self.REG_RESET, 4) # bit 2 is ASIC reset as far as I can see

    def turnOnAsic(self, asic):
        asicVal = int(asic)
        if (asicVal < 0) or (asicVal >= self.NASICS):
            print(
                "femb_config_femb : turnOnAsics - invalid ASIC number, only 0 to {} allowed"
                .format(self.NASICS - 1))
            return
        print("turnOnAsic " + str(asicVal))
        oldReg = self.femb.read_reg(self.REG_PWR_CTRL)
        newReg = oldReg | (1 << asic)
        self.femb.write_reg(self.REG_PWR_CTRL, newReg)

        time.sleep(2)  #pause after turn on
        #self.femb.write_reg(self.REG_RESET, 4) # bit 2 is ASIC reset as far as I can see

    def turnOnAsics(self):
        print("turnOnAsics 0-{}".format(int(self.NASICS - 1)))

        reg = self.femb.read_reg(self.REG_PWR_CTRL)
        for iAsic in reversed(range(self.NASICS)):
            print("iAsic", iAsic)
            reg = reg | (1 << iAsic)
            self.femb.write_reg(self.REG_PWR_CTRL, reg)
            if iAsic > 0:
                print("sleeping...")
                time.sleep(5.)

        #pause after turning on ASICs
        time.sleep(5)

    def setFPGADac(self, amp, mode, freq, delay):
        """
        mode: 0 DAC only, 1 ext tp, 2 gnd, 3 1.8V, 4 test pulse, 5 1.8V FE, 6 ASIC TP DAC
        """
        ampRegVal = ((mode & 0xFFFF) << 16) | (amp & 0xFFFF)
        freqRegVal = ((delay & 0xFFFF) << 16) | (freq & 0xFFFF)

        self.femb.write_reg(self.REG_DAC2, freqRegVal)
        time.sleep(0.05)

        self.femb.write_reg(self.REG_DAC1, ampRegVal)
        time.sleep(0.05)
        self.femb.write_reg(self.REG_DAC1, ampRegVal & 0x80000000)
        time.sleep(0.05)
        self.femb.write_reg(self.REG_DAC1, ampRegVal)

    def writePLLs(self, step0, step1, step2):
        for iChip in range(3):
            self.writePLL(iChip, step0, step1, step2)

    def writePLL(self, iChip, step0, step1, step2):
        def writeRegAndPrint(name, reg, val):
            self.femb.write_reg(reg, val)
            time.sleep(0.05)
            readback = self.femb.read_reg(reg)
            #print("PLL Register {0:15} number {1:3} set to {2:10} = {2:#010x}".format(name,reg,val))
            #print("PLL Register {0:15} number {1:3} set to {2:#034b}".format(name,reg,val))
            if readback == val:
                pass
                #print("  Readback match")
            else:
                print(
                    "PLL Register {0:15} number {1:3} set to {2:10} = {2:#010x}"
                    .format(name, reg, val))
                print(
                    "  READBACK DOESN'T MATCH! write: {:#010x} read: {:#010x}".
                    format(val, readback))

        regBase = self.REG_PLL_BASES[iChip]
        iStr = str(iChip)
        asicRegs = [
            ("pll_STEP0_ADC" + iStr, step0),
            ("pll_STEP1_ADC" + iStr, step1),
            ("pll_STEP2_ADC" + iStr, step2),
        ]
        for iReg, tup in enumerate(asicRegs):
            name = tup[0]
            val = tup[1]
            writeRegAndPrint(name, regBase + iReg, val)

    def syncPLLs(self):
        for iChip in range(1):
            syncd, detail = self.testUnsync(iChip)

    def programFirmware(self, firmware):
        """
        Programs the FPGA using the firmware file given.
        """
        pass

    def checkFirmwareProgrammerStatus(self):
        """
        Prints a debug message for the firmware programmer
        """
        pass

    def programFirmware1Mhz(self):
        pass

    def programFirmware2Mhz(self):
        pass

    def getClockStr(self):
        latchloc = self.femb.read_reg(self.REG_LATCHLOC)
        clkphase = self.femb.read_reg(self.REG_ADC_CLK)
        if latchloc is None:
            return "Register Read Error"
        if clkphase is None:
            return "Register Read Error"
        return "Latch Loc: {:#010x} Clock Phase: {:#010x}".format(
            latchloc, clkphase & 0xF)
class FEMB_CONFIG(FEMB_CONFIG_BASE):

    #__INIT__#
    def __init__(self):
        #declare basic system parameters
        self.NFEMBS = 4
        self.NASICS = 8
        self.NASICCH = 16

        #declare board specific registers
        self.FEMB_VER = "WIB_SBND"
        self.REG_RESET = 0
        self.REG_ASIC_RESET = 1
        self.REG_ASIC_SPIPROG = 2

        self.REG_LATCHLOC_3_TO_0 = 4
        self.REG_LATCHLOC_7_TO_4 = 14

        self.REG_FPGA_TP_EN = 16
        self.REG_ASIC_TP_EN = 16
        self.REG_DAC_SELECT = 16
        self.REG_TP = 5

        self.CLK_SELECT = 6
        self.CLK_SELECT2 = 15

        self.REG_SEL_ASIC = 7
        self.REG_SEL_ASIC_LSB = 8

        self.REG_WIB_MODE = 8
        self.REG_ADC_DISABLE = 8

        self.REG_HS_DATA = 9

        self.INT_TP_EN = 18
        self.EXT_TP_EN = 18

        #EXTERNAL CLOCK STUFF HERE

        self.REG_SPI_BASE = 512
        self.REG_SPI_RDBACK_BASE = 592

        self.fembNum = 0

        #initialize FEMB UDP object
        self.femb = FEMB_UDP()
        self.femb.UDP_PORT_WREG = 32000  #WIB PORTS
        self.femb.UDP_PORT_RREG = 32001
        self.femb.UDP_PORT_RREGRESP = 32002

        #ASIC config variables
        self.feasicLeakage = 0  #0 = 500pA, 1 = 100pA
        self.feasicLeakagex10 = 0  #0 = pA, 1 = pA*10
        self.feasicAcdc = 0  #AC = 0, DC = 1

        self.feasicEnableTestInput = 1  #0 = disabled, 1 = enabled
        self.feasicBaseline = 0  #0 = 200mV, 1 = 900mV
        self.feasicGain = 1  #4.7,7.8,14,25
        self.feasicShape = 3  #0.5,1,2,3
        self.feasicBuf = 0  #0 = OFF, 1 = ON

    def resetBoard(self):
        print("Reset")

    def initBoard(self):
        self.initWib()
        for femb in range(0, 4, 1):
            self.initFemb(femb)

    def initWib(self):

        #WIB initialization

        #set UDP ports to WIB registers
        self.femb.UDP_PORT_WREG = 32000
        self.femb.UDP_PORT_RREG = 32001
        self.femb.UDP_PORT_RREGRESP = 32002
        self.femb.REG_SLEEP = 0.001

        #register 2, LED
        self.femb.write_reg_bits(2, 0, 0xFF, 0)

        #clock select (firmware version dependent)
        #self.femb.write_reg_bits(4 , 2, 0x3, 2 )

        #initialize clock
        self.initSI5338()

        #return register interface to FEMB
        self.selectFemb(self.fembNum)

    def initFemb(self, femb):
        fembVal = int(femb)
        if (fembVal < 0) or (fembVal >= self.NFEMBS):
            return

        #FEMB power enable on WIB
        self.powerOnFemb(fembVal)

        #Make sure register interface is for correct FEMB
        self.selectFemb(fembVal)

        #check if FEMB register interface is working
        self.selectFemb(fembVal)
        print("Checking register interface")
        regVal = self.femb.read_reg(6)
        if (regVal == None) or (regVal == -1):
            print("Error - FEMB register interface is not working.")
            print(" Will not initialize FEMB.")
            return

        #turn off pulser
        self.femb.write_reg_bits(self.REG_FPGA_TP_EN, 0, 0x1,
                                 0)  #test pulse enable
        self.femb.write_reg_bits(self.REG_ASIC_TP_EN, 1, 0x1,
                                 0)  #test pulse enable
        self.femb.write_reg_bits(self.REG_DAC_SELECT, 8, 0x1,
                                 0)  #test pulse enable
        self.femb.write_reg_bits(self.REG_TP, 0, 0x1F,
                                 0x00)  #test pulse amplitude
        self.femb.write_reg_bits(self.REG_TP, 16, 0xFFFF,
                                 0x100)  #test pulse frequency
        self.femb.write_reg_bits(self.REG_TP, 8, 0xFF, 0x00)  #test pulse delay

        #phase control
        self.femb.write_reg_bits(self.CLK_SELECT, 0, 0xFF, 0x0)  #clock select
        self.femb.write_reg_bits(self.CLK_SELECT2, 0, 0xFF,
                                 0xF0)  #clock select 2
        self.femb.write_reg_bits(self.REG_LATCHLOC_3_TO_0, 0, 0xFFFFFFFF,
                                 0x00000000)  #datashift
        self.femb.write_reg_bits(self.REG_LATCHLOC_7_TO_4, 0, 0xFFFFFFFF,
                                 0x00000000)  #datashift

        #enable streaming
        self.femb.write_reg_bits(self.REG_HS_DATA, 0, 0x1,
                                 1)  #Enable streaming
        self.femb.write_reg_bits(self.REG_HS_DATA, 3, 0x1, 1)  #Enable ADC data

        #Set FE ASIC SPI configuration registers
        self.configFeAsic()

        #Set ADC SPI configuration registers

        #EXTERNAL CLOCK STUFF

    #Test FEMB SPI working
    def checkFembSpi(self, femb):
        fembVal = int(femb)

        print("About to check SPI:", self.femb.read_reg(8))

        print("Check ASIC SPI")
        for regNum in range(self.REG_SPI_BASE, self.REG_SPI_BASE + 72, 1):
            val = self.femb.read_reg(regNum)
            if (val == None) or (val == -1):
                print("Error - FEMB register interface is not working.")
                continue
        #    print( str(hex(val)) )

        print("Check ASIC SPI Readback")
        for regNum in range(self.REG_SPI_RDBACK_BASE,
                            self.REG_SPI_RDBACK_BASE + 72, 1):
            val = self.femb.read_reg(regNum)
            if (val == None) or (val == -1):
                print("Error - FEMB register interface is not working.")
                continue
        #    print( str(hex(val)) )

        #Compare input to output

    #FEMB power enable on WIB
    def powerOnFemb(self, femb):
        fembVal = int(femb)
        if (fembVal < 0) or (fembVal > 3):
            return

        #set UDP ports to WIB registers
        self.femb.UDP_PORT_WREG = 32000
        self.femb.UDP_PORT_RREG = 32001
        self.femb.UDP_PORT_RREGRESP = 32002

        regBase = int(fembVal * 4)

        #FEMB power enable
        self.femb.write_reg_bits(8, regBase + 0, 0x1, 1)  #3.6V
        self.femb.write_reg_bits(8, regBase + 1, 0x1, 1)  #2.8V
        self.femb.write_reg_bits(8, regBase + 2, 0x1, 1)  #2.5V
        self.femb.write_reg_bits(8, regBase + 3, 0x1, 1)  #1.5V
        self.femb.write_reg_bits(8, 16 + fembVal, 0x1, 1)  #BIAS enable

        print("FEMB Power on: ", hex(self.femb.read_reg(8)))

        #return register interface to FEMB
        self.selectFemb(self.fembNum)

    def powerOffFemb(self, femb):
        fembVal = int(femb)
        if (fembVal < 0) or (fembVal > 3):
            return

        #set UDP ports to WIB registers
        self.femb.UDP_PORT_WREG = 32000
        self.femb.UDP_PORT_RREG = 32001
        self.femb.UDP_PORT_RREGRESP = 32002

        regBase = int(fembVal * 4)

        #FEMB power disable
        self.femb.write_reg_bits(8, 16 + fembVal, 0x1, 0)  #BIAS
        self.femb.write_reg_bits(8, regBase + 0, 0x1, 0)  #3.6V
        self.femb.write_reg_bits(8, regBase + 1, 0x1, 0)  #2.8V
        self.femb.write_reg_bits(8, regBase + 2, 0x1, 0)  #2.5V
        self.femb.write_reg_bits(8, regBase + 3, 0x1, 0)  #1.5V

        print("FEMB Power off: ", hex(self.femb.read_reg(8)))

        #return register interface to FEMB
        self.selectFemb(self.fembNum)

    def selectChannel(self, asic, chan):
        #print("Select channel")
        asicVal = int(asic)
        if (asicVal < 0) or (asicVal > self.NASICS):
            return

        #set UDP ports to WIB
        self.femb.UDP_PORT_WREG = 32000
        self.femb.UDP_PORT_RREG = 32001
        self.femb.UDP_PORT_RREGRESP = 32002

        #select ASIC
        #print("Selecting ASIC " + str(asicVal) )
        self.femb.write_reg_bits(self.REG_SEL_ASIC, self.REG_SEL_ASIC_LSB, 0xF,
                                 asicVal)

        #Note: WIB data format streams all 16 channels, don't need to select specific channel

        #set UDP ports back to normal
        self.selectFemb(self.fembNum)

    def configFeAsic(self):
        print("CONFIG ASICs")
        #configure ASICs to default

        #global config varibles
        feasicLeakageVal = int(self.feasicLeakage)  #0 = 500pA, 1 = 100pA
        feasicLeakagex10Val = int(self.feasicLeakagex10)  #0 = x1, 1 = x10
        acdcVal = int(self.feasicAcdc)  #DC = 0, AC = 1

        #channel specific variables
        testVal = int(self.feasicEnableTestInput)
        baseVal = int(self.feasicBaseline)  #0 = 900mV, 1 = 200mV
        gainVal = int(self.feasicGain)
        shapeVal = int(self.feasicShape)
        bufVal = int(self.feasicBuf)  #0 = OFF, 1 = ON

        if (testVal < 0) or (testVal > 1):
            return
        if (baseVal < 0) or (baseVal > 1):
            return
        if (gainVal < 0) or (gainVal > 3):
            return
        if (shapeVal < 0) or (shapeVal > 3):
            return
        if (acdcVal < 0) or (acdcVal > 1):
            return
        if (bufVal < 0) or (bufVal > 1):
            return
        if (feasicLeakageVal < 0) or (feasicLeakageVal > 1):
            return
        if (feasicLeakagex10Val < 0) or (feasicLeakagex10Val > 1):
            return

        chReg = 0
        #test capacitor, bit 7
        chReg = chReg + ((testVal & 0x01) << 7)

        #baseline control, bit 6
        baseVal = 1 - baseVal  #assign 0 = 200mV, 1 = 900mV
        chReg = chReg + ((baseVal & 0x01) << 6)

        #gain control, bits 4-5
        gainArray = [0, 2, 1, 3]
        chReg = chReg + ((gainArray[gainVal] & 0x03) << 4)

        #shape control, bits 2-3
        shapeArray = [2, 0, 3, 1]  #I don't know why
        chReg = chReg + ((shapeArray[shapeVal] & 0x03) << 2)

        #buffer control, bit 0
        chReg = chReg + ((bufVal & 0x01) << 0)

        #construct the channel word
        chWord = (chReg << 24) + (chReg << 16) + (chReg << 8) + chReg

        asicReg = int(0)
        #asicReg = int(0x0A00)

        #leakage control 1, bit 0
        asicReg = asicReg + ((feasicLeakageVal & 0x01) << 0)

        #leakage control 2, bit 4
        asicReg = asicReg + ((feasicLeakagex10Val & 0x01) << 4)

        #AC/DC control

        #monitor control, bits 1-2

        #internal DAC enable, bit 8

        #external DAC enable, bit 9

        #DAC OUTPUT bits 8-9 , 0xA00 = external DAC

        #turn off HS data before register writes
        self.femb.write_reg_bits(9, 0, 0x1, 0)
        print("HS link turned off")
        time.sleep(2)

        #write SPI regs - very rough version
        chWord = (chReg << 24) + (chReg << 16) + (chReg << 8) + chReg
        for asic in range(0, self.NASICS, 1):
            baseReg = self.REG_SPI_BASE + int(asic) * 9
            self.femb.write_reg_bits(baseReg + 4, 16, 0xFF, chReg)  #ch0
            self.femb.write_reg_bits(baseReg + 4, 24, 0xFF, chReg)  #ch1
            self.femb.write_reg(baseReg + 5, chWord)  #ch2-5
            self.femb.write_reg(baseReg + 6, chWord)  #ch6-9
            self.femb.write_reg(baseReg + 7, chWord)  #ch10-13
            self.femb.write_reg_bits(baseReg + 8, 0, 0xFF, chReg)  #ch14
            self.femb.write_reg_bits(baseReg + 8, 8, 0xFF, chReg)  #ch15
            self.femb.write_reg_bits(baseReg + 8, 16, 0xFFFF,
                                     asicReg)  #ASIC gen reg

        #run the SPI programming
        self.doAsicConfig()

        #turn HS link back on
        print("HS link turned back on")
        time.sleep(2)
        self.femb.write_reg_bits(9, 0, 0x1, 1)

    def doAsicConfig(self):

        #for regNum in range(self.REG_SPI_BASE,self.REG_SPI_BASE+72,1):
        #    regVal = self.femb.read_reg( regNum)
        #    print( str(regNum) + "\t" + str(hex(regVal)) )

        #Write ADC ASIC SPI
        print("Program ASIC SPI")
        self.femb.write_reg(self.REG_ASIC_RESET, 1)
        time.sleep(0.1)
        self.femb.write_reg(self.REG_ASIC_SPIPROG, 1)
        time.sleep(0.1)
        self.femb.write_reg(self.REG_ASIC_SPIPROG, 1)
        time.sleep(0.1)

        #for regNum in range(self.REG_SPI_RDBACK_BASE,self.REG_SPI_RDBACK_BASE+72,1):
        #    regVal = self.femb.read_reg( regNum)
        #    print( str(regNum) + "\t" + str(hex(regVal)) )

    def setInternalPulser(self, pulserEnable, pulseHeight):
        print("Set Pulser")

    def syncADC(self):
        print("Sync")

    def selectFemb(self, fembIn):
        fembVal = int(fembIn)
        if (fembVal < 0) or (fembVal > self.NFEMBS):
            print("Invalid FEMB # requested")
            return

        self.fembNum = fembVal

        #set data streaming for requested FEMB
        #set UDP ports to WIB
        self.femb.UDP_PORT_WREG = 32000
        self.femb.UDP_PORT_RREG = 32001
        self.femb.UDP_PORT_RREGRESP = 32002
        self.femb.write_reg_bits(7, 16, 0x3, self.fembNum)

        #set read/write ports
        if fembVal == 0:
            self.femb.UDP_PORT_WREG = 32016
            self.femb.UDP_PORT_RREG = 32017
            self.femb.UDP_PORT_RREGRESP = 32018

        if fembVal == 1:
            self.femb.UDP_PORT_WREG = 32032
            self.femb.UDP_PORT_RREG = 32033
            self.femb.UDP_PORT_RREGRESP = 32034

        if fembVal == 2:
            self.femb.UDP_PORT_WREG = 32048
            self.femb.UDP_PORT_RREG = 32049
            self.femb.UDP_PORT_RREGRESP = 32050

        if fembVal == 3:
            self.femb.UDP_PORT_WREG = 32064
            self.femb.UDP_PORT_RREG = 32065
            self.femb.UDP_PORT_RREGRESP = 32066

        #slow down register interface for FEMBs
        self.femb.REG_SLEEP = 0.05

    def initSI5338(self):
        #set UDP ports to WIB
        self.femb.UDP_PORT_WREG = 32000
        self.femb.UDP_PORT_RREG = 32001
        self.femb.UDP_PORT_RREGRESP = 32002
        self.femb.REG_SLEEP = 0.001

        #disable all outputs
        #i2c_reg_wr(i2c_bus_base_addr, si5338_i2c_addr, 230, 0x10);
        self.write_reg_SI5338(230, 0x10)

        #pause lol
        #i2c_reg_wr(i2c_bus_base_addr, si5338_i2c_addr, 241, 0xE5);
        self.write_reg_SI5338(241, 0xE5)

        import femb_python.configuration.femb_config_wib_sbnd_si5338_data
        for word in range(0, 349, 1):
            wordNum = int(word)
            addr = int(femb_python.configuration.
                       femb_config_wib_sbnd_si5338_data.data[3 * wordNum + 0])
            val = int(femb_python.configuration.
                      femb_config_wib_sbnd_si5338_data.data[3 * wordNum + 1])
            mask = int(femb_python.configuration.
                       femb_config_wib_sbnd_si5338_data.data[3 * wordNum + 2])

            if wordNum % 10 == 0:
                print("Writing SI5338 register # " + str(wordNum) +
                      " out of 349")

            if mask == 0:
                continue

            writeVal = val
            if mask != 0xFF:
                curr_val = self.read_reg_SI5338(addr)
                clear_curr_val = curr_val & (~mask)
                clear_new_val = val & mask
                writeVal = clear_curr_val | clear_new_val
            self.write_reg_SI5338(addr, writeVal)
            #print(str(addr) + "\t" + str(writeVal))

        #validate input clock status
#i2c_reg_rd(i2c_bus_base_addr, si5338_i2c_addr, 218);
        clkStatus = (self.read_reg_SI5338(218) & 0x04)
        count = 0
        while count < 100:
            clkStatus = (self.read_reg_SI5338(218) & 0x04)
            if clkStatus != 0x04:
                break
            count = count + 1
        if clkStatus == 0x04:
            print("Did not finish clock initialization")
            return

        #configure pll
        pllWord = int(
            femb_python.configuration.femb_config_wib_sbnd_si5338_data.data[
                3 * 49 + 1])
        self.write_reg_SI5338(49, (0x7F & pllWord))

        #reset the chip
        self.write_reg_SI5338(246, 0x02)

        time.sleep(0.1)

        #restart lol
        self.write_reg_SI5338(241, 0x65)

        #validate pll
        pllStatus = self.read_reg_SI5338(218)
        count = 0
        while count < 100:
            pllStatus = self.read_reg_SI5338(218)
            if pllStatus == 0:
                break
            count = count + 1
        if pllStatus != 0:
            print("Did not finish clock initialization")
            return

        #copy FCAL values to active registers
        fcalVal = self.read_reg_SI5338(235)
        self.write_reg_SI5338(45, fcalVal)

        fcalVal = self.read_reg_SI5338(236)
        self.write_reg_SI5338(46, fcalVal)

        fcalVal = self.read_reg_SI5338(237)
        fcalVal = (0x14 | (fcalVal & 0x3))
        self.write_reg_SI5338(47, fcalVal)

        #set pll to use FCAL values
        #i2c_reg_wr(i2c_bus_base_addr, si5338_i2c_addr, 49, 0x80|SI5338Reg[49*3+1]);
        setPllWord = (0x80 | pllWord)
        self.write_reg_SI5338(49, setPllWord)

        #enable outputs
        self.write_reg_SI5338(230, 0x00)
        print("Done initalizing Si5338 clock")

    def read_reg_SI5338(self, addr):
        addrVal = int(addr)
        if (addrVal < 0) or (addrVal > 255):
            return
        self.femb.write_reg(11, 0)
        self.femb.write_reg(12, addrVal)
        self.femb.write_reg(15, 0xE0)

        self.femb.write_reg(10, 1)
        self.femb.write_reg(10, 0)

        self.femb.write_reg(11, 1)

        self.femb.write_reg(10, 2)
        self.femb.write_reg(10, 0)

        regVal = self.femb.read_reg(14)
        return regVal

    def write_reg_SI5338(self, addr, val):
        addrVal = int(addr)
        if (addrVal < 0) or (addrVal > 255):
            return
        regVal = int(val)
        if (regVal < 0) or (regVal > 255):
            return
        self.femb.write_reg(11, 1)
        self.femb.write_reg(12, addrVal)
        self.femb.write_reg(13, regVal)

        self.femb.write_reg(10, 1)
        self.femb.write_reg(10, 0)

    def setFpgaPulser(self, enable, dac):
        enableVal = int(enable)
        if (enableVal < 0) or (enableVal > 1):
            print("femb_config_femb : setFpgaPulser - invalid enable value")
            return
        dacVal = int(dac)
        if (dacVal < 0) or (dacVal > 0x3F):
            print("femb_config_femb : setFpgaPulser - invalid dac value")
            return

        #set pulser enable bit
        if enableVal == 1:
            self.femb.write_reg(
                self.EXT_TP_EN,
                0x2)  #pulser enabled, bit 0 is FPGA pulser NOT enabled
        else:
            self.femb.write_reg(self.EXT_TP_EN, 0x3)  #pulser disabled

        #connect channel test input to external pin
        for asic in range(0, self.NASICS, 1):
            baseReg = self.REG_SPI_BASE + int(asic) * 9
            if enableVal == 1:
                self.femb.write_reg_bits(baseReg + 8, 24, 0x3,
                                         0x2)  #ASIC gen reg
            else:
                self.femb.write_reg_bits(baseReg + 8, 24, 0x3,
                                         0x0)  #ASIC gen reg

        self.doAsicConfig()

        self.femb.write_reg_bits(self.REG_FPGA_TP_EN, 0, 0x3,
                                 0x1)  #test pulse enable
        self.femb.write_reg_bits(self.REG_FPGA_TP_EN, 8, 0x1,
                                 1)  #test pulse enable
        self.femb.write_reg_bits(self.REG_TP, 0, 0x3F, dacVal)  #TP Amplitude
        self.femb.write_reg_bits(self.REG_TP, 8, 0xFF, 219)  #DLY
        self.femb.write_reg_bits(self.REG_TP, 16, 0xFFFF, 197)  #FREQ

    def setInternalPulser(self, enable, dac):
        enableVal = int(enable)
        if (enableVal < 0) or (enableVal > 1):
            print(
                "femb_config_femb : setInternalPulser - invalid enable value")
            return
        dacVal = int(dac)
        if (dacVal < 0) or (dacVal > 0x3F):
            print("femb_config_femb : setInternalPulser - invalid dac value")
            return

        #set pulser enable bit
        if enableVal == 1:
            self.femb.write_reg(self.INT_TP_EN, 0x1)  #pulser enabled
        else:
            self.femb.write_reg(self.INT_TP_EN, 0x3)  #pulser disabled

        dacVal = (dacVal & 0x3F)
        newDacVal = int('{:08b}'.format(dacVal)[::-1], 2)

        asicWord = ((newDacVal << 8) & 0xFFFF)
        if enableVal == 1:
            asicWord = asicWord + (0x1 << 8)

        #connect channel test input to external pin
        for asic in range(0, self.NASICS, 1):
            baseReg = self.REG_SPI_BASE + int(asic) * 9
            if enableVal == 1:
                self.femb.write_reg_bits(baseReg + 8, 24, 0xFF, newDacVal)
                self.femb.write_reg_bits(baseReg + 8, 24, 0x3,
                                         0x1)  #ASIC gen reg
            else:
                self.femb.write_reg_bits(baseReg + 8, 24, 0xFF,
                                         0x0)  #ASIC gen reg

        self.doAsicConfig()

        self.femb.write_reg_bits(self.REG_ASIC_TP_EN, 0, 0x3, 0x2)
        self.femb.write_reg_bits(self.REG_DAC_SELECT, 8, 0x1,
                                 0)  #test pulse enable
        self.femb.write_reg_bits(self.REG_TP, 0, 0x3F, dacVal)  #TP Amplitude
        self.femb.write_reg_bits(self.REG_TP, 8, 0xFF, 219)  #DLY
        self.femb.write_reg_bits(self.REG_TP, 16, 0xFFFF, 197)  #FREQ
class FEMB_CONFIG(FEMB_CONFIG_BASE):
    def __init__(self, exitOnError=True):
        super().__init__(exitOnError=exitOnError)
        #declare board specific registers
        self.FEMB_VER = "adctestP1single"
        self.REG_RESET = 0
        self.REG_ASIC_RESET = 1
        self.REG_ASIC_SPIPROG = 2
        self.REG_SEL_CH = 7
        self.REG_HS = 17
        self.REG_FESPI_BASE = 0x250  # 592 in decimal
        self.REG_ADCSPI_BASE = 0x200  # 512 in decimal
        self.REG_FESPI_RDBACK_BASE = 0x278  # 632 in decimal
        self.REG_ADCSPI_RDBACK_BASE = 0x228  # 552 in decimal
        self.REG_LATCHLOC1_4 = 4
        self.REG_LATCHLOC5_8 = 14
        self.REG_CLKPHASE = 6

        self.REG_LATCHLOC1_4_data = 0x6
        self.REG_LATCHLOC5_8_data = 0x0
        self.REG_CLKPHASE_data = 0xfffc0000

        self.REG_LATCHLOC1_4_data_1MHz = 0x5
        self.REG_LATCHLOC5_8_data_1MHz = 0x0
        self.REG_CLKPHASE_data_1MHz = 0xffff0000

        self.REG_LATCHLOC1_4_data_cold = 0x6
        self.REG_LATCHLOC5_8_data_cold = 0x0
        self.REG_CLKPHASE_data_cold = 0xfffc0000

        self.REG_LATCHLOC1_4_data_1MHz_cold = 0x4
        self.REG_LATCHLOC5_8_data_1MHz_cold = 0x0
        self.REG_CLKPHASE_data_1MHz_cold = 0xfffc0001

        self.ADC_TESTPATTERN = [
            0x12, 0x345, 0x678, 0xf1f, 0xad, 0xc01, 0x234, 0x567, 0x89d, 0xeca,
            0xff0, 0x123, 0x456, 0x789, 0xabc, 0xdef
        ]

        ##################################
        # external clock control registers
        ##################################
        self.FPGA_FREQ_MHZ = 200  # frequency of FPGA clock in MHz
        self.REG_EXTCLK_RD_EN_OFF = 23
        self.REG_EXTCLK_ADC_OFF = 21
        self.REG_EXTCLK_ADC_WID = 22
        self.REG_EXTCLK_MSB_OFF = 25
        self.REG_EXTCLK_MSB_WID = 26
        self.REG_EXTCLK_PERIOD = 20
        self.REG_EXTCLK_LSB_FC_WID2 = 32
        self.REG_EXTCLK_LSB_FC_OFF1 = 29
        self.REG_EXTCLK_RD_EN_WID = 24
        self.REG_EXTCLK_LSB_FC_WID1 = 30
        self.REG_EXTCLK_LSB_FC_OFF2 = 31
        self.REG_EXTCLK_LSB_S_WID = 28
        self.REG_EXTCLK_LSB_S_OFF = 27
        self.REG_EXTCLK_INV = 33
        ##################################
        ##################################

        self.NASICS = 1
        self.FUNCGENINTER = Keysight_33600A("/dev/usbtmc1", 1)
        self.POWERSUPPLYINTER = RigolDP800(
            "/dev/usbtmc0", ["CH2", "CH3", "CH1"])  # turn on CH2 first
        self.F2DEFAULT = 0
        self.CLKDEFAULT = "fifo"

        ## Firmware update related variables
        self.FIRMWAREPATH2MHZ = "/opt/sw/releases/femb_firmware-0.1.0/adc_tester/S7_2M_SBND_FPGA.sof"
        self.FIRMWAREPATH1MHZ = "/opt/sw/releases/femb_firmware-0.1.0/adc_tester/S7_1M_SBND_FPGA.sof"
        self.FIRMWAREPROGEXE = "/opt/sw/intelFPGA/17.0/qprogrammer/bin/quartus_pgm"
        #self.FIRMWAREPROGCABLE = "USB-Blaster"
        self.FIRMWAREPROGCABLE = "USB-BlasterII"
        self.SAMPLERATE = 2e6

        #initialize FEMB UDP object
        self.femb = FEMB_UDP()
        self.adc_reg = ADC_ASIC_REG_MAPPING()

    def resetBoard(self):
        #Reset system
        self.femb.write_reg(self.REG_RESET, 1)
        time.sleep(5.)

        #Reset registers
        self.femb.write_reg(self.REG_RESET, 2)
        time.sleep(1.)

        #Time stamp reset
        #femb.write_reg( 0, 4)
        #time.sleep(0.5)

        #Reset ADC ASICs
        self.femb.write_reg(self.REG_ASIC_RESET, 1)
        time.sleep(0.5)

    def initBoard(self):
        nRetries = 5
        for iRetry in range(nRetries):
            #set up default registers

            #Reset ADC ASICs
            self.femb.write_reg(self.REG_ASIC_RESET, 1)
            time.sleep(0.5)

            readback = self.femb.read_reg(0)
            if readback is None:
                if self.exitOnError:
                    print("FEMB_CONFIG: Error reading register 0, Exiting.")
                    sys.exit(1)
                else:
                    raise ReadRegError("Couldn't read register 0")

            #Set ADC test pattern register
            self.femb.write_reg(3, 0x01170000)  # test pattern off
            #self.femb.write_reg( 3, 0x81170000) # test pattern on

            #Set ADC latch_loc and clock phase
            latchloc1 = None
            latchloc5 = None
            clockphase = None
            if self.SAMPLERATE == 1e6:
                if self.COLD:
                    print("Using 1 MHz cold latchloc/clockphase")
                    latchloc1 = self.REG_LATCHLOC1_4_data_1MHz_cold
                    latchloc5 = self.REG_LATCHLOC5_8_data_1MHz_cold
                    clockphase = self.REG_CLKPHASE_data_1MHz_cold
                else:
                    print("Using 1 MHz warm latchloc/clockphase")
                    latchloc1 = self.REG_LATCHLOC1_4_data_1MHz
                    latchloc5 = self.REG_LATCHLOC5_8_data_1MHz
                    clockphase = self.REG_CLKPHASE_data_1MHz
            else:  # use 2 MHz values
                if self.COLD:
                    print("Using 2 MHz cold latchloc/clockphase")
                    latchloc1 = self.REG_LATCHLOC1_4_data_cold
                    latchloc5 = self.REG_LATCHLOC5_8_data_cold
                    clockphase = self.REG_CLKPHASE_data_cold
                else:
                    print("Using 2 MHz warm latchloc/clockphase")
                    latchloc1 = self.REG_LATCHLOC1_4_data
                    latchloc5 = self.REG_LATCHLOC5_8_data
                    clockphase = self.REG_CLKPHASE_data

            print(
                "Initializing with Latch Loc: {:#010x} {:#010x} Clock Phase: {:#010x}"
                .format(latchloc1, latchloc5, clockphase))
            self.femb.write_reg(self.REG_LATCHLOC1_4, latchloc1)
            self.femb.write_reg(self.REG_LATCHLOC5_8, latchloc5)
            for iTry in range(5):
                self.femb.write_reg(self.REG_CLKPHASE, ~clockphase)
                time.sleep(0.05)
                self.femb.write_reg(self.REG_CLKPHASE, ~clockphase)
                time.sleep(0.05)
                self.femb.write_reg(self.REG_CLKPHASE, clockphase)
                time.sleep(0.05)
                self.femb.write_reg(self.REG_CLKPHASE, clockphase)
                time.sleep(0.05)

            print("Readback: ", self.getClockStr())

            #internal test pulser control
            self.femb.write_reg(5, 0x00000000)
            self.femb.write_reg(13, 0x0)  #enable

            #Set test and readout mode register
            self.femb.write_reg(
                self.REG_HS,
                0x0)  # 0 readout all 15 channels, 1 readout only selected one
            self.femb.write_reg(
                self.REG_SEL_CH,
                0x0000)  #11-8 = channel select, 3-0 = ASIC select

            #Set number events per header
            self.femb.write_reg(8, 0x0)

            #Configure ADC (and external clock inside)
            try:
                self.configAdcAsic()
                #self.configAdcAsic(clockMonostable=True)
            except ReadRegError:
                continue
            # Check that board streams data
            data = self.femb.get_data(1)
            if data == None:
                print("Board not streaming data, retrying initialization...")
                continue  # try initializing again
            print("FEMB_CONFIG--> Reset FEMB is DONE")
            return
        print(
            "Error: Board not streaming data after trying to initialize {} times."
            .format(nRetries))
        if self.exitOnError:
            print("Exiting.")
            sys.exit(1)
        else:
            raise InitBoardError

    def configAdcAsic_regs(self, Adcasic_regs):
        #ADC ASIC SPI registers
        assert (len(Adcasic_regs) == 36)
        print("FEMB_CONFIG--> Config ADC ASIC SPI")
        for k in range(10):
            i = 0
            for regNum in range(self.REG_ADCSPI_BASE,
                                self.REG_ADCSPI_BASE + len(Adcasic_regs), 1):
                self.femb.write_reg(regNum, Adcasic_regs[i])
                time.sleep(0.05)
                i = i + 1

            #print("  ADC ASIC write : ",Adcasic_regs)
            #ADC ASIC sync -- Justin: I don't think this exists anymore
            #self.femb.write_reg ( 17, 0x1) # controls HS link, 0 for on, 1 for off
            #self.femb.write_reg ( 17, 0x0) # controls HS link, 0 for on, 1 for off

            #Write ADC ASIC SPI
            print("FEMB_CONFIG--> Program ADC ASIC SPI")
            self.femb.write_reg(self.REG_ASIC_RESET, 1)
            time.sleep(0.1)
            self.femb.write_reg(self.REG_ASIC_SPIPROG, 1)
            time.sleep(0.1)
            self.femb.write_reg(self.REG_ASIC_SPIPROG, 1)
            time.sleep(0.1)

            #enable streaming
            #self.femb.write_reg( 9, 0x1)

            #LBNE_ADC_MODE
            self.femb.write_reg(18, 0x1)

            print("FEMB_CONFIG--> Check ADC ASIC SPI")
            adcasic_rb_regs = []
            for regNum in range(
                    self.REG_ADCSPI_RDBACK_BASE,
                    self.REG_ADCSPI_RDBACK_BASE + len(Adcasic_regs), 1):
                val = self.femb.read_reg(regNum)
                if val is None:
                    message = "Error in FEMB_CONFIG.configAdcAsic_regs: read from board failed"
                    print(message)
                    if self.exitOnError:
                        return
                    else:
                        raise ReadRegError
                adcasic_rb_regs.append(val)

            #print("{:32}  {:32}".format("Write","Readback"))
            #print("{:8}  {:8}".format("Write","Readback"))
            # we only get 15 LSBs back so miss D0 for a channel and CLK0
            readbackMatch = True
            for regNum in range(36):
                write_val = Adcasic_regs[regNum]  #& 0x7FFF
                readback_val = adcasic_rb_regs[(regNum + 9) % 36] >> 1
                # we only get the 15 LSBs back
                if readback_val != (Adcasic_regs[regNum] & 0x7FFF):
                    readbackMatch = False
                #print("{:032b}  {:032b}".format(write_val,readback_val))
                #print("{:08X}  {:08X}".format(write_val,readback_val))

            if readbackMatch:
                print("FEMB_CONFIG--> ADC ASIC SPI is OK")
                return
            else:
                print(
                    "FEMB_CONFIG--> ADC ASIC Readback didn't match, retrying..."
                )
        print("Error: Wrong ADC SPI readback.")
        if self.exitOnError:
            print("Exiting.")
            sys.exit(1)
        else:
            raise ConfigADCError

    def configAdcAsic(self,
                      enableOffsetCurrent=None,
                      offsetCurrent=None,
                      testInput=None,
                      freqInternal=None,
                      sleep=None,
                      pdsr=None,
                      pcsr=None,
                      clockMonostable=None,
                      clockExternal=None,
                      clockFromFIFO=None,
                      sLSB=None,
                      f0=None,
                      f1=None,
                      f2=None,
                      f3=None,
                      f4=None,
                      f5=None):
        """
        Configure ADCs
          enableOffsetCurrent: 0 disable offset current, 1 enable offset current
          offsetCurrent: 0-15, amount of current to draw from sample and hold
          testInput: 0 digitize normal input, 1 digitize test input
          freqInternal: internal clock frequency: 0 1MHz, 1 2MHz
          sleep: 0 disable sleep mode, 1 enable sleep mode
          pdsr: if pcsr=0: 0 PD is low, 1 PD is high
          pcsr: 0 power down controlled by pdsr, 1 power down controlled externally
          Only one of these can be enabled:
            clockMonostable: True ADC uses monostable clock
            clockExternal: True ADC uses external clock
            clockFromFIFO: True ADC uses digital generator FIFO clock
          sLSB: LSB current steering mode. 0 for full, 1 for partial (ADC7 P1)
          f0, f1, f2, f3, f4, f5: version specific
        """
        FEMB_CONFIG_BASE.configAdcAsic(self,
                                       clockMonostable=clockMonostable,
                                       clockExternal=clockExternal,
                                       clockFromFIFO=clockFromFIFO)
        if enableOffsetCurrent is None:
            enableOffsetCurrent = 0
        if offsetCurrent is None:
            offsetCurrent = 0
        else:
            offsetCurrent = int(
                "{:04b}".format(offsetCurrent)[::-1],
                2)  # need to reverse bits, use string/list tricks
        if testInput is None:
            testInput = 1
        if freqInternal is None:
            freqInternal = 1
        if sleep is None:
            sleep = 0
        if pdsr is None:
            pdsr = 0
        if pcsr is None:
            pcsr = 0
        if sLSB is None:
            sLSB = 0
        if f1 is None:
            f1 = 0
        if f2 is None:
            f2 = 0
        if f3 is None:
            f3 = 0
        if f4 is None:
            f4 = 1
        if f5 is None:
            f5 = 0
        if not (clockMonostable or clockExternal or clockFromFIFO):
            clockExternal = True
        # a bunch of things depend on the clock choice
        clk0 = 0
        clk1 = 0
        if clockExternal:
            clk0 = 1
            clk1 = 0
        elif clockFromFIFO:
            clk0 = 0
            clk1 = 1
        if f0 is None:
            if clockExternal:
                f0 = 1
            else:
                f0 = 0
        if clockExternal:
            self.extClock(enable=True)
        else:
            self.extClock(enable=False)

        self.adc_reg.set_sbnd_board(en_gr=enableOffsetCurrent,
                                    d=offsetCurrent,
                                    tstin=testInput,
                                    frqc=freqInternal,
                                    slp=sleep,
                                    pdsr=pdsr,
                                    pcsr=pcsr,
                                    clk0=clk0,
                                    clk1=clk1,
                                    f0=f0,
                                    f1=f1,
                                    f2=f2,
                                    f3=f3,
                                    f4=f4,
                                    f5=f5,
                                    slsb=sLSB)
        self.configAdcAsic_regs(self.adc_reg.REGS)

    def selectChannel(self, asic, chan, hsmode=1, singlechannelmode=None):
        """
        asic is chip number 0 to 7
        chan is channel within asic from 0 to 15
        hsmode: if 0 then streams all channels of a chip, if 1 only te selected channel. defaults to 1
        singlechannelmode: not implemented
        """
        hsmodeVal = int(hsmode) & 1
        asicVal = int(asic)
        if (asicVal < 0) or (asicVal >= self.NASICS):
            print(
                "femb_config_femb : selectChan - invalid ASIC number, only 0 to {} allowed"
                .format(self.NASICS - 1))
            return
        chVal = int(chan)
        if (chVal < 0) or (chVal > 15):
            print(
                "femb_config_femb : selectChan - invalid channel number, only 0 to 15 allowed"
            )
            return

        #print( "Selecting ASIC " + str(asicVal) + ", channel " + str(chVal))

        self.femb.write_reg(self.REG_HS, hsmodeVal)
        regVal = (chVal << 8) + asicVal
        self.femb.write_reg(self.REG_SEL_CH, regVal)

    def syncADC(self, iASIC=None):
        #turn on ADC test mode
        print("FEMB_CONFIG--> Start sync ADC")
        reg3 = self.femb.read_reg(3)
        newReg3 = (reg3 | 0x80000000)

        self.femb.write_reg(3, newReg3)  #31 - enable ADC test pattern
        time.sleep(0.1)

        alreadySynced = True
        for a in range(0, self.NASICS, 1):
            print("FEMB_CONFIG--> Test ADC " + str(a))
            unsync, syncDicts = self.testUnsync(a)
            if unsync != 0:
                alreadySynced = False
                print("FEMB_CONFIG--> ADC not synced, try to fix")
                self.fixUnsync(a)
        latchloc1_4 = self.femb.read_reg(self.REG_LATCHLOC1_4)
        latchloc5_8 = self.femb.read_reg(self.REG_LATCHLOC5_8)
        clkphase = self.femb.read_reg(self.REG_CLKPHASE)
        if self.SAMPLERATE == 1e6:
            if self.COLD:
                self.REG_LATCHLOC1_4_data_1MHz_cold = latchloc1_4
                self.REG_LATCHLOC5_8_data_1MHz_cold = latchloc5_8
                self.REG_CLKPHASE_data_1MHz_cold = clkphase
            else:
                self.REG_LATCHLOC1_4_data_1MHz = latchloc1_4
                self.REG_LATCHLOC5_8_data_1MHz = latchloc5_8
                self.REG_CLKPHASE_data_1MHz = clkphase
        else:  # 2 MHz
            if self.COLD:
                self.REG_LATCHLOC1_4_data_cold = latchloc1_4
                self.REG_LATCHLOC5_8_data_cold = latchloc5_8
                self.REG_CLKPHASE_data_cold = clkphase
            else:
                self.REG_LATCHLOC1_4_data = latchloc1_4
                self.REG_LATCHLOC5_8_data = latchloc5_8
                self.REG_CLKPHASE_data = clkphase
        print("FEMB_CONFIG--> Latch latency {:#010x} {:#010x} Phase: {:#010x}".
              format(latchloc1_4, latchloc5_8, clkphase))
        self.femb.write_reg(3, (reg3 & 0x7fffffff))
        self.femb.write_reg(3, (reg3 & 0x7fffffff))
        print("FEMB_CONFIG--> End sync ADC")
        return not alreadySynced, latchloc1_4, latchloc5_8, clkphase

    def testUnsync(self, adc, npackets=10):
        print("Starting testUnsync adc: ", adc)
        adcNum = int(adc)
        if (adcNum < 0) or (adcNum > 7):
            print(
                "FEMB_CONFIG--> femb_config_femb : testLink - invalid asic number"
            )
            return

        #loop through channels, check test pattern against data
        syncDataCounts = [{} for i in range(16)]  #dict for each channel
        for ch in range(0, 16, 1):
            self.selectChannel(adcNum, ch, 1)
            time.sleep(0.05)
            data = self.femb.get_data(npackets)
            if data == None:
                continue
            for samp in data:
                if samp == None:
                    continue
                #chNum = ((samp >> 12 ) & 0xF)
                sampVal = (samp & 0xFFF)
                if sampVal in syncDataCounts[ch]:
                    syncDataCounts[ch][sampVal] += 1
                else:
                    syncDataCounts[ch][sampVal] = 1
        # check jitter
        badSync = 0
        maxCodes = [None] * 16
        syncDicts = [{}] * 16
        for ch in range(0, 16, 1):
            sampSum = 0
            maxCode = None
            nMaxCode = 0
            for code in syncDataCounts[ch]:
                nThisCode = syncDataCounts[ch][code]
                sampSum += nThisCode
                if nThisCode > nMaxCode:
                    nMaxCode = nThisCode
                    maxCode = code
            maxCodes[ch] = maxCode
            syncDicts[ch]["maxCode"] = maxCode
            syncDicts[ch]["nSamplesMaxCode"] = nMaxCode
            syncDicts[ch]["nSamples"] = sampSum
            syncDicts[ch]["zeroJitter"] = True
            if len(syncDataCounts[ch]) > 1:
                syncDicts[ch]["zeroJitter"] = False
                badSync = 1
                diff = sampSum - nMaxCode
                frac = diff / float(sampSum)
                print("Sync Error: Jitter for Ch {:2}: {:8.4%} ({:5}/{:5})".
                      format(ch, frac, diff, sampSum))
        for ch in range(0, 16, 1):
            maxCode = maxCodes[ch]
            correctCode = self.ADC_TESTPATTERN[ch]
            syncDicts[ch]["data"] = True
            syncDicts[ch]["maxCodeMatchesExpected"] = True
            if maxCode is None:
                syncDicts[ch]["data"] = False
                badSync = 1
                print("Sync Error: no data for ch {:2}".format(ch))
            elif maxCode != correctCode:
                syncDicts[ch]["maxCodeMatchesExpected"] = True
                badSync = 1
                print(
                    "Sync Error: mismatch for ch {:2}: expected {:#03x} observed {:#03x}"
                    .format(ch, correctCode, maxCode))
        return badSync, syncDicts

    def fixUnsync(self, adc):
        adcNum = int(adc)
        if (adcNum < 0) or (adcNum > 7):
            print(
                "FEMB_CONFIG--> femb_config_femb : testLink - invalid asic number"
            )
            return

        initLATCH1_4 = self.femb.read_reg(self.REG_LATCHLOC1_4)
        initLATCH5_8 = self.femb.read_reg(self.REG_LATCHLOC5_8)
        initPHASE = self.femb.read_reg(self.REG_CLKPHASE)

        phases = [0, 1]
        if self.COLD:
            phases = [0, 1, 0, 1, 0]

        #loop through sync parameters
        for shift in range(0, 16, 1):
            shiftMask = (0x3F << 8 * adcNum)
            if (adcNum < 4):
                testShift = ((initLATCH1_4 & ~(shiftMask)) |
                             (shift << 8 * adcNum))
                self.femb.write_reg(self.REG_LATCHLOC1_4, testShift)
                time.sleep(0.01)
            else:
                testShift = ((initLATCH5_8 & ~(shiftMask)) |
                             (shift << 8 * adcNum))
                self.femb.write_reg(self.REG_LATCHLOC5_8, testShift)
                time.sleep(0.01)
            for phase in phases:
                clkMask = (0x1 << adcNum)
                testPhase = ((initPHASE & ~(clkMask)) | (phase << adcNum))
                self.femb.write_reg(self.REG_CLKPHASE, testPhase)
                time.sleep(0.01)
                print("try shift: {} phase: {} testingUnsync...".format(
                    shift, phase))
                print(
                    "     initPHASE: {:#010x}, phase: {:#010x}, testPhase: {:#010x}"
                    .format(initPHASE, phase, testPhase))
                #reset ADC ASIC
                self.femb.write_reg(self.REG_ASIC_RESET, 1)
                time.sleep(0.01)
                self.femb.write_reg(self.REG_ASIC_SPIPROG, 1)
                time.sleep(0.01)
                self.femb.write_reg(self.REG_ASIC_SPIPROG, 1)
                time.sleep(0.01)
                #test link
                unsync, syncDicts = self.testUnsync(adcNum)
                if unsync == 0:
                    print("FEMB_CONFIG--> ADC synchronized")
                    return
        #if program reaches here, sync has failed
        print("Error: FEMB_CONFIG--> ADC SYNC process failed for ADC # " +
              str(adc))
        print(
            "Setting back to original values: LATCHLOC1_4: {:#010x}, LATCHLOC5_8: {:#010x}, PHASE: {:#010x}"
            .format, initLATCH1_4, initLATCH5_8, initPHASE)
        self.femb.write_reg(self.REG_LATCHLOC1_4, initLATCH1_4)
        self.femb.write_reg(self.REG_LATCHLOC5_8, initLATCH5_8)
        self.femb.write_reg(self.REG_CLKPHASE, initPHASE)
        if self.exitOnError:
            sys.exit(1)
        else:
            raise SyncADCError

    def extClock(self,
                 enable=False,
                 period=500,
                 mult=1,
                 offset_rst=0,
                 offset_read=480,
                 offset_msb=230,
                 offset_lsb=480,
                 width_rst=50,
                 width_read=20,
                 width_msb=270,
                 width_lsb=20,
                 offset_lsb_1st_1=50,
                 width_lsb_1st_1=190,
                 offset_lsb_1st_2=480,
                 width_lsb_1st_2=20,
                 inv_rst=True,
                 inv_read=True,
                 inv_msb=False,
                 inv_lsb=False,
                 inv_lsb_1st=False):
        """
        Programs external clock. All non-boolean arguments except mult are in nanoseconds
        """

        rd_en_off = 0
        adc_off = 0
        adc_wid = 0
        msb_off = 0
        msb_wid = 0
        period_val = 0
        lsb_fc_wid2 = 0
        lsb_fc_off1 = 0
        rd_en_wid = 0
        lsb_fc_wid1 = 0
        lsb_fc_off2 = 0
        lsb_s_wid = 0
        lsb_s_off = 0
        inv = 0

        if enable:
            clock = 1. / self.FPGA_FREQ_MHZ * 1000.  # clock now in ns
            #print("FPGA Clock freq: {} MHz period: {} ns".format(self.FPGA_FREQ_MHZ,clock))
            #print("ExtClock option mult: {}".format(mult))
            #print("ExtClock option period: {} ns".format(period))
            #print("ExtClock option offset_read: {} ns".format(offset_read))
            #print("ExtClock option offset_rst: {} ns".format(offset_rst))
            #print("ExtClock option offset_msb: {} ns".format(offset_msb))
            #print("ExtClock option offset_lsb: {} ns".format(offset_lsb))
            #print("ExtClock option offset_lsb_1st_1: {} ns".format(offset_lsb_1st_1))
            #print("ExtClock option offset_lsb_1st_2: {} ns".format(offset_lsb_1st_2))
            #print("ExtClock option width_read: {} ns".format(width_read))
            #print("ExtClock option width_rst: {} ns".format(width_rst))
            #print("ExtClock option width_msb: {} ns".format(width_msb))
            #print("ExtClock option width_lsb: {} ns".format(width_lsb))
            #print("ExtClock option width_lsb_1st_1: {} ns".format(width_lsb_1st_1))
            #print("ExtClock option width_lsb_1st_2: {} ns".format(width_lsb_1st_2))
            #print("ExtClock option inv_rst: {}".format(inv_rst))
            #print("ExtClock option inv_read: {}".format(inv_read))
            #print("ExtClock option inv_msb: {}".format(inv_msb))
            #print("ExtClock option inv_lsb: {}".format(inv_lsb))
            #print("ExtClock option inv_lsb_1st: {}".format(inv_lsb_1st))
            denominator = clock / mult
            #print("ExtClock denominator: {} ns".format(denominator))
            period_val = period // denominator
            rd_en_off = offset_read // denominator
            adc_off = offset_rst // denominator
            adc_wid = width_rst // denominator
            msb_off = offset_msb // denominator
            msb_wid = width_msb // denominator
            lsb_fc_wid2 = width_lsb_1st_2 // denominator
            lsb_fc_off1 = offset_lsb_1st_1 // denominator
            rd_en_wid = width_read // denominator
            lsb_fc_wid1 = width_lsb_1st_1 // denominator
            lsb_fc_off2 = offset_lsb_1st_2 // denominator
            lsb_s_wid = width_lsb // denominator
            lsb_s_off = offset_lsb // denominator
            if inv_rst:
                inv += 1 << 0
            if inv_read:
                inv += 1 << 1
            if inv_msb:
                inv += 1 << 2
            if inv_lsb:
                inv += 1 << 3
            if inv_lsb_1st:
                inv += 1 << 4

        regsValsToWrite = [
            ("rd_en_off", self.REG_EXTCLK_RD_EN_OFF, rd_en_off),
            ("adc_off", self.REG_EXTCLK_ADC_OFF, adc_off),
            ("adc_wid", self.REG_EXTCLK_ADC_WID, adc_wid),
            ("msb_off", self.REG_EXTCLK_MSB_OFF, msb_off),
            ("msb_wid", self.REG_EXTCLK_MSB_WID, msb_wid),
            ("period", self.REG_EXTCLK_PERIOD, period_val),
            ("lsb_fc_wid2", self.REG_EXTCLK_LSB_FC_WID2, lsb_fc_wid2),
            ("lsb_fc_off1", self.REG_EXTCLK_LSB_FC_OFF1, lsb_fc_off1),
            ("rd_en_wid", self.REG_EXTCLK_RD_EN_WID, rd_en_wid),
            ("lsb_fc_wid1", self.REG_EXTCLK_LSB_FC_WID1, lsb_fc_wid1),
            ("lsb_fc_off2", self.REG_EXTCLK_LSB_FC_OFF2, lsb_fc_off2),
            ("lsb_s_wid", self.REG_EXTCLK_LSB_S_WID, lsb_s_wid),
            ("lsb_s_off", self.REG_EXTCLK_LSB_S_OFF, lsb_s_off),
            ("inv", self.REG_EXTCLK_INV, inv),
        ]
        for name, reg, val in regsValsToWrite:
            val = int(val) & 0xFFFF  # only 16 bits for some reason
            #print("ExtClock Register {0:12} number {1:3} set to {2:5} = {2:#06x}".format(name,reg,val))
            self.femb.write_reg(reg, val)

    def programFirmware(self, firmware):
        """
        Programs the FPGA using the firmware file given.
        """
        if self.FIRMWAREPROGCABLE == "USB-BlasterII":
            # this programmer is too fast for our board
            # (or linux or something) so we have to slow it down
            jtagconfig_commandline = os.path.dirname(self.FIRMWAREPROGEXE)
            jtagconfig_commandline = os.path.join(jtagconfig_commandline,
                                                  "jtagconfig")
            jtagconfig_commandline += " --setparam 1 JtagClock 6M"
            print(jtagconfig_commandline)
            subprocess.run(jtagconfig_commandline.split(), check=True)
        commandline = "{} -c {} -m jtag -o p;{}".format(
            self.FIRMWAREPROGEXE, self.FIRMWAREPROGCABLE, firmware)
        commandlinelist = commandline.split()
        print(commandline)
        print(commandlinelist)
        subprocess.run(commandlinelist, check=True)

    def checkFirmwareProgrammerStatus(self):
        """
        Prints a debug message for the firmware programmer
        """
        jtagconfig_commandline = os.path.dirname(self.FIRMWAREPROGEXE)
        jtagconfig_commandline = os.path.join(jtagconfig_commandline,
                                              "jtagconfig")
        if self.FIRMWAREPROGCABLE == "USB-BlasterII":
            # this programmer is too fast for our board
            # (or linux or something) so we have to slow it down
            jtagconfig_commandline_speed = jtagconfig_commandline + " --setparam 1 JtagClock 6M"
            print(jtagconfig_commandline_speed)
            subprocess.run(jtagconfig_commandline_speed.split())
        subprocess.run(jtagconfig_commandline.split())

    def programFirmware1Mhz(self):
        self.programFirmware(self.FIRMWAREPATH1MHZ)
        self.SAMPLERATE = 1e6

    def programFirmware2Mhz(self):
        self.programFirmware(self.FIRMWAREPATH2MHZ)
        self.SAMPLERATE = 2e6

    def getClockStr(self):
        latchloc1 = self.femb.read_reg(self.REG_LATCHLOC1_4)
        latchloc5 = self.femb.read_reg(self.REG_LATCHLOC5_8)
        clkphase = self.femb.read_reg(self.REG_CLKPHASE)
        if latchloc1 is None:
            return "Register Read Error"
        if latchloc5 is None:
            return "Register Read Error"
        if clkphase is None:
            return "Register Read Error"
        return "Latch Loc: {:#010x} {:#010x} Clock Phase: {:#010x}".format(
            latchloc1, latchloc5, clkphase)

    def getSyncStatus(self):
        return [None], [True], None
Exemple #12
0
class FEMB_CONFIG(FEMB_CONFIG_BASE):
    def __init__(self):
        super().__init__()
        #declare board specific registers
        self.FEMB_VER = "35t"
        self.REG_RESET = 0
        self.REG_ASIC_RESET = 1
        self.REG_ASIC_SPIPROG = 2
        self.REG_SEL_ASIC = 7
        self.REG_SEL_CH = 7
        self.REG_FESPI_BASE = 592
        self.REG_ADCSPI_BASE = 512
        self.REG_FESPI_RDBACK_BASE = 632
        self.REG_ADCSPI_RDBACK_BASE = 552
        self.REG_HS = 17
        self.REG_LATCHLOC = 4
        self.REG_CLKPHASE = 6
        self.ADC_TESTPATTERN = [
            0x12, 0x345, 0x678, 0xf1f, 0xad, 0xc01, 0x234, 0x567, 0x89d, 0xeca,
            0xff0, 0x123, 0x456, 0x789, 0xabc, 0xdef
        ]
        self.NASICS = 8

        #initialize FEMB UDP object
        self.femb = FEMB_UDP()

        #initialiuze ASIC ch objects, specify SPI register number (firmware specific!!!)
        self.feasic_ch_list = []
        for ch in range(0, 128, 1):
            chVal = int(ch)
            if (chVal < 0) or (chVal > 127):
                continue

            #seriously messy mapping between ch # and register bits
            regGrp = int((chVal % 64) / 16)
            regGrpLine = 7 - int((chVal % 16) / 2)
            regGrpBase = [27, 18, 9, 0]

            regNum = self.REG_FESPI_BASE + regGrpBase[regGrp] + regGrpLine
            regPos = (1 - chVal % 2) * 8 + int(chVal / 64) * 16

            feasic_ch = FEASIC_CH_CONFIG(ch, regNum, regPos)
            self.feasic_ch_list.append(feasic_ch)

    def resetBoard(self):
        #Reset system
        self.femb.write_reg(self.REG_RESET, 1)
        time.sleep(5.)

        #Reset registers
        self.femb.write_reg(self.REG_RESET, 2)
        time.sleep(1.)

        #Time stamp reset
        #femb.write_reg( 0, 4)
        #time.sleep(0.5)

        #Reset ADC ASICs
        self.femb.write_reg(self.REG_ASIC_RESET, 1)
        time.sleep(0.5)

        #Reset FE ASICs
        self.femb.write_reg(self.REG_ASIC_RESET, 2)
        time.sleep(0.5)

    def initBoard(self):
        nRetries = 5
        for iRetry in range(nRetries):
            #set up default registers

            #Reset ADC ASICs
            self.femb.write_reg(self.REG_ASIC_RESET, 1)
            time.sleep(0.5)

            #Reset FE ASICs
            self.femb.write_reg(self.REG_ASIC_RESET, 2)
            time.sleep(0.5)

            #Set ADC test pattern register
            self.femb.write_reg(3, 0x01230000)  #31 - enable ADC test pattern,

            #Set ADC latch_loc
            self.femb.write_reg(self.REG_LATCHLOC, 0x77777677)
            #Set ADC clock phase
            self.femb.write_reg(self.REG_CLKPHASE, 0x1e)

            #internal test pulser control
            self.femb.write_reg(5, 0x02000001)
            self.femb.write_reg(13, 0x0)  #enable

            #Set test and readout mode register
            self.femb.write_reg(
                7, 0x0000)  #11-8 = channel select, 3-0 = ASIC select

            #Set number events per header
            self.femb.write_reg(8, 0x0)

            #FE ASIC SPI registers
            print("Config FE ASIC SPI")
            for regNum in range(self.REG_FESPI_BASE, self.REG_FESPI_BASE + 34,
                                1):
                self.femb.write_reg(regNum, 0xC4C4C4C4)
            self.femb.write_reg(self.REG_FESPI_BASE + 8, 0xC400C400)
            self.femb.write_reg(self.REG_FESPI_BASE + 16, 0x00C400C4)
            self.femb.write_reg(self.REG_FESPI_BASE + 25, 0xC400C400)
            self.femb.write_reg(self.REG_FESPI_BASE + 33, 0x00C400C4)

            #ADC ASIC SPI registers
            print("Config ADC ASIC SPI")
            self.femb.write_reg(self.REG_ADCSPI_BASE + 0, 0xc0c0c0c)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 1, 0xc0c0c0c)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 2, 0xc0c0c0c)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 3, 0xc0c0c0c)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 4, 0xc0c0c0c)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 5, 0xc0c0c0c)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 6, 0xc0c0c0c)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 7, 0xc0c0c0c)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 8, 0x18321832)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 9, 0x18181818)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 10, 0x18181818)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 11, 0x18181818)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 12, 0x18181818)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 13, 0x18181818)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 14, 0x18181818)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 15, 0x18181818)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 16, 0x64186418)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 17, 0x30303030)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 18, 0x30303030)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 19, 0x30303030)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 20, 0x30303030)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 21, 0x30303030)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 22, 0x30303030)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 23, 0x30303030)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 24, 0x30303030)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 25, 0x60c860c8)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 26, 0x60606060)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 27, 0x60606060)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 28, 0x60606060)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 29, 0x60606060)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 30, 0x60606060)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 31, 0x60606060)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 32, 0x60606060)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 33, 0x90609060)
            self.femb.write_reg(self.REG_ADCSPI_BASE + 34, 0x10001)

            #ADC ASIC sync
            self.femb.write_reg(17,
                                0x1)  # controls HS link, 0 for on, 1 for off
            self.femb.write_reg(17,
                                0x0)  # controls HS link, 0 for on, 1 for off

            #Write FE ASIC SPI
            print("Program FE ASIC SPI")
            self.femb.write_reg(self.REG_ASIC_SPIPROG, 2)
            time.sleep(0.1)
            self.femb.write_reg(self.REG_ASIC_SPIPROG, 2)
            time.sleep(0.1)

            #Write ADC ASIC SPI
            print("Program ADC ASIC SPI")
            self.femb.write_reg(self.REG_ASIC_RESET, 1)
            time.sleep(0.1)
            self.femb.write_reg(self.REG_ASIC_SPIPROG, 1)
            time.sleep(0.1)
            self.femb.write_reg(self.REG_ASIC_SPIPROG, 1)
            time.sleep(0.1)

            #35t ONLY, check if sync is ok, try redoing ADC reprogramming if not
            for test in range(0, 5, 1):
                regVal = self.femb.read_reg(6)
                isSync = ((regVal & 0xFFFF0000) >> 16)
                if isSync == 0:
                    print("Synced ADCs")
                    break
                self.femb.write_reg(self.REG_ASIC_RESET, 1)
                time.sleep(0.1)
                self.femb.write_reg(self.REG_ASIC_SPIPROG, 1)
                time.sleep(0.1)
                self.femb.write_reg(self.REG_ASIC_SPIPROG, 1)
                time.sleep(0.1)
            """
            print("Check ADC ASIC SPI")
            for regNum in range(self.REG_ADCSPI_RDBACK_BASE,self.REG_ADCSPI_RDBACK_BASE+34,1):
                    val = self.femb.read_reg( regNum ) 
                    print(hex(val))

            print("Check FE ASIC SPI")
            for regNum in range(self.REG_FESPI_RDBACK_BASE,self.REG_FESPI_RDBACK_BASE+34,1):
                    val = self.femb.read_reg( regNum)            
                    print(hex(val))
            """

            # Check that board streams data
            data = self.femb.get_data(1)
            if data == None:
                print("Board not streaming data, retrying initialization...")
                continue  # try initializing again
            print("FEMB_CONFIG--> Reset FEMB is DONE")
            return
        print(
            "Error: Board not streaming data after trying to initialize {} times. Exiting."
            .format(nRetries))
        sys.exit(1)

    def configFeAsic(self, gain, shape, base):
        gainVal = int(gain)
        if (gainVal < 0) or (gainVal > 3):
            return
        shapeVal = int(shape)
        if (shapeVal < 0) or (shapeVal > 3):
            return
        baseVal = int(base)
        if (baseVal < 0) or (baseVal > 1):
            return

        #get ASIC channel config register SPI values
        chReg = 0x0
        gainArray = [0, 2, 1, 3]
        shapeArray = [2, 0, 3, 1]  #I don't know why
        chReg = (gainArray[gainVal] << 4) + (shapeArray[shapeVal] << 2)

        if (baseVal == 0):
            chReg = chReg + 0x40

        #enable test capacitor here
        chReg = chReg + 0x80  #enabled
        #chReg = chReg + 0x0 #disabled

        #need better organization of SPI, just store in words for now
        word1 = chReg + (chReg << 8) + (chReg << 16) + (chReg << 24)
        word2 = (chReg << 8) + (chReg << 24)
        word3 = chReg + (chReg << 16)

        #turn off HS data before register writes
        self.femb.write_reg_bits(9, 0, 0x1, 0)
        print("HS link turned off")
        time.sleep(1)

        print("Config FE ASIC SPI")
        for regNum in range(self.REG_FESPI_BASE, self.REG_FESPI_BASE + 34, 1):
            self.femb.write_reg(regNum, word1)
        self.femb.write_reg(self.REG_FESPI_BASE + 8, word2)
        self.femb.write_reg(self.REG_FESPI_BASE + 16, word3)
        self.femb.write_reg(self.REG_FESPI_BASE + 25, word2)
        self.femb.write_reg(self.REG_FESPI_BASE + 33, word3)

        #Write FE ASIC SPI
        print("Program FE ASIC SPI")
        self.femb.write_reg(self.REG_ASIC_SPIPROG, 2)
        time.sleep(0.1)
        self.femb.write_reg(self.REG_ASIC_SPIPROG, 2)
        time.sleep(0.1)

        #print "Check FE ASIC SPI"
        #for regNum in range(self.REG_FESPI_RDBACK_BASE,self.REG_FESPI_RDBACK_BASE+34,1):
        #        val = self.femb.read_reg( regNum)
        #        print hex(val)

        #turn HS link back on
        print("HS link turned back on")
        time.sleep(1)
        self.femb.write_reg_bits(9, 0, 0x1, 1)

    def selectChannel(self, asic, chan, hsmode=None):
        asicVal = int(asic)
        if (asicVal < 0) or (asicVal > 7):
            print("femb_config_femb : selectChan - invalid ASIC number")
            return
        chVal = int(chan)
        if (chVal < 0) or (chVal > 15):
            print("femb_config_femb : selectChan - invalid channel number")
            return

        #print "Selecting ASIC " + str(asicVal) + ", channel " + str(chVal)

        regVal = (chVal << 8) + asicVal
        self.femb.write_reg(self.REG_SEL_CH, regVal)

    def setInternalPulser(self, pulserEnable, pulseHeight):
        pulserEnable = int(pulserEnable)
        if (pulserEnable < 0) or (pulserEnable > 1):
            return
        pulserEnableVal = int(pulserEnable)
        if (pulseHeight < 0) or (pulseHeight > 32):
            return
        pulseHeightVal = int(pulseHeight)
        self.femb.write_reg_bits(5, 0, 0x1F, pulseHeightVal)
        #self.femb.write_reg_bits( 16, 8,0x1,0)
        self.femb.write_reg_bits(13, 1, 0x1, pulserEnableVal)

    def syncADC(self):
        #turn on ADC test mode
        print("Start sync ADC")
        reg3 = self.femb.read_reg(3)
        newReg3 = (reg3 | 0x80000000)
        self.femb.write_reg(3, newReg3)  #31 - enable ADC test pattern
        alreadySynced = True
        for a in range(0, 8, 1):
            print("Test ADC " + str(a))
            unsync = self.testUnsync(a)
            if unsync != 0:
                print("ADC not synced, try to fix")
                alreadySynced = False
                self.fixUnsync(a)
        LATCH = self.femb.read_reg(self.REG_LATCHLOC)
        PHASE = self.femb.read_reg(self.REG_CLKPHASE)
        print("Latch latency " + str(hex(LATCH)) + "\tPhase " +
              str(hex(PHASE)))
        print("End sync ADC")
        return not alreadySynced, LATCH, None, PHASE

    def testUnsync(self, adc):
        adcNum = int(adc)
        if (adcNum < 0) or (adcNum > 7):
            print("femb_config_femb : testLink - invalid asic number")
            return

        #loop through channels, check test pattern against data
        badSync = 0
        for ch in range(0, 16, 1):
            self.selectChannel(adcNum, ch)
            time.sleep(0.1)
            for test in range(0, 10, 1):
                data = self.femb.get_data(1)
                if data == None:
                    continue
                for samp in data:
                    if samp == None:
                        continue
                    chNum = ((samp >> 12) & 0xF)
                    sampVal = (samp & 0xFFF)
                    if sampVal != self.ADC_TESTPATTERN[ch]:
                        badSync = 1
                    if badSync == 1:
                        break
                if badSync == 1:
                    break
            if badSync == 1:
                break
        return badSync

    def fixUnsync(self, adc):
        adcNum = int(adc)
        if (adcNum < 0) or (adcNum > 7):
            print("femb_config_femb : testLink - invalid asic number")
            return

        initLATCH = self.femb.read_reg(self.REG_LATCHLOC)
        initPHASE = self.femb.read_reg(self.REG_CLKPHASE)

        #loop through sync parameters
        for phase in range(0, 2, 1):
            clkMask = (0x1 << adcNum)
            testPhase = ((initPHASE & ~(clkMask)) | (phase << adcNum))
            self.femb.write_reg(self.REG_CLKPHASE, testPhase)
            for shift in range(0, 16, 1):
                shiftMask = (0xF << 4 * adcNum)
                testShift = ((initLATCH & ~(shiftMask)) |
                             (shift << 4 * adcNum))
                self.femb.write_reg(self.REG_LATCHLOC, testShift)
                #reset ADC ASIC
                self.femb.write_reg(self.REG_ASIC_RESET, 1)
                time.sleep(0.1)
                self.femb.write_reg(self.REG_ASIC_SPIPROG, 1)
                time.sleep(0.1)
                self.femb.write_reg(self.REG_ASIC_SPIPROG, 1)
                time.sleep(0.1)
                #test link
                unsync = self.testUnsync(adcNum)
                if unsync == 0:
                    print("ADC synchronized")
                    return
        #if program reaches here, sync has failed
        print("ADC SYNC process failed for ADC # " + str(adc))
Exemple #13
0
class FEMB_CONFIG(FEMB_CONFIG_BASE):
    def __init__(self):
        super().__init__()
        #declare board specific registers
        self.FEMB_VER = "SBND(FE-ASIC with internal DAC)"
        self.REG_RESET = 0
        self.REG_ASIC_RESET = 1
        self.REG_ASIC_SPIPROG = 2
        self.REG_SEL_ASIC = 7
        self.REG_SEL_CH = 7
        self.REG_FESPI_BASE = 0x250
        self.REG_ADCSPI_BASE = 0x200
        self.REG_FESPI_RDBACK_BASE = 0x278
        self.REG_ADCSPI_RDBACK_BASE = 0x228
        self.REG_HS = 17
        self.REG_LATCHLOC1_4 = 4
        self.REG_LATCHLOC1_4_data = 0x07060707
        self.REG_LATCHLOC5_8 = 14
        self.REG_LATCHLOC5_8_data = 0x06060606
        self.REG_CLKPHASE = 6
        self.REG_CLKPHASE_data = 0xe1
        self.REG_EN_CALI = 16
        self.ADC_TESTPATTERN = [
            0x12, 0x345, 0x678, 0xf1f, 0xad, 0xc01, 0x234, 0x567, 0x89d, 0xeca,
            0xff0, 0x123, 0x456, 0x789, 0xabc, 0xdef
        ]
        self.NASICS = 8

        #initialize FEMB UDP object
        self.femb = FEMB_UDP()
        self.adc_reg = ADC_ASIC_REG_MAPPING()
        self.fe_reg = FE_ASIC_REG_MAPPING()

    def resetBoard(self):
        #Reset system
        self.femb.write_reg(self.REG_RESET, 1)

        #Reset registers
        self.femb.write_reg(self.REG_RESET, 2)

        #Time stamp reset
        #femb.write_reg ( 0, 4)

        #Reset ADC ASICs
        self.femb.write_reg(self.REG_ASIC_RESET, 1)

    def initBoard(self):
        nRetries = 5
        for iRetry in range(nRetries):
            print("FEMB_CONFIG--> Reset FEMB")
            #set up default registers

            #Reset ADC ASICs
            self.femb.write_reg(self.REG_ASIC_RESET, 1)

            #Set ADC test pattern register
            self.femb.write_reg(3, 0x01170000)  #31 - enable ADC test pattern,

            #Set ADC latch_loc
            self.femb.write_reg(self.REG_LATCHLOC1_4,
                                self.REG_LATCHLOC1_4_data)
            self.femb.write_reg(self.REG_LATCHLOC5_8,
                                self.REG_LATCHLOC5_8_data)
            #Set ADC clock phase
            self.femb.write_reg(self.REG_CLKPHASE, self.REG_CLKPHASE_data)

            #internal test pulser control
            freq = 500
            dly = 80
            ampl = 0 % 32
            int_dac = 0  # or 0xA1
            dac_meas = int_dac  # or 60
            reg_5_value = ((freq << 16) & 0xFFFF0000) + (
                (dly << 8) & 0xFF00) + ((dac_meas | ampl) & 0xFF)
            self.femb.write_reg(5, reg_5_value)
            self.femb.write_reg(16, 0x0)

            self.femb.write_reg(13, 0x0)  #enable

            #Set test and readout mode register
            self.femb.write_reg(
                7, 0x0000)  #11-8 = channel select, 3-0 = ASIC select
            self.femb.write_reg(17,
                                1)  #11-8 = channel select, 3-0 = ASIC select

            #for iReg in range(len(self.fe_reg.REGS)):
            #  self.fe_reg.REGS[iReg] = 0xFFFFFFFF
            #set default value to FEMB ADCs and FEs
            #self.configAdcAsic(pdsr=1,pcsr=1,clockFromFIFO=True,freqInternal=1,f2=1)
            self.configAdcAsic_regs(self.adc_reg.REGS)
            self.configFeAsic_regs(self.fe_reg.REGS)

            #Set number events per header -- no use
            #self.femb.write_reg ( 8, 0x0)

            # Check that board streams data
            data = self.femb.get_data(1)
            if data == None:
                print("Board not streaming data, retrying initialization...")
                continue  # try initializing again
            print("FEMB_CONFIG--> Reset FEMB is DONE")
            return
        print(
            "Error: Board not streaming data after trying to initialize {} times. Exiting."
            .format(nRetries))
        sys.exit(1)

    def configAdcAsic_regs(self, Adcasic_regs):
        #ADC ASIC SPI registers
        print("FEMB_CONFIG--> Config ADC ASIC SPI")
        for k in range(10):
            i = 0
            for regNum in range(self.REG_ADCSPI_BASE,
                                self.REG_ADCSPI_BASE + len(Adcasic_regs), 1):
                #print("{:032b}".format(Adcasic_regs[i]))
                #print("{:08x}".format(Adcasic_regs[i]))
                self.femb.write_reg(regNum, Adcasic_regs[i])
                time.sleep(0.05)
                i = i + 1

            #print("  ADC ASIC write : ",Adcasic_regs)
            #ADC ASIC sync
            #self.femb.write_reg ( 17, 0x1) # controls HS link, 0 for on, 1 for off
            #self.femb.write_reg ( 17, 0x0) # controls HS link, 0 for on, 1 for off

            #Write ADC ASIC SPI
            print("FEMB_CONFIG--> Program ADC ASIC SPI")
            self.femb.write_reg(self.REG_ASIC_SPIPROG, 1)
            time.sleep(0.1)
            self.femb.write_reg(self.REG_ASIC_SPIPROG, 1)
            time.sleep(0.1)

            self.femb.write_reg(18, 0x0)
            time.sleep(0.1)

            print("FEMB_CONFIG--> Check ADC ASIC SPI")
            adcasic_rb_regs = []
            for regNum in range(
                    self.REG_ADCSPI_RDBACK_BASE,
                    self.REG_ADCSPI_RDBACK_BASE + len(Adcasic_regs), 1):
                val = self.femb.read_reg(regNum)
                adcasic_rb_regs.append(val)

            #print("  ADC ASIC read back: ",adcasic_rb_regs)
            if (adcasic_rb_regs != Adcasic_regs):
                if (k == 1):
                    sys.exit("femb_config : Wrong readback. ADC SPI failed")
                    return
                print(
                    "FEMB_CONFIG--> ADC ASIC Readback didn't match, retrying..."
                )
            else:
                print("FEMB_CONFIG--> ADC ASIC SPI is OK")
                break
        #enable streaming
        #self.femb.write_reg ( 9, 0x8)
        #LBNE_ADC_MODE

    def configFeAsic_regs(self, feasic_regs):
        print("FEMB_CONFIG--> Config FE ASIC SPI")
        assert (len(feasic_regs) == 34)

        for k in range(10):
            i = 0
            for regNum in range(self.REG_FESPI_BASE,
                                self.REG_FESPI_BASE + len(feasic_regs), 1):
                self.femb.write_reg(regNum, feasic_regs[i])
                i = i + 1
            #Write FE ASIC SPI
            print("FEMB_CONFIG--> Program FE ASIC SPI")
            self.femb.write_reg(self.REG_ASIC_SPIPROG, 2)
            self.femb.write_reg(self.REG_ASIC_SPIPROG, 2)

            print("FEMB_CONFIG--> Check FE ASIC SPI")
            feasic_rb_regs = []
            for regNum in range(self.REG_FESPI_RDBACK_BASE,
                                self.REG_FESPI_RDBACK_BASE + len(feasic_regs),
                                1):
                val = self.femb.read_reg(regNum)
                feasic_rb_regs.append(val)

            if (feasic_rb_regs != feasic_regs):
                if (k == 9):
                    sys.exit(
                        "femb_config_femb : Wrong readback. FE SPI failed")
                    return
                print(
                    "FEMB_CONFIG--> FE ASIC Readback didn't match, retrying..."
                )
                if len(feasic_rb_regs) == len(feasic_regs):
                    print("{:15} {:15}".format("feasic_rb_regs",
                                               "feasic_regs"))
                    for iReg in range(len(feasic_rb_regs)):
                        print("{:#010x}      {:#010x}".format(
                            feasic_rb_regs[iReg], feasic_regs[iReg]))
                else:
                    print("lens don't match: {} != {}".format(
                        len(feasic_rb_regs), len(feasic_regs)))
            else:
                print("FEMB_CONFIG--> FE ASIC SPI is OK")
                #print("{:15} {:15}".format("feasic_rb_regs","feasic_regs"))
                #for iReg in range(len(feasic_rb_regs)):
                #    print("{:#010x}      {:#010x}".format(feasic_rb_regs[iReg],feasic_regs[iReg]))
                break

    def configFeAsic(self,
                     gain,
                     shape,
                     base,
                     slk=None,
                     slkh=None,
                     monitorBandgap=None,
                     monitorTemp=None):
        """
        Configure FEs with given gain/shape/base values.
        Also, configure leakage current slk = 0 for 500 pA, 1 for 100 pA
            and slkh = 0 for 1x leakage current, 1 for 10x leakage current
        if monitorBandgap is True: monitor bandgap instead of signal
        if monitorTemp is True: monitor temperature instead of signal
        """
        gain = int("{:02b}".format(gain)[::-1],
                   2)  # need to reverse bits, use string/list tricks
        shape = int("{:02b}".format(shape)[::-1],
                    2)  # need to reverse bits, use string/list tricks
        if slk is None:
            slk = 0
        if slkh is None:
            slkh = 0
        if monitorBandgap and monitorTemp:
            raise Exception(
                "You can't monitor bandgap and temperature at the same time. Set either monitorBandgap or monitorTemp False"
            )
        stb = 0
        if monitorBandgap:
            stb = 0b11
        if monitorTemp:
            stb = 0b10
        self.fe_reg.set_fe_sbnd_board(snc=base,
                                      sg=gain,
                                      st=shape,
                                      slk0=slk,
                                      stb=stb)
        self.configFeAsic_regs(self.fe_reg.REGS)

    def configAdcAsic(self,
                      enableOffsetCurrent=None,
                      offsetCurrent=None,
                      testInput=None,
                      freqInternal=None,
                      sleep=None,
                      pdsr=None,
                      pcsr=None,
                      clockMonostable=None,
                      clockExternal=None,
                      clockFromFIFO=None,
                      sLSB=None,
                      f0=None,
                      f1=None,
                      f2=None,
                      f3=None,
                      f4=None,
                      f5=None):
        """
        Configure ADCs
          enableOffsetCurrent: 0 disable offset current, 1 enable offset current
          offsetCurrent: 0-15, amount of current to draw from sample and hold
          testInput: 0 digitize normal input, 1 digitize test input
          freqInternal: internal clock frequency: 0 1MHz, 1 2MHz
          sleep: 0 disable sleep mode, 1 enable sleep mode
          pdsr: if pcsr=0: 0 PD is low, 1 PD is high
          pcsr: 0 power down controlled by pdsr, 1 power down controlled externally
          Only one of these can be enabled:
            clockMonostable: True ADC uses monostable clock
            clockExternal: True ADC uses external clock
            clockFromFIFO: True ADC uses digital generator FIFO clock
          sLSB: LSB current steering mode. 0 for full, 1 for partial (ADC7 P1)
          f0, f1, f2, f3, f4, f5: version specific
        """
        FEMB_CONFIG_BASE.configAdcAsic(self,
                                       clockMonostable=clockMonostable,
                                       clockExternal=clockExternal,
                                       clockFromFIFO=clockFromFIFO)
        if enableOffsetCurrent is None:
            enableOffsetCurrent = 0
        if offsetCurrent is None:
            offsetCurrent = 0
        else:
            offsetCurrent = int(
                "{:04b}".format(offsetCurrent)[::-1],
                2)  # need to reverse bits, use string/list tricks
        if testInput is None:
            testInput = 0
        if freqInternal is None:
            freqInternal = 0
        if sleep is None:
            sleep = 0
        if pdsr is None:
            pdsr = 0
        if pcsr is None:
            pcsr = 0
        clk0 = 0
        clk1 = 0
        if clockExternal:
            clk0 = 1
            clk1 = 0
        elif clockFromFIFO:
            clk0 = 0
            clk1 = 1
        self.adc_reg.set_sbnd_board(en_gr=enableOffsetCurrent,
                                    d=offsetCurrent,
                                    tstin=testInput,
                                    frqc=freqInternal,
                                    slp=sleep,
                                    pdsr=pdsr,
                                    pcsr=pcsr,
                                    clk0=clk0,
                                    clk1=clk1,
                                    f1=f1,
                                    f2=f2)
        self.configAdcAsic_regs(self.adc_reg.REGS)

    def selectChannel(self, asic, chan, hsmode=1):
        asicVal = int(asic)
        if (asicVal < 0) or (asicVal >= self.NASICS):
            print(
                "femb_config_femb : selectChan - invalid ASIC number, only 0 to {} allowed"
                .format(self.NASICS - 1))
            return
        chVal = int(chan)
        if (chVal < 0) or (chVal > 15):
            print(
                "femb_config_femb : selectChan - invalid channel number, only 0 to 15 allowed"
            )
            return
        hsmodeVal = int(hsmode)
        if (hsmodeVal != 0) and (hsmodeVal != 1):
            print(
                "FEMB_CONFIG--> femb_config_femb : selectChan - invalid HS mode"
            )
            return

        print("FEMB_CONFIG--> Selecting ASIC " + str(asicVal) + ", channel " +
              str(chVal))

        self.femb.write_reg(self.REG_HS, hsmodeVal)
        self.femb.write_reg(self.REG_HS, hsmodeVal)
        regVal = (chVal << 8) + asicVal
        self.femb.write_reg(self.REG_SEL_CH, regVal)
        self.femb.write_reg(self.REG_SEL_CH, regVal)

    def setInternalPulser(self, pulserEnable, pulseHeight):
        if pulserEnable:
            print("Enabling internal FPGA DAC")

            # turn on test capacitor on all FE ASIC channels
            fe_reg = copy.deepcopy(self.fe_reg.REGS)

            self.fe_reg.set_fe_sbnd_board(sts=1)
            for i in range(len(fe_reg)):
                self.fe_reg.REGS[i] = fe_reg[i] | self.fe_reg.REGS[i]
                print(hex(self.fe_reg.REGS[i]))

            self.configFeAsic_regs(self.fe_reg.REGS)

            # internal FPGA DAC settings
            freq = 20  # number of samples between pulses
            dly = 80  # dly*5ns sets inteval between where FPGA starts pulse and ADC samples
            ampl = pulseHeight % 32  # mV injected
            int_dac = 0  # or 0xA1
            dac_meas = int_dac  # or 60
            reg_5_value = ((freq << 16) & 0xFFFF0000) + (
                (dly << 8) & 0xFF00) + ((dac_meas | ampl) & 0xFF)
            self.femb.write_reg(5, reg_5_value)

            # set to pulser mode (0x01) and enable FPGA DAC (0x01xx)
            self.femb.write_reg(16, 0x0101)
            print(self.femb.read_reg(16))
        else:
            print("Disabling pulser (still testing may not work)")

            # disable test capacitor
            fe_reg = copy.deepcopy(self.fe_reg.REGS)

            self.fe_reg.set_fe_sbnd_board(sts=0)
            for i in range(len(fe_reg)):
                self.fe_reg.REGS[i] = fe_reg[i] | self.fe_reg.REGS[i]
                print(hex(self.fe_reg.REGS[i]))

            self.configFeAsic_regs(self.fe_reg.REGS)

            # disable pulser mode
            self.femb.write_reg(16, 0x0)
            print(self.femb.read_reg(16))

    def syncADC(self):
        #turn on ADC test mode
        print("FEMB_CONFIG--> Start sync ADC")
        reg3 = self.femb.read_reg(3)
        newReg3 = (reg3 | 0x80000000)

        self.femb.write_reg(3, newReg3)  #31 - enable ADC test pattern
        time.sleep(0.1)
        alreadySynced = True
        for a in range(0, self.NASICS, 1):
            print("FEMB_CONFIG--> Test ADC " + str(a))
            unsync = self.testUnsync(a)
            if unsync != 0:
                alreadySynced = False
                print("FEMB_CONFIG--> ADC not synced, try to fix")
                self.fixUnsync(a)
        self.REG_LATCHLOC1_4_data = self.femb.read_reg(self.REG_LATCHLOC1_4)
        self.REG_LATCHLOC5_8_data = self.femb.read_reg(self.REG_LATCHLOC5_8)
        self.REG_CLKPHASE_data = self.femb.read_reg(self.REG_CLKPHASE)
        print("FEMB_CONFIG--> Latch latency " + str(hex(self.REG_LATCHLOC1_4_data)) \
                        + str(hex(self.REG_LATCHLOC5_8_data )) + \
                        "\tPhase " + str(hex(self.REG_CLKPHASE_data)))
        self.femb.write_reg(3, (reg3 & 0x7fffffff))
        self.femb.write_reg(3, (reg3 & 0x7fffffff))
        print("FEMB_CONFIG--> End sync ADC")
        return not alreadySynced, self.REG_LATCHLOC1_4_data, self.REG_LATCHLOC5_8_data, self.REG_CLKPHASE_data

    def testUnsync(self, adc):
        print("Starting testUnsync adc: ", adc)
        adcNum = int(adc)
        if (adcNum < 0) or (adcNum > 7):
            print(
                "FEMB_CONFIG--> femb_config_femb : testLink - invalid asic number"
            )
            return

        #loop through channels, check test pattern against data
        badSync = 0
        for ch in range(0, 16, 1):
            self.selectChannel(adcNum, ch, 1)
            time.sleep(0.05)
            for test in range(0, 10, 1):
                data = self.femb.get_data(1)
                #print("test: ",test," data: ",data)
                if data == None:
                    continue
                for samp in data[0:(16 * 1024 + 1023)]:
                    if samp == None:
                        continue
                    chNum = ((samp >> 12) & 0xF)
                    sampVal = (samp & 0xFFF)
                    if sampVal != self.ADC_TESTPATTERN[ch]:
                        badSync = 1
                    if badSync == 1:
                        break
                if badSync == 1:
                    break
            if badSync == 1:
                break
        return badSync

    def fixUnsync(self, adc):
        adcNum = int(adc)
        if (adcNum < 0) or (adcNum > 7):
            print(
                "FEMB_CONFIG--> femb_config_femb : testLink - invalid asic number"
            )
            return

        initLATCH1_4 = self.femb.read_reg(self.REG_LATCHLOC1_4)
        initLATCH5_8 = self.femb.read_reg(self.REG_LATCHLOC5_8)
        initPHASE = self.femb.read_reg(self.REG_CLKPHASE)

        #loop through sync parameters
        for phase in range(0, 2, 1):
            clkMask = (0x1 << adcNum)
            testPhase = ((initPHASE & ~(clkMask)) | (phase << adcNum))
            self.femb.write_reg(self.REG_CLKPHASE, testPhase)
            time.sleep(0.01)
            for shift in range(0, 16, 1):
                shiftMask = (0x3F << 8 * adcNum)
                if (adcNum < 4):
                    testShift = ((initLATCH1_4 & ~(shiftMask)) |
                                 (shift << 8 * adcNum))
                    self.femb.write_reg(self.REG_LATCHLOC1_4, testShift)
                    time.sleep(0.01)
                else:
                    testShift = ((initLATCH5_8 & ~(shiftMask)) |
                                 (shift << 8 * adcNum))
                    self.femb.write_reg(self.REG_LATCHLOC5_8, testShift)
                    time.sleep(0.01)
                print("trying phase: ", phase, " shift: ", shift,
                      " testingUnsync...")
                #reset ADC ASIC
                self.femb.write_reg(self.REG_ASIC_RESET, 1)
                time.sleep(0.01)
                self.femb.write_reg(self.REG_ASIC_SPIPROG, 1)
                time.sleep(0.01)
                self.femb.write_reg(self.REG_ASIC_SPIPROG, 1)
                time.sleep(0.01)
                #test link
                unsync = self.testUnsync(adcNum)
                if unsync == 0:
                    print("FEMB_CONFIG--> ADC synchronized")
                    return
        #if program reaches here, sync has failed
        print("FEMB_CONFIG--> ADC SYNC process failed for ADC # " + str(adc))

    def get_rawdata_chipXchnX(self, chip=0, chn=0):
        i = 0
        while (1):
            i = i + 1
            self.selectChannel(chip, chn, 1)
            data = self.femb.get_rawdata()
            #make sure the data belong to chipXchnX
            data0 = struct.unpack_from(">1H", data[0:2])
            if (len(data) > 2):
                if ((data0[0] >> 12) == chn):
                    if (i > 1):
                        print(
                            "FEMB_CONFIG--> get_rawdata_chipXchnX--> cycle%d to get right data"
                            % i)
                    break
        return data

    def get_rawdata_packets_chipXchnX(self, chip=0, chn=0, val=10):
        i = 0
        while (1):
            self.selectChannel(chip, chn, 1)
            data = self.femb.get_rawdata_packets(val)
            #make sure the data belong to chnX
            if (len(data) > 2):
                data0 = struct.unpack_from(">1H", data[0:2])
                if ((data0[0] >> 12) == chn):
                    if (i > 1):
                        print(
                            "FEMB_CONFIG--> get_rawdata_chipXchnX--> cycle%d to get right data"
                            % i)
                    break
        return data

    def enablePulseMode(self, srcflag):
        #Configures board in test pulse mode
        #srcflag = 0 means external input is enabled
        #srcflag = 1 means internal FPGA DAC is enabled with default settings
        #srcflag = 99 means turn it off
        #ETW just playing around with the internal DAC settings not sure this works
        srcflag = int(srcflag)
        if (srcflag == 1):
            print("Enabling internal FPGA DAC")

            # turn on test capacitor on all FE ASIC channels
            fe_reg = copy.deepcopy(self.fe_reg.REGS)

            self.fe_reg.set_fe_sbnd_board(sts=1)
            for i in range(len(fe_reg)):
                self.fe_reg.REGS[i] = fe_reg[i] | self.fe_reg.REGS[i]
                print(hex(self.fe_reg.REGS[i]))

            self.configFeAsic_regs(self.fe_reg.REGS)

            # internal FPGA DAC settings
            freq = 20  # number of samples between pulses
            dly = 80  # dly*5ns sets inteval between where FPGA starts pulse and ADC samples
            ampl = 20 % 32  # mV injected
            int_dac = 0  # or 0xA1
            dac_meas = int_dac  # or 60
            reg_5_value = ((freq << 16) & 0xFFFF0000) + (
                (dly << 8) & 0xFF00) + ((dac_meas | ampl) & 0xFF)
            self.femb.write_reg(5, reg_5_value)

            # set to pulser mode (0x01) and enable FPGA DAC (0x01xx)
            self.femb.write_reg(16, 0x0101)
            print(self.femb.read_reg(16))

        elif (srcflag == 0):
            print("Enabling external pulse (still testing may not work)")

            # turn on test capacitor on all FE ASIC channels
            fe_reg = copy.deepcopy(self.fe_reg.REGS)

            self.fe_reg.set_fe_sbnd_board(sts=1)
            for i in range(len(fe_reg)):
                self.fe_reg.REGS[i] = fe_reg[i] | self.fe_reg.REGS[i]
                print(hex(self.fe_reg.REGS[i]))

            self.configFeAsic_regs(self.fe_reg.REGS)

            # set to pulser mode (0x01) and enable external input (0x00xx)
            self.femb.write_reg(16, 0x0001)
            print(self.femb.read_reg(16))

        elif (srcflag == 99):
            print("Disabling pulser (still testing may not work)")

            # disable test capacitor
            fe_reg = copy.deepcopy(self.fe_reg.REGS)

            self.fe_reg.set_fe_sbnd_board(sts=0)
            for i in range(len(fe_reg)):
                self.fe_reg.REGS[i] = fe_reg[i] | self.fe_reg.REGS[i]
                print(hex(self.fe_reg.REGS[i]))

            self.configFeAsic_regs(self.fe_reg.REGS)

            # disable pulser mode
            self.femb.write_reg(16, 0x0)
            print(self.femb.read_reg(16))

        else:
            print("Source flag must be 0 (ext), 1 (dac), or 99 (disable)")