Commit f186c99d authored by Tomasz Wlostowski's avatar Tomasz Wlostowski

tests02: fixed DDMTD calibration. Use latest SPEC/WRCore firmware for…

tests02: fixed DDMTD calibration. Use latest SPEC/WRCore firmware for calibration purposes. Cross-check CNT91 calibration against DDMTD
parent fbf493f9
......@@ -129,7 +129,7 @@ void fdelay_set_user_offset(fdelay_device_t *dev,int input, int64_t offset);
int fdelay_get_time(fdelay_device_t *dev, fdelay_time_t *t);
int fdelay_set_time(fdelay_device_t *dev, const fdelay_time_t t);
int fdelay_dmtd_calibration(fdelay_device_t *dev, const char *filename, uint32_t *userTimeout);
int fdelay_dmtd_calibration(fdelay_device_t *dev, const char *filename, int setpoint, int *measured_delays);
int fdelay_write_eeprom_from_file(fdelay_device_t *dev, const char *filename, uint32_t offset);
......
......@@ -2,6 +2,8 @@
OBJS = fdelay_lib.o i2c_master.o onewire.o spec_common.o spec/tools/speclib.o spec/kernel/loader-ll.o fdelay_dmtd_calibration.o
CFLAGS = -I../include -g -Imini_bone -Ispec/tools
SPEC_SW?=spec
ifeq ($(SPEC_SW),)
throw_error:
......
/*
FmcDelay1ns4Cha (a.k.a. The Fine Delay Card)
DMTD insertion delay calibration stuff
Short explaination:
We feed the input of the card with a perioid sequence of pulses, of a frequency, say,
1 MHz. The card is programmed to introduce a delay of Td. Then, we sample both the input
and the output of the card with a clock that is slightly offset in frequency wrs to the one that
was used to generate the pulses - in our case it's (1 + 1/16384) * 1 MHz. The resulting waveforms
are of very low frequency, but keep the phase shift of the original signals, scaled by a factor of 16384.
This way we can easily measure the actual insertion delay of the FD and apply a correction factor
for fdelay_configure_output().
Tomasz Włostowski/BE-CO-HT, 2012
(c) Copyright CERN 2012
Licensed under LGPL 2.1
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <sys/time.h>
#include <math.h>
#include "fd_channel_regs.h"
#include "fd_main_regs.h"
#include "fdelay_lib.h"
#include "fdelay_private.h"
extern void dbg(const char *fmt, ...);
extern int64_t get_tics();
extern void udelay(uint32_t usecs);
uint32_t wait_promptTimeout;
/*
FmcDelay1ns4Cha (a.k.a. The Fine Delay Card)
DMTD insertion delay calibration stuff
Short explaination:
We feed the input of the card with a perioid sequence of pulses, of a frequency, say,
1 MHz. The card is programmed to introduce a delay of Td. Then, we sample both the input
and the output of the card with a clock that is slightly offset in frequency wrs to the one that
was used to generate the pulses - in our case it's (1 + 1/16384) * 1 MHz. The resulting waveforms
are of very low frequency, but keep the phase shift of the original signals, scaled by a factor of 16384.
This way we can easily measure the actual insertion delay of the FD and apply a correction factor
for fdelay_configure_output().
Tomasz Włostowski/BE-CO-HT, 2012
(c) Copyright CERN 2012
Licensed under LGPL 2.1
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <sys/time.h>
#include <math.h>
#include "fd_channel_regs.h"
#include "fd_main_regs.h"
#include "fdelay_lib.h"
#include "fdelay_private.h"
extern void dbg(const char *fmt, ...);
extern int64_t get_tics();
extern void udelay(uint32_t usecs);
uint32_t wait_promptTimeout;
/* WR core shell communication functions */
......@@ -119,224 +119,261 @@ static int wrc_shell_exec(fdelay_device_t *dev, const char *cmd, char *retval)
dbg("wr_core exec '%s', retval: '%s'\n", cmd, retval);
return 0;
}
#define DMTD_N_AVGS 100 /* number of average samples */
#define DELAY_SETPOINT 750000 /* test delay value */
#define DMTD_PULSE_PERIOD 144
#define DMTD_OUTPUT_PERIOD (16384 * DMTD_PULSE_PERIOD / 2) /* period of the DDMTD output signal in 62.5 MHz clock cycles */
struct dmtd_channel {
int64_t base;
int64_t prev_tag, phase;
}
#define DMTD_N_AVGS 100 /* number of average samples */
#define DMTD_PULSE_PERIOD 144
#define DMTD_OUTPUT_PERIOD (16384 * DMTD_PULSE_PERIOD / 2) /* period of the DDMTD output signal in 62.5 MHz clock cycles */
struct dmtd_channel {
int64_t base;
int64_t prev_tag, phase;
int64_t period;
};
#define TAG_BITS 23
static void init_dmtd(struct dmtd_channel *ch, int64_t period)
{
ch->period = period;
ch->prev_tag = -1;
ch->base = 0;
}
static int read_dmtd(fdelay_device_t *dev, struct dmtd_channel *ch, int is_out)
{
fd_decl_private(dev)
uint32_t addr = (is_out ? FD_REG_DMTR_IN : FD_REG_DMTR_OUT);
uint32_t value = fd_readl(addr);
if(value & FD_DMTR_IN_RDY)
{
int64_t tag = (int64_t) (value & ((1<<TAG_BITS) - 1)) + ch->base;
if(ch->prev_tag >= 0 && tag < ch->prev_tag) /* DMTD tag counter has 23 bits. We need to unwrap it */
{
ch->base += (1LL<<TAG_BITS);
tag += (1LL<<TAG_BITS);
}
int64_t epoch = (tag / DMTD_OUTPUT_PERIOD) * DMTD_OUTPUT_PERIOD; /* calculate the offset between the beginning of DDMTD cycle and the current tag */
ch->phase = tag - epoch;
ch->prev_tag = tag;
return 1;
}
return 0;
}
int calibrate_channel(fdelay_device_t *dev, int channel, double *mean, double *std)
{
int64_t samples_in[DMTD_N_AVGS], samples_out[DMTD_N_AVGS], delta[DMTD_N_AVGS];
struct dmtd_channel ch_in, ch_out;
int i, n_in = 0, n_out = 0;
fd_decl_private(dev);
fdelay_configure_trigger(dev, 0, 0);
fd_writel(FD_CALR_PSEL_W(0), FD_REG_CALR);
/* Configure the output to introduce DELAY_SETPOINT delay (but with fixed offset set to 0)*/
};
#define TAG_BITS 23
static void init_dmtd(struct dmtd_channel *ch, int64_t period)
{
ch->period = period;
ch->prev_tag = -1;
ch->base = 0;
}
static int read_dmtd(fdelay_device_t *dev, struct dmtd_channel *ch, int is_out)
{
fd_decl_private(dev)
uint32_t addr = (is_out ? FD_REG_DMTR_OUT : FD_REG_DMTR_IN);
uint32_t value = fd_readl(addr);
if(value & FD_DMTR_IN_RDY)
{
int64_t tag = (int64_t) (value & ((1<<TAG_BITS) - 1)) + ch->base;
if(ch->prev_tag >= 0 && tag < ch->prev_tag) /* DMTD tag counter has 23 bits. We need to unwrap it */
{
ch->base += (1LL<<TAG_BITS);
tag += (1LL<<TAG_BITS);
}
int64_t epoch = (tag / DMTD_OUTPUT_PERIOD) * DMTD_OUTPUT_PERIOD; /* calculate the offset between the beginning of DDMTD cycle and the current tag */
ch->phase = tag - epoch;
// if(!is_out)
// printf("i%d %lld %lld %lld tp %d\n", is_out, tag, tag-ch->prev_tag, ch->phase, tag % (16384 * 144));
//
ch->prev_tag = tag;
return 1;
}
return 0;
}
fdelay_time_t ts_sub(fdelay_time_t a, fdelay_time_t b);
void test_timestamper(fdelay_device_t *dev)
{
fdelay_time_t prev, t;
int i;
fdelay_configure_readout(dev, 0);
fdelay_configure_readout(dev, 1);
for(i=0;i<100;i++)
{
if (fdelay_read(dev, &t, 1) == 1)
{
if(i>0)
{
fdelay_time_t delta;
delta = ts_sub(t, prev);
// printf("t %lld:%d:%d prev %lld:%d:%d\n", t.utc, t.coarse, t.frac, prev.utc, prev.coarse, prev.frac);
printf("delta:%lld seq:%d\n", fdelay_to_picos(delta)/1000, t.seq_id);
}
prev = t;
i++;
}
}
}
int calibrate_channel(fdelay_device_t *dev, int channel, int setpoint_ps, double *mean, double *std)
{
int64_t samples_in[DMTD_N_AVGS], samples_out[DMTD_N_AVGS], delta[DMTD_N_AVGS];
struct dmtd_channel ch_in, ch_out;
int i, n_in = 0, n_out = 0;
fd_decl_private(dev);
fdelay_configure_trigger(dev, 0, 0);
fd_writel(FD_CALR_PSEL_W(0), FD_REG_CALR);
/* Configure the output to introduce DELAY_SETPOINT delay (but with fixed offset set to 0)*/
hw->calib.out_zero_offset[channel-1] = 0;
hw->calib.tdc_zero_offset = 0;
fdelay_configure_output(dev, channel, 1, (int64_t)DELAY_SETPOINT, 300000LL, 0LL, 1);
/* Disable ALL outputs to prevent the calibration pulses from driving whatever
is connected to the board */
if(sgpio_set_pin(dev, SGPIO_OUTPUT_EN(0), 0) < 0)
return -1;
if(sgpio_set_pin(dev, SGPIO_OUTPUT_EN(1), 0) < 0)
return -1;
if(sgpio_set_pin(dev, SGPIO_OUTPUT_EN(2), 0) < 0)
return -1;
if(sgpio_set_pin(dev, SGPIO_OUTPUT_EN(3), 0) < 0)
return -1;
/* Select internal trigger */
if(sgpio_set_pin(dev, SGPIO_TRIG_SEL, 0) < 0)
return -1;
for(i=1;i<=4;i++) /* disable all other channels */
if(i != channel)
fd_writel(0, i * 0x100 + FD_REG_DCR);
fd_readl(FD_REG_DMTR_IN);
fd_readl(FD_REG_DMTR_OUT);
fdelay_configure_trigger(dev, 1, 0);
fd_writel(FD_CALR_PSEL_W(0) | FD_CALR_CAL_DMTD, FD_REG_CALR);
init_dmtd(&ch_in, DMTD_OUTPUT_PERIOD);
init_dmtd(&ch_out, DMTD_OUTPUT_PERIOD);
/* Get DMTD_N_AVGS samples to reduce error */
n_in = n_out = 0;
int64_t ts = get_tics();
while((n_in < DMTD_N_AVGS || n_out < DMTD_N_AVGS) && ((get_tics()-ts) < 10000000))
{
if(read_dmtd(dev, &ch_in, 0))
{
if(n_in < DMTD_N_AVGS)
samples_in[n_in++] = ch_in.phase;
}
if(read_dmtd(dev, &ch_out, 1))
{
if(n_out < DMTD_N_AVGS)
samples_out[n_out++] = ch_out.phase;
}
}
if((get_tics()-ts) >= 10000000)
{
dbg("%s has timed out\n", __FUNCTION__);
return -1;
}
for(i=0;i<DMTD_N_AVGS;i++)
{
delta[i] = samples_out[i] - samples_in[i];
if(delta[i] < 0)
delta[i] += DMTD_OUTPUT_PERIOD;
// printf("in %lld out %lld delta %lld\n", samples_in[i], samples_out[i], delta[i]);
}
double avg = 0, s= 0;
for(i=0;i<DMTD_N_AVGS;i++)
avg+=(double) (delta[i]);
avg/=(double)DMTD_N_AVGS;
double scalefact = (double) (DMTD_PULSE_PERIOD * 16000 / 2) / (double)DMTD_OUTPUT_PERIOD;
*mean = avg * scalefact;
for(i=0;i<DMTD_N_AVGS;i++)
s+=((double)delta[i]-avg) * ((double)delta[i]-avg);
*std = sqrt(s / (double)(DMTD_N_AVGS-1)) * scalefact;
fd_writel(FD_CALR_PSEL_W(0), FD_REG_CALR);
/* Reset to normal trigger and disable input and output */
if(sgpio_set_pin(dev, SGPIO_TRIG_SEL, 1) < 0)
return -1;
fdelay_configure_trigger(dev, 0, 0);
return 0;
}
int fdelay_dmtd_calibration(fdelay_device_t *dev, const char *filename, uint32_t *userTimeout)
{
char resp[1024];
char c;
int i, error = 0;
fd_decl_private(dev);
int64_t base = 0, prev_tag = -1;
if(spec_load_lm32(dev->priv_io, filename, 0xc0000))
{
dbg("%s: Failed to load LM32 firmware\n", __FUNCTION__);
return -1;
}
//sleep(2);
/* Configure the WR core to produce a proper calibration clock: */
/* Disable PTP and enter free-running master mode */
if(wrc_shell_exec(dev, "ptp stop", resp) < 0)
return -1;
if(wrc_shell_exec(dev, "mode master", resp) < 0)
return -1;
/* And lock the DMTD oscillator to the FMC clock instead of the SPEC 125 MHz oscillator. Set the FMC VCO DAC to 0
to have some headroom */
if(wrc_shell_exec(dev, "pll sdac 1 0", resp) < 0)
return -1;
if(wrc_shell_exec(dev, "pll init 2 1 1", resp) < 0)
return -1;
/* Wait until the PLL locks... */
int64_t ts = get_tics();
hw->calib.tdc_zero_offset = 0;
dbg("calibrating channel : %d\n", channel);
fdelay_configure_output(dev, channel, 1, (int64_t)setpoint_ps, 300000LL, 0LL, 1);
/* Disable ALL outputs to prevent the calibration pulses from driving whatever
is connected to the board */
if(sgpio_set_pin(dev, SGPIO_OUTPUT_EN(0), 0) < 0)
return -1;
if(sgpio_set_pin(dev, SGPIO_OUTPUT_EN(1), 0) < 0)
return -1;
if(sgpio_set_pin(dev, SGPIO_OUTPUT_EN(2), 0) < 0)
return -1;
if(sgpio_set_pin(dev, SGPIO_OUTPUT_EN(3), 0) < 0)
return -1;
/* Select internal trigger */
if(sgpio_set_pin(dev, SGPIO_TRIG_SEL, 0) < 0)
return -1;
for(i=1;i<=4;i++) /* disable all other channels */
if(i != channel)
fd_writel(0, i * 0x100 + FD_REG_DCR);
fd_readl(FD_REG_DMTR_IN);
fd_readl(FD_REG_DMTR_OUT);
fdelay_configure_trigger(dev, 1, 0);
fd_writel(FD_CALR_PSEL_W(0) | FD_CALR_CAL_DMTD, FD_REG_CALR);
init_dmtd(&ch_in, DMTD_OUTPUT_PERIOD);
init_dmtd(&ch_out, DMTD_OUTPUT_PERIOD);
// test_timestamper(dev);
/* Get DMTD_N_AVGS samples to reduce error */
n_in = n_out = 0;
int64_t ts = get_tics();
while((n_in < DMTD_N_AVGS || n_out < DMTD_N_AVGS) && ((get_tics()-ts) < 10000000))
{
if(read_dmtd(dev, &ch_in, 0))
{
if(n_in < DMTD_N_AVGS)
samples_in[n_in++] = ch_in.phase;
}
if(read_dmtd(dev, &ch_out, 1))
{
if(n_out < DMTD_N_AVGS)
samples_out[n_out++] = ch_out.phase;
}
}
if((get_tics()-ts) >= 10000000)
{
dbg("%s has timed out\n", __FUNCTION__);
return -1;
}
for(i=0;i<DMTD_N_AVGS;i++)
{
delta[i] = samples_in[i] - samples_out[i];
if(delta[i] < 0)
delta[i] += DMTD_OUTPUT_PERIOD;
// printf("in %lld out %lld delta %lld\n", samples_in[i], samples_out[i], delta[i]);
}
double avg = 0, s= 0;
for(i=0;i<DMTD_N_AVGS;i++)
avg+=(double) (delta[i]);
avg/=(double)DMTD_N_AVGS;
double scalefact = (double) (DMTD_PULSE_PERIOD * 16000 / 2) / (double)DMTD_OUTPUT_PERIOD;
*mean = avg * scalefact;
for(i=0;i<DMTD_N_AVGS;i++)
s+=((double)delta[i]-avg) * ((double)delta[i]-avg);
*std = sqrt(s / (double)(DMTD_N_AVGS-1)) * scalefact;
fd_writel(FD_CALR_PSEL_W(0), FD_REG_CALR);
/* Reset to normal trigger and disable input and output */
if(sgpio_set_pin(dev, SGPIO_TRIG_SEL, 1) < 0)
return -1;
fdelay_configure_trigger(dev, 0, 0);
return 0;
}
int fdelay_dmtd_calibration(fdelay_device_t *dev, const char *filename, int setpoint, int *measured_delays)
{
char resp[1024];
int i, error = 0;
fd_decl_private(dev);
dbg("DDMTD Calibration start\n");
if(spec_load_lm32(dev->priv_io, filename, 0xc0000))
{
dbg("%s: Failed to load LM32 firmware\n", __FUNCTION__);
return -1;
}
//sleep(2);
/* Configure the WR core to produce a proper calibration clock: */
/* Disable PTP and enter free-running master mode */
if(wrc_shell_exec(dev, "ptp stop", resp) < 0)
return -1;
if(wrc_shell_exec(dev, "mode master", resp) < 0)
return -1;
/* And lock the DMTD oscillator to the FMC clock instead of the SPEC 125 MHz oscillator. Set the FMC VCO DAC to 0
to have some headroom */
if(wrc_shell_exec(dev, "pll sdac 1 0", resp) < 0)
return -1;
if(wrc_shell_exec(dev, "pll init 2 1 1", resp) < 0)
return -1;
/* Wait until the PLL locks... */
int64_t ts = get_tics();
while(1)
{
if(wrc_shell_exec(dev, "pll cl 0", resp) < 0)
return -1;
if(!strcmp(resp, "1"))
{
if(wrc_shell_exec(dev, "pll cl 0", resp) < 0)
return -1;
if(!strcmp(resp, "1"))
break;
if(get_tics() - ts > 10000000LL)
{
dbg("%s has timed out, the PLL doesn't lock\n", __FUNCTION__);
return -1;
}
sleep(1);
}
double mean_out[4], std_out[4];
usleep(500000);
for(i=1;i<=4;i++)
{
dbg("\n\nPerforming DDMTD-based calibration of channel %d\n", i);
if(calibrate_channel(dev, i, &mean_out[i-1], &std_out[i-1]) < 0)
{
dbg("Failed, DDMTD calibration failure for channel %d, DMTD Tag Ready flag is not coming high\n", i);
hw->calib.out_zero_offset[i-1] = 0;
error = 1;
}
else
{
dbg("Channel %d: delay %.0f ps, std %.0f ps.\n", i, mean_out[i-1], std_out[i-1]);
hw->calib.out_zero_offset[i-1] = mean_out[i-1] - DELAY_SETPOINT - hw->calib.tdc_zero_offset;
}
}
return error ? -1 : 0;
}
dbg("%s has timed out, the PLL doesn't lock\n", __FUNCTION__);
return -1;
}
sleep(1);
}
double mean_out[4], std_out[4];
usleep(500000);
for(i=1;i<=4;i++)
{
dbg("\n\nPerforming DDMTD-based calibration of channel %d\n", i);
if(calibrate_channel(dev, i, setpoint, &mean_out[i-1], &std_out[i-1]) < 0)
{
dbg("Failed, DDMTD calibration failure for channel %d, DMTD Tag Ready flag is not coming high\n", i);
measured_delays[i-1] = 0;
error = 1;
}
else
{
dbg("Channel %d: delay %.0f ps, std %.0f ps.\n", i, mean_out[i-1], std_out[i-1]);
measured_delays[i-1] = mean_out[i-1];
}
}
return error ? -1 : 0;
}
......@@ -1316,7 +1316,7 @@ fdelay_time_t fdelay_from_picos(const uint64_t ps)
}
/* Substract two timestamps */
static fdelay_time_t ts_sub(fdelay_time_t a, fdelay_time_t b)
fdelay_time_t ts_sub(fdelay_time_t a, fdelay_time_t b)
{
a.frac -= b.frac;
if(a.frac < 0)
......@@ -1330,6 +1330,7 @@ static fdelay_time_t ts_sub(fdelay_time_t a, fdelay_time_t b)
a.coarse += 125000000;
a.utc --;
}
a.utc-=b.utc;
return a;
}
......
......@@ -78,11 +78,13 @@ class FineDelay:
def write_calibration_eeprom(self):
self.return_value = self.fdelay.write_calibration_eeprom(self.handle)
def fdelay_dmtd_calibration(self):
def fdelay_dmtd_calibration(self, setpoint):
# last value is user timeout for spec_vuart
self.return_value = self.fdelay.fdelay_dmtd_calibration(self.handle, c_char_p(self.cwd + '/../firmware/wrc.bin'), pointer(c_uint32(3000000)))
self.dmtd = 1
delays = (c_int * 4)()
self.return_value = self.fdelay.fdelay_dmtd_calibration(self.handle, c_char_p(self.cwd + '/../firmware/wrc.bin'), c_int(setpoint), pointer(delays))
return delays
def write_eeprom_from_file(self, filename, offset):
self.return_value = self.fdelay.fdelay_write_eeprom_from_file(self.handle, c_char_p(filename), c_uint32(offset))
......
......@@ -21,11 +21,9 @@ def main (card=None, default_directory='.',suite=None):
# initialize card with long tests
card = init_card(new_stderr, default_directory, 2)
print("Dir %s" %default_directory)
# restore stderr and display debug information from file
restore_stderr(new_stderr, default_directory)
return card
if __name__ == '__main__' :
......
......@@ -198,6 +198,14 @@ def main (card=None, default_directory='.',suite=None):
if(error[3] != 0):
print("Output enable line for channel 4 is not working OK, check for soldering or connection problem.")
dmtd_setpoint = 750000
dmtd_ff_offset = 1000
dmtd_delays = card.fdelay_dmtd_calibration(dmtd_setpoint)
dmtd_err_threshold = 1000
calib = FDCalibBlock()
# calib.load(os.path.join(default_directory,"sdbfs/calibration"))
......@@ -210,6 +218,15 @@ def main (card=None, default_directory='.',suite=None):
calib.save(os.path.join(default_directory,"sdbfs/calibration"))
print("DDMTD vs CNT-91 cross-check: \n")
for i in range(0, 4):
err = dmtd_delays[i] + calib.tdc_zero_offset + calib.out_zero_offset[i] - dmtd_setpoint - dmtd_ff_offset
print("Channel %d : difference = %d ps, max = %d ps, status: %s " % (i+1, err, dmtd_err_threshold, "OK" if abs(err) < dmtd_err_threshold else "FAIL" ) )
if(err > dmtd_err_threshold):
raise PtsError("DDMTD vs CNT-91 Cross-check failed. Probably a soldering issue with DDMTD calibration flip-flops or multiplexer")
# redirect.InOut(red)
# restore stderr and display debug information from file
......
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