External_Adjustment_for_the_Atmel_iADC - david-macmahon/wiki_convert_test GitHub Wiki

Memo (PDF)

  • External Adjustment for the Atmel iADC - January 2011 (Hong Chen, Mark Wagner)

Datasheet

  • AT84AD001B_datasheet
  • AT84AD001C_iADC-datasheet
  • Memo on iadc_controller by David George

Test Design

  • Download: Model file and bof file

 File:Iadc ``test`` ``design.png

Code

Simulation


  • Download (Code): Iadc_simulation_code.tar
  • ''' Code: adc_simulation.m '''

Software simulation of interleaved ADC for effects caused by gain mismatch, delay and offset. Simulate with simple monochromatic sin wave.

 % Interleaved ADC mismatches simulation % including gain, delay, and offset; % Sampling frequency  Fs = 2e9;  % Sample time T = 1/Fs;                      % Length of signal L = (2^16)*8;                     % Time vector t = (0:L-1)*T;    t1 = t(1:2:end); t2 = t(2:2:end); % Frequency of Sine Wave freq = 1e8; % delay % default = 0.0 delay=0.05; t2=t2+delay*(1/Fs); % gain % default= 1.0 gain=1.01; % offset % default = 0.0 offset=0.05; % Create a sine wave of input frequency. x = sin(2*pi*t*freq);  x1 = (sin(2*pi*t1*freq)+offset)*gain; x2 = sin (2*pi*t2*freq); x(1:2:end)=x1; x(2:2:end)=x2; nfft = 2^nextpow2(L); % Next power of 2 from length of y y = fft(x,nfft)/L; f = Fs/2*linspace(0,1,nfft/2+1); f = f/(1e6)*1.0; % Plot single-sided amplitude spectrum. figure(3) semilogy(f,2*abs(y(1:nfft/2+1)))  graph_title=strcat('Power Spectrum of a',char(32)); graph_title2=num2str(freq/(1e6)*1.0); graph_title2=strcat(graph_title2,'MHz Sine Wave, with delay:'); graph_title2=strcat(graph_title2,num2str(delay,8)); graph_title2=strcat(graph_title2,' offset:'); graph_title2=strcat(graph_title2,num2str(offset,8)); graph_title2=strcat(graph_title2,' gain:'); graph_title2=strcat(graph_title2,num2str(gain,8)); graph_title={graph_title,graph_title2}; title(graph_title);  xlabel('Frequency (MHz)');  ylabel('Power'); % Loop through different frequency value % figure(2) % for i=1:1:15, %     freq=freq+i*(freq/15); %     x1 = (sin(2*pi*t1*freq)+offset)*gain; %     x2 = sin (2*pi*t2*freq); %     x(1:2:end)=x1; %     x(2:2:end)=x2; %     y = fft(x,nfft)/L; %     semilogy(f,2*abs(y(1:nfft/2+1)))  %     graph_title=strcat('Power Spectrum of a ',num2str(freq)); %     graph_title=strcat(graph_title,'Hz Sine Wave, with delay:'); %     graph_title=strcat(graph_title,num2str(delay,8)); %     graph_title=strcat(graph_title,' offset:'); %     graph_title=strcat(graph_title,num2str(offset,8)); %     graph_title=strcat(graph_title,' gain:'); %     graph_title=strcat(graph_title,num2str(gain,8)); %     title(graph_title);  %     xlabel('Frequency (Hz)');  %     ylabel('Power'); %     pause(1); % end  

More Simulation

  • Download (Code and Plots):Iadc_simulation.tar

Testing and Adjusting


  • Download (Code): Iadc_ext_adj_code.tar

Interleaved ADC Testing and External Adjustment Code


iadc.py
  • iadc.py

A collection of functions that can be used to adjust the iADC by writing to the registers.

