Commit 2cb274e4 authored by Matthieu Cattin's avatar Matthieu Cattin

test25: New test, calibration verifiaction.

Checks that the offset and gain errors are within the specified % of the FS.
parent cec89c6c
#! /usr/bin/env python
# coding: utf8
# Copyright CERN, 2011
# Author: Matthieu Cattin <matthieu.cattin@cern.ch>
# Licence: GPL v2 or later.
# Website: http://www.ohwr.org
# Last modifications: 7/6/2012
# Import system modules
import sys
import time
import os
# Add common modules and libraries location to path
sys.path.append('../../../')
sys.path.append('../../../gnurabbit/python/')
sys.path.append('../../../common/')
sys.path.append('../../fmceeprom/python/')
# Import common modules
from ptsexcept import *
from fmc_eeprom import *
import rr
# Import specific modules
from fmc_adc_spec import *
from fmc_adc import *
from numpy import *
from pylab import *
from calibr_box import *
from cp210x_eeprom import *
import find_usb_tty
from PAGE.Agilent33250A import *
from PAGE.SineWaveform import *
from ctypes import *
"""
test25: Calibration verification
Note: Requires test00.py to run first to load the firmware!
Requires test23.py to run before to write the calibration data to the FMC EEPROM
"""
NB_CHANNELS = 4
AWG_SET_SLEEP = 0.3
SSR_SET_SLEEP = 0.005
BOX_SET_SLEEP = 0.01
DAC_SET_SLEEP = 0.01
ACQ_TIMEOUT = 10
MAX_FIRMWARE_RELOAD = 10
PRE_TRIG_SAMPLES = 100
POST_TRIG_SAMPLES = 100000
NB_SHOTS = 1
ACQ_LENGTH = 50000 # in samples
ADC_NBITS = 16 # ADC chip is 14 bits, but shifted to 16 bits in the firmware
DAC_NBITS = 16
DAC_FS = 10 # DAC full scale range is 10V
# Full scale volatge values for input ranges
ADC_FS = {'10V':10.0, '1V':1.0, '100mV':0.1}
RANGES = ['10V', '1V', '100mV']
# Number of repeatition of the measurement, then the results are averaged
REPEAT = 2
ADC_LSB = {'10V':10.0/2**14, '1V':1.0/2**14, '100mV':0.1/2**14}
DAC_LSB = 10.0/2**16
# Maximum reference voltage ripple in LSB
MAX_RIPPLE_LSB = 2
# Maximum offset error in % FS
MAX_OFFSET_ERROR = 0.1
# Maximum gain error in % FS
MAX_GAIN_ERROR = 0.1
# Disconnect inputs of all channels
def disconnect_channels(fmc):
for i in range(1,NB_CHANNELS+1):
fmc.set_ssr(i, 0x00)
time.sleep(SSR_SET_SLEEP)
# Initialise acquisition
def fmc_adc_init(spec, fmc):
print "Initialise FMC board.\n"
# Reset offset DACs
fmc.dc_offset_reset()
# Make sure all switches are OFF
disconnect_channels(fmc)
# Reset offset DACs
fmc.dc_offset_reset()
# Set trigger
fmc.set_soft_trig()
# Set acquisition
fmc.set_pre_trig_samples(PRE_TRIG_SAMPLES)
fmc.set_post_trig_samples(POST_TRIG_SAMPLES)
fmc.set_shots(NB_SHOTS)
# Print configuration
#fmc.print_adc_core_config()
# Converts two's complement hex to signed
def hex2signed(value):
if(value & 0x8000):
return -((~value & 0xFFFF) + 1)
else:
return value
# Converts signed to two's complement hex
def signed2hex(value):
if value < 0:
return (((abs(value) ^ 0xffff) + 1) & 0xffff)
else:
return value
# Converts digital value to volts with half full range offset
def digital2volt(value, full_scale, nb_bit):
return (float(value) * float(full_scale)/2**nb_bit) - full_scale/2.0
# Converts digital value to volts
def digital2volt_without_offset(value, full_scale, nb_bit):
return float(value) * float(full_scale)/2**nb_bit
# Converts volts to digital value with half full range offset
def volt2digital(value, full_scale, nb_bit):
digital = (value + full_scale/2) * 2**nb_bit/full_scale
if(digital > 2**nb_bit - 1):
digital = 2**nb_bit - 1
if(digital < 0):
digital = 0
#print "volt2digital: %2.9f > %2.9f"%(value,digital)
return int(digital)
# Converts volts to digital value
def volt2digital_without_offset(value, full_scale, nb_bit):
if(value > (2**nb_bit)/2 - 1):
value = (2**nb_bit)/2 - 1
if(value < -((2**nb_bit)/2)):
value = -((2**nb_bit)/2)
digital = (value) * 2**nb_bit/full_scale
#print "volt2digital: %2.9f > %2.9f"%(value,digital)
return int(digital)
# Make an acquisition of a channel
def acq_channel(carrier, fmc, ch, adc_fs, adc_nbits=16, pause=0.01):
# Make sure no acquisition is running
fmc.stop_acq()
# Start acquisition
fmc.start_acq()
#time.sleep(pause)
# Trigger
fmc.sw_trig()
# Wait end of acquisition
timeout = 0
time.sleep(0.001)
while('IDLE' != fmc.get_acq_fsm_state()):
time.sleep(.01)
timeout += 1
if(ACQ_TIMEOUT < timeout):
print "Acquisition timeout. Missing trigger?."
print "Acq FSm state: %s"%fmc.get_acq_fsm_state()
return 1
# Retrieve data trough DMA
trig_pos = fmc.get_trig_pos()
# Enable "DMA done" iinterrupt
carrier.set_irq_en_mask(0x1)
# Read ACQ_LENGTH samples after the trigger for all channels
channels_data = carrier.get_data((trig_pos<<3), ACQ_LENGTH*8)
# Disable "DMA done" iinterrupt
carrier.set_irq_en_mask(0x0)
channels_data = [hex2signed(item) for item in channels_data]
channels_data = [digital2volt_without_offset(item,adc_fs,adc_nbits) for item in channels_data]
channel_data = channels_data[ch-1::4]
return mean(channel_data)
# Set channel offset with the DAC
def set_offset_dac(fmc, channel, in_range, offset_volt, dac_fs=10.0, dac_nbits=16, dac_corr_flag=False):
dac_v = offset_volt
dac_d = volt2digital(dac_v,dac_fs,dac_nbits)
dac_r = digital2volt(dac_d,dac_fs,dac_nbits)
#print "DAC requested: %2.9f hex:%.4X actual:%2.9f"%(dac_v, dac_d, dac_r)
if(True == dac_corr_flag):
fmc.set_dc_offset_corrected(channel, in_range, dac_d, print_value=False)
else:
fmc.set_dc_offset(channel,dac_d)
time.sleep(DAC_SET_SLEEP)
# Make a measurement, where the input and offset DAC values are set to a known value
def make_meas(carrier, fmc, box, dac_value, in_value, in_range, adc_fs, repeat):
ch_meas = []
ch_diff = []
for ch in range(1,NB_CHANNELS+1):
# Set offset DAC
set_offset_dac(fmc, ch, in_range, dac_value, dac_corr_flag=True)
# Configure calibration box
if in_value == 0.0:
# Configure analogue input, ground input
fmc.set_input_range(ch, ('CAL_'+in_range))
time.sleep(SSR_SET_SLEEP)
# Connect AWG to current channel
box.select_output_ch(ch)
time.sleep(BOX_SET_SLEEP)
else:
# Configure analogue input range
fmc.set_input_range(ch, in_range)
time.sleep(SSR_SET_SLEEP)
# Connect Vref for selected input range to current channel
# Note that channel in parameter is the one connected to AWG
if ch == 1:
awg_ch = 4
else:
awg_ch = ch-1
box.select_output_ch(awg_ch, in_range)
time.sleep(BOX_SET_SLEEP)
# Perform an acquisition
meas = []
for i in range(repeat):
meas.append(acq_channel(carrier, fmc, ch, adc_fs))
# Calculate mean and delta of acquisitions
ch_meas.append(mean(meas))
ch_diff.append(max(meas)-min(meas))
meas_diff = max(meas)-min(meas)
return ch_meas, ch_diff
def main (default_directory = '.'):
# Constants declaration
TEST_NB = 20
EXPECTED_BITSTREAM_TYPE = 0x1
# Calibration box vendor and product IDs
BOX_USB_VENDOR_ID = 0x10c4 # Cygnal Integrated Products, Inc.
BOX_USB_PRODUCT_ID = 0xea60 # CP210x Composite Device
# Agilent AWG serial access vendor and product IDs
AWG_USB_VENDOR_ID = 0x0403 # Future Technology Devices International, Ltd
AWG_USB_PRODUCT_ID = 0x6001 # FT232 USB-Serial (UART) IC
AWG_BAUD = 57600
CALIBR_FILENAME = "calibration_data.txt"
EEPROM_BIN_FILENAME = "eeprom_content.out"
EEPROM_SIZE = 8192 # in Bytes
start_test_time = time.time()
print "================================================================================"
print "Test%02d start\n" % TEST_NB
# SPEC object declaration
print "Loading hardware access library and opening device.\n"
spec = rr.Gennum()
# Carrier object declaration (SPEC board specific part)
# Used to check that the firmware is loaded.
try:
carrier = CFmcAdc100mSpec(spec, EXPECTED_BITSTREAM_TYPE)
except FmcAdc100mSpecOperationError as e:
raise PtsCritical("Carrier init failed, test stopped: %s" % e)
# Mezzanine object declaration (FmcAdc100m14b4cha board specific part)
try:
fmc = CFmcAdc100m(spec)
except FmcAdc100mOperationError as e:
raise PtsCritical("Mezzanine init failed, test stopped: %s" % e)
try:
# Others objects declaration
usb_tty = find_usb_tty.CttyUSB()
awg_tty = usb_tty.find_usb_tty(AWG_USB_VENDOR_ID, AWG_USB_PRODUCT_ID)
box_tty = usb_tty.find_usb_tty(BOX_USB_VENDOR_ID, BOX_USB_PRODUCT_ID)
gen = Agilent33250A(device=awg_tty[0], bauds=AWG_BAUD)
sine = SineWaveform()
box = CCalibr_box(box_tty[0])
box_eeprom = CCP210x_Eeprom("%X"%BOX_USB_VENDOR_ID, "%X"%BOX_USB_PRODUCT_ID)
# Initialise fmc adc
fmc_adc_init(spec, fmc)
# Connect to AWG
gen.connect()
# Switch AWG output and sync OFF
gen.output = False
gen.sync = False
# Measure FMC and carrier temperature
print "SPEC temperature: %3.3f°C" % carrier.get_temp()
print "FMC temperature : %3.3f°C" % fmc.get_temp()
# Open all switches, reset offset DAC to mid-scale (0V)
for channel in range(1,NB_CHANNELS+1):
fmc.set_input_range(channel, 'OPEN')
fmc.set_input_term(channel, 'OFF')
fmc.dc_offset_reset()
# Get calibration data from calibration box
box_calibr_data = box_eeprom.get_calibr_data()
print "\nCalibration box reference voltages:"
for item in box_calibr_data.iteritems():
print "%5s: %1.5fV" % (item[0], float(item[1]))
#---------------------------------------------------------------------------
# Get ADC and DAC offset and gain correction parameters
#---------------------------------------------------------------------------
print "\nRead calibration data from FMC EEPROM:"
adc_corr_data = {'10V':{'offset':[],'gain':[],'temp':0},
'1V':{'offset':[],'gain':[],'temp':0},
'100mV':{'offset':[],'gain':[],'temp':0}}
dac_corr_data = {'10V':{'offset':[],'gain':[],'temp':0},
'1V':{'offset':[],'gain':[],'temp':0},
'100mV':{'offset':[],'gain':[],'temp':0}}
"""
# Read calibration data from file
all_corr_data = []
in_filename = CALIBR_FILENAME
f_in = open(in_filename,"r+")
for line in f_in:
all_corr_data.append(int(line,16))
print "0x%04X" % all_corr_data[-1]
f_in.close()
"""
# Read entire EEPROM
eeprom_data_read = fmc.sys_i2c_eeprom_read(0, EEPROM_SIZE)
# Write EEPROM data to binary file
f_eeprom = open(EEPROM_BIN_FILENAME, "wb")
for byte in eeprom_data_read:
f_eeprom.write(chr(byte))
f_eeprom.close()
# Get calibration data
print "Get calibration data from EEPROM."
eeprom_data = open(EEPROM_BIN_FILENAME, "rb").read()
int_use_data = ipmi_get_internal_use_data(eeprom_data)
# Re-arrange correction data into 16-bit number (from bytes)
eeprom_corr_data = []
for i in range(0,len(int_use_data),2):
eeprom_corr_data.append((int_use_data[i+1] << 8) + (int_use_data[i]))
print "0x%04X" % eeprom_corr_data[-1]
print "Calibration data length (16-bit): %d" % len(eeprom_corr_data)
print "Correction data from eeprom:"
print "\nGet ADC correction parameters:"
for IN_RANGE in RANGES:
for ch in range(NB_CHANNELS):
adc_corr_data[IN_RANGE]['offset'].append(hex2signed(eeprom_corr_data.pop(0)))
for ch in range(NB_CHANNELS):
adc_corr_data[IN_RANGE]['gain'].append(eeprom_corr_data.pop(0))
adc_corr_data[IN_RANGE]['temp'] = eeprom_corr_data.pop(0)/100.0
for ranges in adc_corr_data.iteritems():
print "%s:" % ranges[0]
for corr in ranges[1].iteritems():
print " - %6s: " % corr[0],
if type(corr[1]) is list:
for val in corr[1]:
print "%6d " % val,
else:
print "%2.3f " % corr[1],
print ""
print ""
print "\nGet DAC correction parameters:"
for IN_RANGE in RANGES:
for ch in range(NB_CHANNELS):
dac_corr_data[IN_RANGE]['offset'].append(hex2signed(eeprom_corr_data.pop(0)))
for ch in range(NB_CHANNELS):
dac_corr_data[IN_RANGE]['gain'].append(eeprom_corr_data.pop(0))
dac_corr_data[IN_RANGE]['temp'] = eeprom_corr_data.pop(0)/100.0
for ranges in dac_corr_data.iteritems():
print "%s:" % ranges[0]
for corr in ranges[1].iteritems():
print " - %6s: " % corr[0],
if type(corr[1]) is list:
for val in corr[1]:
print "%6d " % val,
else:
print "%2.3f " % corr[1],
print ""
print ""
#---------------------------------------------------------------------------
# Write DAC gain and offset correction value to fmc class
#---------------------------------------------------------------------------
print "\nApply DAC correction\n"
fmc.set_dac_corr(dac_corr_data)
#---------------------------------------------------------------------------
# ADC Calibration verification
#---------------------------------------------------------------------------
error = 0
for IN_RANGE in RANGES:
print "\n--------------------------------------------------------------------------------"
print "ADC calibration verification for %s input range\n----------------------------------"%(IN_RANGE)
print "\nApply ADC offset correction\n"
#fmc.print_adc_core_config()
for ch in range(NB_CHANNELS):
fmc.set_adc_gain_offset_corr(ch+1, adc_corr_data[IN_RANGE]['gain'][ch], adc_corr_data[IN_RANGE]['offset'][ch])
#fmc.print_adc_core_config()
#for ch in range(NB_CHANNELS):
# print "CH%d ADC offset correction write:0x%.8X read:0x%.8X"%(ch+1,adc_corr_data[IN_RANGE]['offset'][ch],fmc.get_adc_offset_corr(ch+1))
# print "CH%d ADC gain correction write:0x%.8X read:0x%.8X"%(ch+1,adc_corr_data[IN_RANGE]['gain'][ch],fmc.get_adc_gain_corr(ch+1))
print "SPEC temperature: %3.3f°C" % carrier.get_temp()
print "FMC temperature : %3.3f°C" % fmc.get_temp()
print ""
for ch in range(1,NB_CHANNELS+1):
print "Channel %d ADC gain correction register : 0x%04X" % (ch, fmc.get_adc_gain_corr(ch))
print "Channel %d ADC offset correction register: 0x%04X" % (ch, fmc.get_adc_offset_corr(ch))
# Input and offset DAC volatges for the measurements
vref = float(box_calibr_data[IN_RANGE]) # from box cailbration data in CP2103 EEPROM
v_in = [0.0, vref]
v_dac = 0.0
# Make the measurements
v_meas = []
d_meas = []
for i in range(len(v_in)):
print "\nMeasurement %d: channel input = %2.8fV, offset DAC = %2.8fV"%((i+1), v_in[i], v_dac)
mean, diff = make_meas(carrier, fmc, box, v_dac, v_in[i], IN_RANGE, ADC_FS[IN_RANGE], REPEAT)
v_meas.append(mean)
d_meas.append(diff)
max_ripple = MAX_RIPPLE_LSB * ADC_LSB[IN_RANGE]
print " Check voltage stability (reference voltage ripple):"
print " Maximum allowed ripple = %d LSB = %1.9f[V]" % (MAX_RIPPLE_LSB, max_ripple)
for ch in range(1,NB_CHANNELS+1):
ripple = d_meas[i][ch-1]
print "Channel %d: v_meas=%2.9f[V], ripple=%1.9f[V] => "%(ch, v_meas[i][ch-1], d_meas[i][ch-1]),
if abs(ripple) > max_ripple:
print "ERROR ###"
error += 1
else:
print "OK"
# Make verification
print "\nVerification"
print "Maximum offset error: %2.4f%% FS" % MAX_OFFSET_ERROR
print "Maximum gain error : %2.4f%% FS" % MAX_GAIN_ERROR
for ch in range(1,NB_CHANNELS+1):
print "Channel %d"%(ch)
# Offset error
offset_err = v_meas[0][ch-1]
# Offset error in % FS
offset_err_pfs = (100.0/ADC_FS[IN_RANGE]) * abs(offset_err)
# Calculate maximum offset error
max_offset_err = ADC_FS[IN_RANGE] * (MAX_OFFSET_ERROR/100.0)
print " Offset error:%1.9f[V] (%2.4f%% FS), max allowed:+/-%1.5f[V] => " % (offset_err, offset_err_pfs, max_offset_err),
if abs(offset_err) > max_offset_err:
print "ERROR ###"
error += 1
else:
print "OK"
# Calculate gain error
gain_err = (v_meas[1][ch-1] - offset_err) - (v_in[1])
# Gain error in % FS
gain_err_pfs = (100.0/ADC_FS[IN_RANGE]) * abs(gain_err)
# Calculate maximum gain error
max_gain_err = ADC_FS[IN_RANGE] * (MAX_GAIN_ERROR/100.0)
print " Gain error :%1.9f[V] (%2.4f%% FS), max allowed:+/-%1.5f[V] => " % (gain_err, gain_err_pfs, max_gain_err),
if abs(gain_err) > max_gain_err:
print "ERROR ###"
error += 1
else:
print "OK"
#---------------------------------------------------------------------------
# DAC Calibration verification
#---------------------------------------------------------------------------
for IN_RANGE in RANGES:
print "\n--------------------------------------------------------------------------------"
print "DAC calibration verification for %s input range\n----------------------------------"%(IN_RANGE)
print "\nApply ADC offset correction\n"
#fmc.print_adc_core_config()
for ch in range(NB_CHANNELS):
fmc.set_adc_gain_offset_corr(ch+1, adc_corr_data[IN_RANGE]['gain'][ch], adc_corr_data[IN_RANGE]['offset'][ch])
#fmc.print_adc_core_config()
#for ch in range(NB_CHANNELS):
# print "CH%d ADC offset correction write:0x%.8X read:0x%.8X"%(ch+1,adc_corr_data[IN_RANGE]['offset'][ch],fmc.get_adc_offset_corr(ch+1))
# print "CH%d ADC gain correction write:0x%.8X read:0x%.8X"%(ch+1,adc_corr_data[IN_RANGE]['gain'][ch],fmc.get_adc_gain_corr(ch+1))
print "SPEC temperature: %3.3f°C" % carrier.get_temp()
print "FMC temperature : %3.3f°C" % fmc.get_temp()
print ""
for ch in range(1,NB_CHANNELS+1):
print "Channel %d ADC gain correction register : 0x%04X" % (ch, fmc.get_adc_gain_corr(ch))
print "Channel %d ADC offset correction register: 0x%04X" % (ch, fmc.get_adc_offset_corr(ch))
# Input and offset DAC volatges for the measurements
vref = float(box_calibr_data[IN_RANGE]) # from box cailbration data in CP2103 EEPROM
v_in = 0.0
v_dac = [0.0, vref]
#v_dac = [0.0, vref, vref/10, -vref]
# Make the measurements
v_meas = []
d_meas = []
for i in range(len(v_dac)):
print "\nMeasurement %d: channel input = %2.8fV, offset DAC = %2.8fV"%((i+1), v_in, v_dac[i])
mean, diff = make_meas(carrier, fmc, box, v_dac[i], v_in, IN_RANGE, ADC_FS[IN_RANGE], REPEAT)
v_meas.append(mean)
d_meas.append(diff)
max_ripple = MAX_RIPPLE_LSB * ADC_LSB[IN_RANGE]
print " Check voltage stability (reference voltage ripple):"
print " Maximum allowed ripple = %d LSB = %1.9f[V]" % (MAX_RIPPLE_LSB, max_ripple)
for ch in range(1,NB_CHANNELS+1):
ripple = d_meas[i][ch-1]
print "Channel %d: v_meas=%02.9f[V], ripple=%1.9f[V] => "%(ch, v_meas[i][ch-1], d_meas[i][ch-1]),
if abs(ripple) > max_ripple:
print "ERROR ###"
error += 1
else:
print "OK"
# Make verification
print "\nVerification"
print "Maximum offset error: %2.4f%% FS" % MAX_OFFSET_ERROR
print "Maximum gain error : %2.4f%% FS" % MAX_GAIN_ERROR
for ch in range(1,NB_CHANNELS+1):
print "Channel %d"%(ch)
# Offset error
offset_err = v_meas[0][ch-1]
# Offset error in % FS
offset_err_pfs = (100.0/ADC_FS[IN_RANGE]) * abs(offset_err)
# Calculate maximum offset error
max_offset_err = ADC_FS[IN_RANGE] * (MAX_OFFSET_ERROR/100.0)
print " Offset error:%1.9f[V] (%2.4f%% FS), max allowed:+/-%1.5f[V] => "%(offset_err, offset_err_pfs, max_offset_err),
if abs(offset_err) > max_offset_err:
print "ERROR ###"
error += 1
else:
print "OK"
# Calculate the actual value of the DAC
dac_d = volt2digital(v_dac[i],DAC_FS,DAC_NBITS)
dac_r = digital2volt(dac_d,DAC_FS,DAC_NBITS)
# Calculate gain error (note that the ADC input is inversely proportional to the DAC output)
gain_err = (v_meas[1][ch-1] - offset_err) + (dac_r)
# Gain error in % FS
gain_err_pfs = (100.0/ADC_FS[IN_RANGE]) * abs(gain_err)
# Calculate maximum gain error
max_gain_err = ADC_FS[IN_RANGE] * (MAX_GAIN_ERROR/100.0)
print " Gain error :%1.9f[V] (%2.4f%% FS), max allowed:+/-%1.5f[V] => "%(gain_err, gain_err_pfs, max_gain_err),
if abs(gain_err) > max_gain_err:
print "ERROR ###"
error += 1
else:
print "OK"
# Open all switches, reset offset DAC to mid-scale (0V)
for channel in range(1,NB_CHANNELS+1):
fmc.set_input_range(channel, 'OPEN')
fmc.set_input_term(channel, 'OFF')
fmc.dc_offset_reset()
# Close AWG
gen.close()
except(FmcAdc100mSpecOperationError,FmcAdc100mOperationError,
CalibrBoxOperationError, CP210xEepromOperationError) as e:
raise PtsError("Test failed: %s" % e)
print ""
print "==> End of test%02d" % TEST_NB
print "================================================================================"
end_test_time = time.time()
print "Test%02d elapsed time: %.2f seconds\n" % (TEST_NB, end_test_time-start_test_time)
# Check if an error occured during calibration verification
if(error != 0):
raise PtsError('An error occured during calibration verification, check log for details.')
if __name__ == '__main__' :
main()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment