Source code for pyripherals.peripherals.AD7961

from ..core import Endpoint
from ..utils import gen_mask
from .ADCDATA import ADCDATA
import copy
import time

[docs]class AD7961(ADCDATA): """An interface to the AD7961 ADC Passed the FPGA object Allows for multiple channels read status flags (FIFO full, empty, count and PLL) configures FPGA internal resets configures chip specific and global enables Formats data returned from the wire out """ # TODO: add Attributes to docstring # the AD7961 does not have internal registers -- just OK endpoints def __init__(self, fpga, endpoints=None): if endpoints is None: endpoints = Endpoint.get_chip_endpoints('AD7961') self.fpga = fpga self.endpoints = endpoints self.name = 'AD7961' self.num_bits = 16 # for AD7961 bits=18 for AD7960
[docs] @staticmethod def create_chips(fpga, number_of_chips, endpoints=None): """Instantiate a given number of new chips. We increment the endpoints between each instantiation as well. The number must be greater than 0. If the endpoints argument is left as None, then we will use copies of the endpoints_from_defines dictionary for the endpoints for each instance, and update that original dictionary when we increment the endpoints. This way, the endpoints there are ready for another instantiation if needed. """ if type(number_of_chips) is not int or number_of_chips <= 0: print('number_of_chips must be an integer greater than 0') return False if endpoints is None: endpoints = Endpoint.endpoints_from_defines.get('AD7961') chips = [] for i in range(number_of_chips): # Use deepcopy here to keep the endpoints for different instances separate chips.append(AD7961(fpga=fpga, endpoints=copy.deepcopy(endpoints))) Endpoint.advance_endpoints(endpoints) return chips
[docs] def get_status(self): """ Get AD796x status: pll lock, FIFO full, half-full, and empty Returns: dictionary of status """ status = self.get_fifo_status() pll_lock = self.get_pll_status() status['pll_lock'] = pll_lock for k in status: print('{} status of {} = {}'.format(self.name, k, status[k])) return status
[docs] def get_pll_status(self): """Get status of the phase locked loop.""" return self.fpga.read_wire_bit(self.endpoints['PLL_LOCKED'].address, self.endpoints['PLL_LOCKED'].bit_index_low)
[docs] def get_timing_pll_status(self): """Get status of the timing of the phase locked loop.""" return self.fpga.read_wire_bit(self.endpoints['TIMING_PLL_LOCKED'].address, self.endpoints['TIMING_PLL_LOCKED'].bit_index_low)
[docs] def get_fifo_status(self): """Get fullness status of the FIFO.""" flags = ['FULL', 'HALFFULL', 'EMPTY'] fifo_status = {} self.fpga.xem.UpdateTriggerOuts() for k in flags: fifo_status[k] = self.fpga.xem.IsTriggered(self.endpoints['FIFO_{}'.format(k)].address, self.endpoints['FIFO_{}'.format(k)].bit_index_low) return fifo_status
# Enable pins: # 0000 - power down # 1001 - enabled with ref buffer on; 28 MHZ # 0100 - test patterns on LVDS # 1101 - enabled with ref buffer on; 9 MHZ # Note that EN[3]=1 enables the VCM output buffer.
[docs] def power_down_adc(self): """Power down single channel of the ADC. Fails if the global enables are 010 for LVDS test patterns """ self.set_enables(0b0, global_enables=False)
[docs] def test_pattern(self): """Enable PRBS test pattern on the LVDS interface.""" self.set_enables(value=0b0100)
def power_up_adc(self, bw='28M'): # TODO: add method docstring # TODO: explain what bw should be in docstring if bw == '28M': self.set_enables(0b1001) elif bw == '9M': self.set_enables(0b1101) else: print(f'incorrect input sampling bandwidth for {self.name}')
[docs] def power_down_all(self): """Power down all channels of the ADC.""" self.set_enables(0b0000) # TODO: return anything here?
[docs] def reset_pll(self): """Reset the phase locked loop.""" return self.fpga.xem.ActivateTriggerIn(self.endpoints['PLL_RESET'].address, self.endpoints['PLL_RESET'].bit_index_low)
[docs] def reset_trig(self): """Reset the FPGA controller for the ADC. Common to synchronize timing (resets ad7961_timing module AND the ads8686 timing) """ return self.fpga.xem.ActivateTriggerIn(self.endpoints['RESET'].address, self.endpoints['RESET'].bit_index_low)
[docs] def reset_wire(self, value): """Set the value of the wire to reset the FPGA controller for the ADC. Uses the Opal Kelly WireIn (the trigger can't hold the reset). Parameters ----------- value : int value = 1 is reset value = 0 releases reset """ if value == 1: self.fpga.set_wire_bit(self.endpoints['WIRE_RESET'].address, self.endpoints['WIRE_RESET'].bit_index_low) if value == 0: self.fpga.clear_wire_bit(self.endpoints['WIRE_RESET'].address, self.endpoints['WIRE_RESET'].bit_index_low)
[docs] def power_down_fpga(self): """Power down the FPGA controller through WireIn.""" # TODO: add functionality (wirein bit) to the FPGA # this would reduce FPGA power consumption self.reset_wire(0)
[docs] def set_enables(self, value=0b0000, global_enables=True): """Set the EN0 specific to this channel (LSB in values). Optionally also modify the global enables (all channels). """ mask = gen_mask(self.endpoints['ENABLE'].bit_index_low) if global_enables: # global enables (connect to all AD7961); create a list of bit position gl_mask = [x+self.endpoints['GLOBAL_ENABLE'].bit_index_low for x in range(self.endpoints['GLOBAL_ENABLE_LEN'].bit_index_low)] # or global mask with the signal channel EN0 mask mask = gen_mask(gl_mask) | mask # setup the values value_chan = (value & 0b0001) << ( self.endpoints['ENABLE'].bit_index_low) if global_enables: # globabl enables are the 3 MSBs val_global = (value & 0b1110) << ( self.endpoints['GLOBAL_ENABLE'].bit_index_low - 1) # minus 1 since already left shifted by 1 print('Global value = 0x{:0x}'.format(val_global)) value = value_chan | val_global print('Enables setting value = 0x{:0x} with mask = = 0x{:0x}'.format( value, mask)) self.fpga.set_wire(self.endpoints['ENABLE'].address, value, mask=mask)
[docs] def setup(self, reset_pll=False): """ 0) put ADC controller into reset 1) optionally reset the ADC PLL and wait for lock (only need to reset PLL on first channel) 2) power up adc 3) release ADC controller reset 4) reset fifo 5) check and return status (PLL and fifo) """ self.reset_wire(1) if reset_pll: # reset PLL self.reset_pll() pll_cnt = 0 while not self.get_pll_status(): time.sleep(0.01) pll_cnt += 1 if pll_cnt > 20: print('PLL lock timeout') return -1 # enable ADC self.power_up_adc() # now that PLL is locked and ADC is ready # release the reset of the ADC controller # self.reset_trig() self.reset_wire(0) # releases the reset # reset FIFO self.reset_fifo() status = self.get_status() # TODO: the FIFO is guaranteed to overflow return status