'''  Functions to write to registers of iADC and adjust it  Author: Hong Chen  Date: July 23, 2010 ''' val=roach.read('iadc_controller',128) '''   read the original value of iadc_controller ''' def read_iadc():     val=roach.read('iadc_controller',128)     return val '''    #first thing to do ''' def start_test():     print '\n'     print 'Starting the test...'     roach.blindwrite('iadc_controller','%c%c%c%c'%(0x0,0x0,0x03,0x0))     time.sleep(0.001)     print 'You can write to the registers now.'     return read_iadc() '''    # reset the DCM ''' def reset_dcm():     #print 'resetting the dcm...'     roach.blindwrite('iadc_controller','%c%c%c%c'%(0x0,0x0,0x03,0x03))     time.sleep(0.001)     #print 'resetting dcm completed'     return read_iadc() ''' start a new calibration phase Old DATA =0110 0100 0110 1100 DATA =X X X X 1 1 0 X X X X X X X X X = 0110 1100 0110 1100 = 0x6c, 0x6c    ADDR =000 ''' def new_cal():    print '\n'    print 'starting a new calibration phase...'    roach.blindwrite('iadc_controller','%c%c%c%c'%(0x6c,0x6c,0x0,0x1),offset=0x4)       time.sleep(0.001) # probably unnecessary wait for delay to take    reset_dcm()    print 'new calibration phase completed.'    return read_iadc() ''' set to no calibration mode so that gain compensation and offset compensation can be done ''' def no_calibration():    print '\n'    print 'setting to no calibration mode...'    roach.blindwrite('iadc_controller','%c%c%c%c'%(0x60,0x6c,0x0,0x01),offset=0x4)    time.sleep(0.001) # probably unnecessary wait for delay to take    reset_dcm()    print 'no calibration mode setting completed.'    return read_iadc() '''    #intput_i    analog input channel selection: analog I-i/q    xxxx|xx0x|xx10|xxxx    =0x00,0x20 (adc0_data)  #or = 0110 0100 0110 1100 =  0x64, 0x6c       ''' def input_i():       print '\n'    print 'selecting input channel I...'    roach.blindwrite('iadc_controller','%c%c%c%c'%(0x64,0x6c,0x0,0x1),offset=0x4)       time.sleep(0.001) # probably unnecessary wait for delay to take    reset_dcm()    print 'Input channel: I-> ADC I&Q'    return read_iadc() '''    #intput_q    analog input channel selection: analog Q-i/q    xxxx|xx0x|xx0x|xxxx    =0x00,0x20 (adc0_data)  #or = 0110 0100 0100 1100 =  0x64, 0x4c   ''' def input_q():    print '\n'    print 'selecting input channel Q...'    roach.blindwrite('iadc_controller','%c%c%c%c'%(0x64,0x4c,0x0,0x1),offset=0x4)       time.sleep(0.001) # probably unnecessary wait for delay to take    reset_dcm()    print 'Input channel:Q-> ADC I&Q'    return read_iadc() '''    # input_iq    analog input channel selection: analog I-i, Q-q    xxxx|xx0x|xx11|xxxx     0110 0100 0111 1100 = 0x64,0x7c ''' def input_iq():    print '\n'    print 'selecting input channel: I & Q ...'    roach.blindwrite('iadc_controller','%c%c%c%c'%(0x64,0x7c,0x0,0x1),offset=0x4)      time.sleep(0.001) # probably unnecessary wait for delay to take    reset_dcm()    print 'Input channel: I-> ADC I;   Q-> ADC Q'    return read_iadc()    # default offset value = 00000000b = 0x00 = 0LSB # i channel and q channel offset_vi=0x00 offset_vq=0x00 '''    # offset compensation     #step 1 * 0.25LSB    address 010 = 0x02    DATA7 to DATA0: channel I    DATA15 to DATA8: channel Q    code 11111111b=0xff = 31.75LSB    code 10000000b=00000000b= 0x80=0x00= 0LSB    code 01111111b=0x7f= -31.75LSB    # code 10000001b=0x81 = 1LSB ''' def offset_inc(channel):     global offset_vi     global offset_vq     v=offset_vi     v2=offset_vq     if v>=128:        v=v+1     elif v==0:        v=129     else:        v=v-1     if v2>=128:        v2=v2+1     elif v2==0:        v2=129     else:        v2=v2-1     if channel=='i':        offset_vi=v     elif channel=='q':        offset_vq=v2     elif channel=='iq' or channel=='qi':        offset_vi=v        offset_vq=v2     else:        print 'invalid argument!'          return     roach.blindwrite('iadc_controller','%c%c%c%c'%(offset_vq,offset_vi,0x02,0x01),offset=0x4)     time.sleep(0.001) # probably unnecessary wait for delay to take     return read_iadc() '''    # offset compensation     #step -1 * 0.25LSB    address 010 = 0x02    DATA7 to DATA0: channel I    DATA15 to DATA8: channel Q    code 11111111b=0xff = 31.75LSB    code 10000000b=00000000b= 0x80=0x00= 0LSB    code 01111111b=0x7f= -31.75LSB    # code 00000001b=0x01 = -1LSB ''' def offset_dec(channel):     global offset_vi     global offset_vq     v=offset_vi     v2=offset_vq     if v==128:        v=1     elif v==0:        v=1     elif v<128:        v=v+1     else:        v=v-1     if v2==128:        v2=1     elif v2==0:        v2=1     elif v2<128:        v2=v2+1     else:        v2=v2-1     if channel=='i':        offset_vi=v     elif channel=='q':        offset_vq=v2     elif channel=='iq' or channel=='qi':        offset_vi=v        offset_vq=v2     else:        print 'invalid argument!'          return     roach.blindwrite('iadc_controller','%c%c%c%c'%(offset_vq,offset_vi,0x02,0x01),offset=0x4)     time.sleep(0.001) # probably unnecessary wait for delay to take     reset_dcm()     return read_iadc() '''    # offset compensation     #step -1 * 0.25LSB    address 010 = 0x02    DATA7 to DATA0: channel I    DATA15 to DATA8: channel Q    code 11111111b=0xff = 31.75LSB    code 10000000b=00000000b= 0x80=0x00= 0LSB    code 01111111b=0x7f= -31.75LSB    # code 00000000b=0x00 = 0LSB ''' def offset_0(channel):     print '\n'     print 'setting the offset to 0 for channel: '+channel     global offset_vi     global offset_vq     if channel=='i':        offset_vi=0     elif channel=='q':        offset_vq=0     elif channel=='iq' or channel=='qi':        offset_vi=0        offset_vq=0     roach.blindwrite('iadc_controller','%c%c%c%c'%(offset_vi,offset_vq,0x02,0x01),offset=0x4)     time.sleep(0.001) # probably unnecessary wait for delay to take     reset_dcm()     print 'setting completed. channel '+channel+': offset 0'     return read_iadc() '''  # offset compensation inc loop ''' def offset_inc_loop(channel,n):     for i in range(0,n):         offset_inc(channel)     return read_iadc() '''  # offset compensation dec loop ''' def offset_dec_loop(channel,n):     for i in range(0,n):         offset_dec(channel)     return read_iadc() '''    # offset compensation     #step -1 * 0.25LSB    address 010 = 0x02    DATA7 to DATA0: channel I    DATA15 to DATA8: channel Q    code 11111111b=0xff = 31.75LSB    code 10000000b=00000000b= 0x80=0x00= 0LSB    code 01111111b=0x7f= -31.75LSB    # code 11111111b=0xff = 31.75LSB ''' def offset_max():     global offset_vi,offset_vq     print '\n'     print 'setting offset to maximum value...'     roach.blindwrite('iadc_controller','%c%c%c%c'%(0xff,0xff,0x02,0x01),offset=0x4)     time.sleep(0.001) # probably unnecessary wait for delay to take     reset_dcm()     offset_vi=0xff     offset_vq=0xff      print 'setting completed. Offset: 31.75LSB(maximum)'     return read_iadc() '''    # offset compensation     #step -1 * 0.25LSB    address 010 = 0x02    DATA7 to DATA0: channel I    DATA15 to DATA8: channel Q    code 11111111b=0xff = 31.75LSB    code 10000000b=00000000b= 0x80=0x00= 0LSB    code 01111111b=0x7f= -31.75LSB    # code 01111111b=0xff = -31.75LSB ''' def offset_min():     print '\n'     print 'setting the offset to minimum value...'     global offset_vi,offset_vq     roach.blindwrite('iadc_controller','%c%c%c%c'%(0x7f,0x7f,0x02,0x01),offset=0x4)     time.sleep(0.001) # probably unnecessary wait for delay to take     reset_dcm()     offset_vi=0x7f     offset_vq=0x7f     print 'setting completed.  Offset: -31.75LSB(minimum)'     return read_iadc() gain_vi=0x80 gain_vq=0x80 '''    # analog gain adjustment    # gain set to minimum = -1.5dB    # address = 001 = 0x01    DATA7 to DATA0: channel I    DATA15 to DATA8: channel Q    code 00000000=0x00= -1.5dB    code 10000000 = 0x80 = 0dB    code 11111111=0xff = 1.5dB ''' def gain_min():    print '\n'    print 'setting the gain to minimum value...'    global gain_vi,gain_vq    roach.blindwrite('iadc_controller','%c%c%c%c'%(0x00,0x00,0x01,0x1),offset=0x4)    time.sleep(0.001) # probably unnecessary wait for delay to take    reset_dcm()    gain_vi=0x00    gain_vq=0x00    print 'setting completed. Gain: -1.5dB(minimum)'    return read_iadc() '''   # analog gain adjustment    # gain set to 0dB    # address = 001 = 0x01    DATA7 to DATA0: channel I    DATA15 to DATA8: channel Q    code 00000000=0x00= -1.5dB    code 10000000 = 0x80 = 0dB    code 11111111=0xff = 1.5dB ''' def gain_0():    print '\n'    print 'setting the gain to 0dB...'    global gain_vi,gain_vq    roach.blindwrite('iadc_controller','%c%c%c%c'%(0x80,0x80,0x01,0x1),offset=0x4)    time.sleep(0.001) # probably unnecessary wait for delay to take    reset_dcm()    gain_vi=0x80    gain_vq=0x80    print 'setting completed. Gain: 0dB'    return read_iadc() '''    # analog gain adjustment    #gain set to max=1.5dB       # step -1*0.011 dB    # address = 001 = 0x01    DATA7 to DATA0: channel I    DATA15 to DATA8: channel Q    code 00000000=0x00= -1.5dB    code 10000000 = 0x80 = 0dB    code 11111111=0xff = 1.5dB    # code 01111111 = 0x7f = -0.001dB ''' def gain_max():      print '\n'      print 'setting the gain to maximum value...'      global gain_vi,gain_vq      roach.blindwrite('iadc_controller','%c%c%c%c'%(0xff,0xff,0x01,0x1),offset=0x4)      time.sleep(0.001) # probably unnecessary wait for delay to take      reset_dcm()      gain_vi=0xff      gain_vq=0xff      print 'setting completed. Gain: 1.5dB(maximum)'      return read_iadc() ''' analog gain adjustment on channel i ''' def gain_inc_loop_i(n):    global gain_vi,gain_vq    v=gain_vi    if (n+v>255):      return 'too big!'    result=arange(0,n,1)    for i in range(0,n):        v=v+1        roach.blindwrite('iadc_controller','%c%c%c%c'%(gain_vq,v,0x01,0x1),offset=0x4)        time.sleep(0.001) # probably unnecessary wait for delay to take        reset_dcm()    gain_vi=v    return read_iadc() gc_v=0x00 ''' Gain Compensation adjustment Gain compensation NOTE:  ONLY 7 BITS,  THE EXAMPLE GIVEN IN THE DATASHEET IS NOT REALLY CORRECT Data6 to Data0: channel I/Q (Q is matched to I for interleaving adjustment) Code 11111111b: ?.315 dB Code 10000000b: 0 dB Code 0000000b: 0 dB Code 0111111b: 0.315 dB Steps: 0.005 dB Data6: sign bit Data15 to Data7 = XXX ''' ''' increase gain compensation by 1 ''' def gc_inc():     global gc_v     v=gc_v     if v==64:        v=1     elif v>64:        v=v-1     elif v==63:        print 'maximum reached!'        return     else:        v=v+1     roach.blindwrite('iadc_controller','%c%c%c%c'%(v,v,0x03,0x01),offset=0x4)     time.sleep(0.001) # probably unnecessary wait for delay to take     reset_dcm()     gc_v=v     return read_iadc() ''' decrease gain compensation by 1 ''' def gc_dec():     global gc_v     v=gc_v     if v==0:        v=65     elif v>=64:        v=v+1     elif v==127:        print 'minimum reached!'        return     else:        v=v-1     roach.blindwrite('iadc_controller','%c%c%c%c'%(v,v,0x03,0x01),offset=0x4)     time.sleep(0.001) # probably unnecessary wait for delay to take     reset_dcm()     gc_v=v     return read_iadc() ''' gain compensation adjustment, using gc_inc(), loop ''' def gc_inc_loop(n):     for i in range(0,n):        gc_inc() ''' gain compensation adjustment, using gc_dec(), loop ''' def gc_dec_loop(n):     for i in range(0,n):        gc_dec() ''' gain compensation to minimum  11111111 =0xff,  -0.315db ''' def gc_min():      print '\n'      print 'setting the gain compensation to minimum value...'      global gc_v      roach.blindwrite('iadc_controller','%c%c%c%c'%(0xff,0xff,0x03,0x01),offset=0x4)      time.sleep(0.001) # probably unnecessary wait for delay to take      reset_dcm()      gc_v=0xff      print 'setting completed. Gain compensation: -0.315dB(minimum)'      return read_iadc() ''' gain compensation to maximum  01111111 =0x7f,  0.315db REAL VALUE SHOULD BE          00111111 =0x3f,  0.315db ''' def gc_max():      print '\n'      print 'setting the gain compensation to maximum value...'      global gc_v      roach.blindwrite('iadc_controller','%c%c%c%c'%(0x3f,0x3f,0x03,0x01),offset=0x4)      time.sleep(0.001) # probably unnecessary wait for delay to take      reset_dcm()      gc_v=0x7f      print 'setting completed. Gain compensation: 0.315dB'      return read_iadc() ''' gain compensation to 0  00000000 = 0x00, 0db ''' def gc_0():      print '\n'      print 'setting the gain compensation to 0...'      global gc_v      roach.blindwrite('iadc_controller','%c%c%c%c'%(0x00,0x00,0x03,0x01),offset=0x4)      time.sleep(0.001) # probably unnecessary wait for delay to take      reset_dcm()      gc_v=0x0      print 'setting completed. Gain compensation: 0dB'      return read_iadc()     fisda_v=0   # default value ''' adjust the fine sampling data adjustment (FISDA) on channel Q ADDR = 111 = 0x07 DATA10 to DATA6 ''' def fisda_inc():     global fisda_v,drda_i,drda_q     if fisda_v==0xf:        print 'maximum reached!'        return     elif fisda_v==16:        fisda_v=1     elif fisda_v>16:        fisda_v=fisda_v-1     else:        fisda_v=fisda_v+1     b=((fisda_v&0x3)<<6)+(drda_q<<3)+drda_i     # marks out the lowest 2 bits in fisda_v, together with drda_q & drda_i, DATA7 to DATA0     a=fisda_v>>2     roach.blindwrite('iadc_controller','%c%c%c%c'%(a,b,0x07,0x01),offset=0x4)     time.sleep(0.001) # probably unnecessary wait for delay to take     reset_dcm()     return read_iadc() ''' adjust the fine sampling data adjustment (FISDA) on channel Q ADDR = 111 = 0x07 DATA10 to DATA6 ''' def fisda_dec():     global fisda_v,drda_i,drda_q     if fisda_v==0x18:        print 'minimum reached!'        return     elif fisda_v==0:        fisda_v=0x11  # assume 11111 is the minimum, -60 ps,  so 10001 should be -4 ps     elif fisda_v>16:        fisda_v=fisda_v+1     else:        fisda_v=fisda_v-1     b=((fisda_v&0x3)<<6)+(drda_q<<3)+drda_i     # marks out the lowest 2 bits in fisda_v, together with drda_q & drda_i, DATA7 to DATA0     a=fisda_v>>2     ##print '%x %x'%(a,b)     roach.blindwrite('iadc_controller','%c%c%c%c'%(a,b,0x07,0x01),offset=0x4)     time.sleep(0.001) # probably unnecessary wait for delay to take     reset_dcm()     return read_iadc() ''' adjust the fine sampling data adjustment (FISDA) on channel Q ADDR = 111 = 0x07 DATA10 to DATA6 loop using fisda_inc() ''' def fisda_inc_loop(n):     for i in range(0,n):         fisda_inc()     return read_iadc() ''' adjust the fine sampling data adjustment (FISDA) on channel Q ADDR = 111 = 0x07 DATA10 to DATA6 loop using fisda_dec() ''' def fisda_dec_loop(n):     for i in range(0,n):         fisda_dec()     return read_iadc()  

