External Adjustment for the Atmel iADC

From Casper
Jump to navigationJump to search

Memo (PDF)


Datasheet


Test Design

Error creating thumbnail: Unable to save thumbnail to destination


Code

Simulation


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


Testing and Adjusting



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

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