analysis_function.py
  • analysis_function.py

A collection of functions that can be used to process data.

'''    functions to deal with data ''' ''' % Author: Hong Chen % Date: August 5,2010 ''' import corr,pylab,numpy,struct ''' input: stream x which represents a sine wave output: the amplitude of the sine wave calculate the amplitude of x using the RMS = amp/sqrt(2) ''' def amp(x):     rms=0     for i in range(0,size(x)):        rms=rms+x[i]*x[i]     rms=rms/size(x)     rms=sqrt(rms)     amp=rms*sqrt(2)     return amp ''' input: two stream x1 and x2, each of them represents a sine wave output: the difference of the amplitudes of x1 and x2 take in two streams of data, caculate their amplitudes seperately, and get the difference between the two amplitudes ''' def diff_amp(x1,x2):     a1=amp(x1)     a2=amp(x2)     print 'amp1 '+str(a1)+'     '+'amp2 '+str(a2)     return a1-a2 ''' input: a stream x output: the difference of the amplitudes of x1 and x2, where x1 and x2 are from x (every other sample) ''' def diff_amp_x(x):     x1=x[0:size(x)+1:2]     x2=x[1:size(x)+1:2]     return diff_amp(x1,x2) ''' input: a stream x, the smapling rate output: the frequency of x ''' def tone_freq(data, fs):     data_len = data.size     fft_data = numpy.fft.fft(data)     fft_abs = numpy.abs(fft_data[0:data_len/2]);     max_pos = numpy.where(fft_abs == fft_abs.max())[0][0]     return float(max_pos)*float(fs)/float(data_len) ''' input: a stream f which is the result of fft of a sine wave, the smapling rate output: the frequency of x ''' def get_freq(f, fs):     data_len = size(f)*2     max_pos = numpy.where(f == f.max())[0][0]     return float(max_pos)*float(fs)/float(data_len) ''' input: a stream f which is the result of fft of a sine wave, the sampling frequency Fs output: the height of spike resulted in interleaving ''' def h_interleaving(f,Fs):     freq=get_freq(f,Fs)     ind=int((Fs/2-freq)*size(f)/(Fs/2))     print 'ind '+str(ind)+'    '+str(size(f))+'     freq: '+str(freq)+'    Fs:'+str(Fs)     print ind*1.0/size(f), ((Fs/2)-freq)/(Fs/2)     start=ind-50     if start<0:        start=0     end=ind+50     if end>size(f):        end=size(f)     spike=f.max()/100     position=-1     for i in range(start,end):         if f[i]>spike:            spike=f[i]            position=i     print 'spike: '+str(spike)+'    position: '+str(position)     return position      ''' find the height of spike which is resulted in interleaving issue ''' def h_s(f,position):     spike=f[position]     print 'spike: '+str(spike)+'    position: '+str(position)     return spike  

config_setup.py
  • config_setup.py

Connect to ROACH board and run the design.

'''   Connect to the ROACH board and run the design ''' ''' % Author: Hong Chen % Date: August 5,2010 ''' import corr,time roach=corr.katcp_wrapper.FpgaClient('192.168.1.217',7147) time.sleep(0.001)   # wait for roach to connect roach.progdev('interleaved_adc_64_2010_Jul_06_1446.bof')  

start.py
  • start.py

Test initialization. start_test() defined in iadc.py

''' % Author: Hong Chen % Date: August 5,2010 ''' print '-------------------------------------' print 'Programming the bof file...' print '-------------------------------------' roach.progdev('interleaved_adc_64_2010_Jul_06_1446.bof') # autamatically do the first thing start_test()  

maker.py
  • maker.py

Trigger the snap64 block and start capturing data. Then read the data out and we'll get 2^16 64-bit unsigned numbers. Each of these unsigned numbers is from 8 8-big unsigned numbers concatenated together. This program will split them up and restore them back to their original value before writing them to a data file.

''' % Author: Hong Chen % Date: August 5,2010 ''' import corr import struct import time from numpy import * from pylab import * '''     Trigger the snap64 block and capture      data, then read it and get (2**16)      64-bit unsigned numbers. Each of the      unsigned numbers is concatenated by 8      8-bit unsigned numbers. We need to      split each long number and get the      original data.     After the 8*(2**16) 8-bit signed number      is recovered, write it to a file using      argument 'name' as file name. ''' def datafile_maker(name):   mask0=2**8-1   mask1=mask0<<8   mask2=mask1<<8   mask3=mask2<<8   divisor=float(2**7)   roach.write_int('snap64_ctrl',0)   roach.write_int('snap64_ctrl',7)   time.sleep(0.1)   roach.write_int('snap64_ctrl',0)   x = roach.read('snap64_bram_msb', 65536*4)   x1 = roach.read('snap64_bram_lsb',65536*4)   y0 = struct.unpack('>65536l', x)   y1 = struct.unpack('>65536l',x1)   # interleave part I   y=arange(0,size(y0)*8,1)   for i in range(0,size(y0)):      y[8*i]   = short((y0[i] & mask3) >> 24)      y[8*i+1] = short((y0[i] & mask2) >> 16)      y[8*i+2] = short((y0[i] & mask1) >> 8)      y[8*i+3] = short((y0[i] & mask0))   # interleave part II   for i in range(0,size(y0)):      y[8*i+4] = short((y1[i] & mask3) >> 24)      y[8*i+5] = short((y1[i] & mask2) >> 16)      y[8*i+6] = short((y1[i] & mask1) >> 8)      y[8*i+7] = short((y1[i] & mask0))   # convert to signed integer: 8 bit   for i in range(0,size(y)):       if (y[i]>127):           y[i]=y[i]-256      # write to file   datafile=open(name,'w')   for i in range(0,size(y)):      datafile.write(str(y[i])+'\n')   datafile.close()         '''   i = range(0,size(y))   print size(y)   plot(i[0:1024/10],y[0:1024/10])   show()   '''   '''   plot(y[1:100])   show()   '''   '''   # FFT and plot   # adc freq=1 GHz = 1000 MHz   Fs = 2000000000 # sampling frequency: 2 GHz   T = 1.0/Fs  # sample time   L = 8*(2**16)  # length of sample points   nfft = L   k = fft(y,nfft)/L   f = Fs/2*linspace(0.0,1.0,nfft/2+1)   #print size(f)   #print size(k)   semilogy(f,2*abs(k[0:nfft/2+1]))    title('interleaved ADCs on roach')   xlabel('frequency')   ylabel('magnitude')   show()   '''   # val=roach.read('iadc_controller',128)  

fft_plot.m
  • '' fft_plot.m''

This program will read the data file made by maker.py, do Fourier Transform to the data and plot its spectrum in frequency domain.

% read from a datafile and do the fft, then plot it % % Author: Hong Chen % Date: August 5,2010 clear all; x= dlmread('filename.txt','\n'); % read from datafile Fs=2e9; % set the sampling rate L=length(x); nfft=2^nextpow2(L); f=Fs/2*linspace(0,1,nfft/2+1); Y3=fft(x,nfft)/L; semilogy(f,2*abs(Y3(1:nfft/2+1))) % plot the result graph_title='interleaved adcs with input signal: 93MHz (test1)'; title(graph_title); xlabel('Frequency (Hz)'); ylabel('Power');  

zoom.m
  • zoom.m

% plot the a small part of the sample data % zoom in and see the details % % Author: Hong Chen % Date: August 5,2010 x= dlmread('default_0.txt','\n'); % read from a data file start=1;   % set the begin of the segment length=100; % set the length of the segment p=1:2:length;  % the x-axis reference plot(p,x(start:2:start+length-1),'or') hold on; plot(p+1,x(start+1:2:start+length),'o') hold on; plot(x(start:1:start+length-1),'-k')  

tester.py
  • tester.py

Basically the same as maker.py, but return the data as a long array instead of writing it into a data file.

''' % Author: Hong Chen % Date: August 5,2010 ''' import corr import struct import time from numpy import * from pylab import * '''  basically the same as maker.py  but return the raw data as a long array rather  than write to a data file  no arguments required ''' def tester():   mask0=2**8-1   mask1=mask0<<8   mask2=mask1<<8   mask3=mask2<<8   divisor=float(2**7)   roach.write_int('snap64_ctrl',0)   roach.write_int('snap64_ctrl',7)   time.sleep(0.1)   roach.write_int('snap64_ctrl',0)   x = roach.read('snap64_bram_msb', 65536*4)   x1 = roach.read('snap64_bram_lsb',65536*4)   y0 = struct.unpack('>65536l', x)   y1 = struct.unpack('>65536l',x1)   # interleave part I   y=arange(0,size(y0)*8,1)   for i in range(0,size(y0)):      y[8*i]   = short((y0[i] & mask3) >> 24)      y[8*i+1] = short((y0[i] & mask2) >> 16)      y[8*i+2] = short((y0[i] & mask1) >> 8)      y[8*i+3] = short((y0[i] & mask0))   # interleave part II   for i in range(0,size(y0)):      y[8*i+4] = short((y1[i] & mask3) >> 24)      y[8*i+5] = short((y1[i] & mask2) >> 16)      y[8*i+6] = short((y1[i] & mask1) >> 8)      y[8*i+7] = short((y1[i] & mask0))   # convert to signed integer: 8 bit   for i in range(0,size(y)):       if (y[i]>127):           y[i]=y[i]-256   '''   # write to file   datafile=open(name,'w')   for i in range(0,size(y)):      datafile.write(str(y[i])+'\n')   datafile.close()   '''   return y   '''   i = range(0,size(y))   print size(y)   plot(i[0:1024/10],y[0:1024/10])   show()   '''   '''   plot(y[1:100])   show()   '''   # FFT and plot   # adc freq=1 GHz = 1000 MHz   Fs = 2000000000 # sampling frequency: 2 GHz   T = 1.0/Fs  # sample time   L = 8*(2**16)  # length of sample points   nfft = L   k = fft(y,nfft)/L   f = Fs/2*linspace(0.0,1.0,nfft/2+1)   #print size(f)   #print size(k)   semilogy(f,2*abs(k[0:nfft/2+1]))    title('interleaved ADCs on roach')   xlabel('frequency')   ylabel('magnitude')   show()   #'''   # val=roach.read('iadc_controller',128)  

fft_out.py
  • fft_out.py

One step further than maker.py. This program extracts data out of the design, performs Fourier Transform, plots the spectrum and returns the frequency array as output value. The argument "channel" allows value 0,1 or 2 to select signals from either of the two interleaved ADCs or from both of them. (I encountered problems of missing spikes in spectrum plot using python, so I have to switch to Matlab to do the spectrum plotting, as in #fft_plot.m)

''' % Author: Hong Chen % Date: August 5,2010 ''' import corr import struct import time from numpy import * from pylab import * def fft_out(channel):   mask0=2**8-1   mask1=mask0<<8   mask2=mask1<<8   mask3=mask2<<8   divisor=float(2**7)   roach.write_int('snap64_ctrl',0)   roach.write_int('snap64_ctrl',7)   time.sleep(0.1)   roach.write_int('snap64_ctrl',0)   x = roach.read('snap64_bram_msb', 65536*4)   x1 = roach.read('snap64_bram_lsb',65536*4)   y0 = struct.unpack('>65536l', x)   y1 = struct.unpack('>65536l',x1)   # interleave part I   y=arange(0,size(y0)*8,1)   for i in range(0,size(y0)):      y[8*i]   = short((y0[i] & mask3) >> 24)      y[8*i+1] = short((y0[i] & mask2) >> 16)      y[8*i+2] = short((y0[i] & mask1) >> 8)      y[8*i+3] = short((y0[i] & mask0))   # interleave part II   for i in range(0,size(y0)):      y[8*i+4] = short((y1[i] & mask3) >> 24)      y[8*i+5] = short((y1[i] & mask2) >> 16)      y[8*i+6] = short((y1[i] & mask1) >> 8)      y[8*i+7] = short((y1[i] & mask0))   # convert to signed integer: 8 bit   for i in range(0,size(y)):       if (y[i]>127):           y[i]=y[i]-256        '''   i = range(0,size(y))   print size(y)   plot(i[0:1024/10],y[0:1024/10])   show()   '''   '''   plot(y[1:100])   show()   '''      # FFT and plot   # adc freq=1 GHz = 1000 MHz   Fs = 1000000000 # sampling frequency: 2 GHz   T = 1.0/Fs  # sample time   L = 4*(2**16)  # length of sample points   nfft = L   if channel==0:      p = y[0:L:2]   elif channel==1:      p = y[1:L:2]   elif channel==2:      p = y      Fs=Fs*2      T=1.0/Fs      L=L*2      nfft=L   else:      print 'invalid argument(s)!'      return   k = fft(p,nfft)/L   f = Fs/2*linspace(0.0,1.0,nfft/2+1)   return 2*abs(k[0:nfft/2+1])      '''   #print size(f)   #print size(k)   semilogy(f,2*abs(k[0:nfft/2+1]))    title('interleaved ADCs on roach')   xlabel('frequency')   ylabel('magnitude')   show()   '''   # val=roach.read('iadc_controller',128)  

auto_adjust.py
  • auto_adjust.py

Running this program will automatically adjust the offset, gain and delay mismatch.

'''   Using e2v Dual 8-bit 1Gsps ADC, AT84AD001B, automatically adjust the register to minimize the offset, gain and delay mismatch   Should be working on AT84AD001C as well   Author: Hong Chen   Date: July 23, 2010 ''' Fs=2e9 execfile('maker.py') execfile('tester.py')   execfile('fft_out.py') execfile('iadc.py') execfile('analysis_function.py') execfile('start.py')   # reprogram the bof file no_calibration()   # set to no calibration mode, so that I'll be able to access the registers and adjust the gain and offset compensation offset_0('iq')    # set offset to 0 for both channel I and channel Q gain_0()    # set analog gain to 0 dB (channel I & channel Q) gc_0() # set gain compensation to 0 dB test=tester()  # get the 8*(2**16) sample points, main purpose here is to make sure that the program has been compiled successfully                # if an error message pops out, please re-run this *auto_adjust.py* again. ''' automatically adjust offset for channel I or channel Q (seperately) TOL is just an approximate expectation to the final result, when it says 'unseccessful!', the result may still be good as long as it's not too far away from TOL # function fft_out(chnl) defined in analysis_function.py # function offset_inc(channel), offset_dec(channel) defined in iadc.py related material in datasheet: AT84AD004B,p35   AT84AD001C,p33 ''' def offset_adjust(channel):     print '\n Start adjusting the offset for channel: '+channel     if channel=='i':        chnl=1     else:        chnl=0     Y=fft_out(chnl)  # get 8*(2**16) sample points, and do the fft, then get the absolute values and set them to Y          off_v0=Y[0]   # measure the height(power) of the spike at 0Hz     print off_v0  # print the current value in the register which controls the offset     off_v1=off_v0     TOL=Y.max()/600   # define the tolerance     print 'TOL '+str(TOL)     while off_v0>TOL:     # try increasing the offset, see if it can reduce the offset error        offset_inc(channel)        Y=fft_out(chnl)        off_v1=Y[0]        print 'offset value: '+str(off_v1)        if off_v1<off_v0:           off_v0=off_v1        else:                     # if increase the offset will not improve the performance, restore to the last state           offset_dec(channel)            break     if off_v0>TOL:       # if the error is still larger than the tolerance        Y=fft_out(chnl)        off_v0=Y[0]        while off_v0>TOL:    # try decreasing the offset, set if it can reduce the offset error            offset_dec(channel)            Y=fft_out(chnl)            off_v1=Y[0]            print 'offset value '+ str(off_v1)            if off_v1<off_v0:              off_v0=off_v1            else:         # if decrease the offset will not improve the performance, restore it to the last state, which should be the optimal              offset_inc(channel)              break     if off_v0>TOL:    # print the message, showing if we can reduce the offset error to the tolerance, and shows the value off the two registers        print 'unseccessful! offset_vi: '+str(offset_vi)+'  offset_vq: '+str(offset_vq)+'\n'     else:        print 'secessful! offset_vi: '+str(offset_vi)+'  offset_vq: '+str(offset_vq)+'\n' ''' automatically adjust the gain mismatch between two channels TOL2 is just an approximate expectation to the final result, when it says 'unseccessful!', the result may still be good as long as it's not too far away from TOL2 # function amp(x) defined in analysis_function.py # function gc_inc_loop(n), gc_dec_loop(n) defined in iadc.py related material in datasheet: AT84AD004B,p35     AT84AD001C,p33 ''' def gain_adjust():     print '\n start adjusting the gain mismatch...'     test=tester()     x1=test[0:size(test)+1:2]    # channel Q     x2=test[1:size(test)+1:2]    # channel I     amp1=amp(x1)      # measure the amplitude of the sine wave x1, function amp() defined in "analysis_function.py"     amp2=amp(x2)      # measure the amplitude of the sine wave x2     print 'amp1 '+str(amp1)+'     '+'amp2 '+str(amp2)     TOL2=abs(amp1-amp2)/2000    # define the tolerance     print 'TOL: '+str(TOL2)     diff0=amp1-amp2     # calculate the difference of the amplitudes for the two streams      while abs(diff0)>TOL2:   # try to reduce the difference till the tolerance         if diff0<0:                              gc_dec_loop(1)            test=tester()            diff1=diff_amp_x(test)            if abs(diff1)<abs(diff0):               diff0=diff1            else:               gc_inc_loop(1)               break;         else:            gc_inc_loop(1)            test=tester()            diff1=diff_amp_x(test)            if abs(diff1)<abs(diff0):               diff0=diff1            else:               gc_dec_loop(1)               break;     if abs(diff0)>TOL2:  # output message,  gain difference,  value of the register controls gain compensation         print 'unseccessful! gain difference: '+str(diff0)+'    gc_v: '+str(gc_v)+'\n'     else:         print 'yeah! gain difference: '+str(diff0)+'    gc_v:' +str(gc_v)+'\n' ''' delay adjustment TOL is just an approximate expectation to the final result, when it says 'unseccessful!', the result may still be good as long as it's not too far away from TOL # function h_interleaving(f,Fs),  h_s(f,position) defined in analysis_function.py # function fft_out(2) defined in fft_out.py # function fisda_inc(),fisda_dec() defined in iadc.py related material in datasheet:  AT84AD004B,p36      AT84AD001C,p34 '''     def delay_adjust():     print '\n start adjusting the delay...'     f=fft_out(2)   # get the result of fft of the sample data     position=h_interleaving(f,Fs)    # find the position of the spike mainly resulted in interleaving issue     spike=h_s(f,position)    # measure the height(power) of the spike mainly resulted in interleaving issue     TOL=f.max()/200     # define the tolerance     while spike>TOL:        fisda_inc()    # fisda: Fine Sampling Delay Adjustment(FISDA) on channel Q        f=fft_out(2)        spike2=h_s(f,position)        if spike2>spike:           fisda_dec()           break        else:           spike=spike2     while spike>TOL:        fisda_dec()        f=fft_out(2)        spike2=h_s(f,position)        if spike2>spike:           fisda_inc()           break        else:           spike=spike2     if spike>TOL:        print 'unseccessful! '+str(spike)+'   '+str(f.max())+'     '+'fisda_v(hex):  '+'%x'%fisda_v+'\n'     else:        print 'seccessful! '+str(spike)+'  '+str(f.max())+'     '+'fisda_v(hex):  '+'%x'%fisda_v+'\n' '''  Using the functions defined above, adjujst the offset for channel i and q, and then adjust the gain mismatch, finnally adjust the delay mismatch ''' datafile_maker('default1.txt') offset_adjust('i') offset_adjust('q') gain_adjust()  delay_adjust() print 'Automatic adjustment completed. Now start capturing data and make a datafile...' datafile_maker('test1.txt')   # function datafile_maker(filename) defined in maker.py                               # capture data and put the data into test1.txt print 'Now datafile test1.txt is ready.'  

Supplement Notes

  • Download: Supplement Notes (Code, data files, and plots)
  • Code: anag_gain_test.py

The Analog gain adjustment is not used as the step is too big for our purpose. But during our test we found some discrepancy between what the datasheet states and the actual behavior. This program perform some very rough test, but it's enough to show the inconsistent result. This observation is described in section 7.2 in the memo.

''' analog gain adjustment on channel i Author: Hong Chen Date: July 23, 2010 ''' def gain_inc_loop_i(n):    global gain_vi,gain_vq    v=gain_vi    if (n+v>255):      return 'too big!'    result=arange(0,n,1)    for i in range(0,n):        v=v+1        roach.blindwrite('iadc_controller','%c%c%c%c'%(gain_vq,v,0x01,0x1),offset=0x4)        time.sleep(0.001) # probably unnecessary wait for delay to take        reset_dcm()        y=tester()        result[i]=y.max()    datafile=open('analog_gain_test_result.txt','w')    datafile.write(str(gain_vi)+'\n')    for i in range(0,n):       datafile.write(str(gain_vi+i)+'   '+str(result[i])+'\n')    datafile.close()    gain_vi=v    datafile=open('analog_gain_test_plot.txt','w')    for i in range(0,n):       datafile.write(str(result[i])+'\n')    datafile.close()    return read_iadc() gain_vi=128   # set to minimum value gain_inc_loop_i(100)  # this program is quite slow due to the frequent register setting and file writing  

⚠️ **GitHub.com Fallback** ⚠️