Commit 29f35be5 authored by Tomasz Wlostowski's avatar Tomasz Wlostowski

Added DDMTD calibration program, reorganized software directory structure, removed warnings

parent f911e540
*.o
fd_test
\ No newline at end of file
*.txt
*.log
*.pyc
spec_top.bin
spec_top_fd.bin
*.a
*.so
lib/spec
all:
make -C lib
make -C tests
clean:
make -C lib clean
make -C tests clean
\ No newline at end of file
......@@ -21,6 +21,10 @@
#define FDELAY_SYNC_LOCAL 0x1 /* use local oscillator */
#define FDELAY_SYNC_WR 0x2 /* use White Rabbit */
/* fdelay_init() flags */
#define FDELAY_RAW_READOUT 0x1
#define FDELAY_PERFORM_LONG_TESTS 0x2
/* Hardware "handle" structure */
typedef struct fdelay_device
{
......@@ -62,26 +66,22 @@ PUBLIC API
*/
/* Creates a local instance of Fine Delay Core at address base_addr. Returns a non-null fdelay_device_t
card context on success or null if an error occured */
fdelay_device_t *fdelay_create(const char *device);
/* Does the same as above, but for a card accessible via EtherBone/MiniBone.
iface = network interface to which the carrier is connected
mac_addr = MAC address of the EtherBone/MiniBone core
base_addr = base address of the FD core (relative to EB/MB address space) */
fdelay_device_t *fdelay_create_minibone(char *iface, char *mac_addr, uint32_t base_addr);
/* Creates a local instance of Fine Delay Core at address base_addr on the SPEC at bus/devfn. Returns 0 on success, negative on error. */
int spec_fdelay_create_bd(fdelay_device_t *dev, int bus, int dev_fn, uint32_t base);
/* A shortcut for test program, parsing the card base/location from command line args */
int spec_fdelay_create(fdelay_device_t *dev, int argc, char *argv[]);
/* Helper functions - converting FD timestamp format from/to plain picoseconds */
fdelay_time_t fdelay_from_picos(const uint64_t ps);
int64_t fdelay_to_picos(const fdelay_time_t t);
/* Enables/disables raw timestamp readout mode (debugging only) */
int fdelay_raw_readout(fdelay_device_t *dev, int raw_moide);
/* Initializes and calibrates the device. 0 = success, negative = error */
int fdelay_init(fdelay_device_t *dev);
int fdelay_init(fdelay_device_t *dev, int init_flags);
/* Disables and releases the resources for a given FD Card */
int fdelay_release(fdelay_device_t *dev);
......@@ -133,6 +133,6 @@ 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);
int fdelay_dmtd_calibration(fdelay_device_t *dev, double *offsets);
#endif
......@@ -86,6 +86,7 @@ struct fine_delay_hw
int wr_enabled;
int wr_state;
int raw_mode;
int do_long_tests;
struct fine_delay_calibration calib;
int64_t input_user_offset, output_user_offset;
};
......
CFLAGS = -I../include -g -Imini_bone -Ispll
#uncomment for extra tests (DAC, output stage INL/DNL)
#CFLAGS += -DPERFORM_LONG_TESTS
OBJS = fdelay_lib.o i2c_master.o onewire.o spec_common.o spec/speclib.o fdelay_dmtd_calibration.o
OBJS_LIB = fdelay_lib.o i2c_master.o onewire.o mini_bone/minibone_lib.o mini_bone/ptpd_netif.o spec_common.o spec/speclib.o fdelay_dmtd_calibration.o
CFLAGS = -I../include -g -Imini_bone
all: testprog lib gs rp-test sweep-test
ifeq ($(SPEC_SW),)
throw_error:
@echo "SPEC software package location environment variable is not set. Can't compile :("
endif
lib: $(OBJS_LIB)
gcc -shared -o libfinedelay.so $(OBJS_LIB)
all: spec lib
testprog: lib fdelay_test.o
gcc -o fdelay_test $(OBJS_LIB) fdelay_test.o -lm
spec:
ln -s $(SPEC_SW)/tools spec
gs: lib fdelay-gs.o
gcc -o fdelay-gs $(OBJS_LIB) fdelay-gs.o -lm
rp-test: lib random_pulse_test.o
gcc -o fdelay-random-test $(OBJS_LIB) random_pulse_test.o -lm
sweep-test: lib sweep_test.o
gcc -o fdelay-sweep-test $(OBJS_LIB) sweep_test.o -lm
lib: $(OBJS)
gcc -shared -o libfinedelay.so $(OBJS)
ar rc libfinedelay.a $(OBJS)
clean:
rm -f *.o fdelay-random-test fdelay-gs fdelay_pps_demo fdelay_test
\ No newline at end of file
rm -f *.o libfinedelay.so
/*
FmcDelay1ns4Cha (a.k.a. The Fine Delay Card)
User-space driver/library - bus API creation functions
Tomasz Włostowski/BE-CO-HT, 2011
(c) Copyright CERN 2011
Licensed under LGPL 2.1
*/
#include <stdio.h>
#include <stdlib.h>
#include "rr_io.h"
#include "minibone_lib.h"
#include "fdelay_lib.h"
static void my_rr_writel(void *priv, uint32_t data, uint32_t addr)
{
rr_writel(data, addr);
}
static uint32_t my_rr_readl(void *priv, uint32_t addr)
{
uint32_t d = rr_readl(addr);
return d;
}
static void my_mb_writel(void *priv, uint32_t data, uint32_t addr)
{
mbn_writel(priv, data, addr >> 2);
}
static uint32_t my_mb_readl(void *priv, uint32_t addr)
{
uint32_t d = mbn_readl(priv, addr >> 2);
return d;
}
fdelay_device_t *fdelay_create_rawrabbit(int fd, uint32_t base_addr)
{
fdelay_device_t *dev = malloc(sizeof(fdelay_device_t));
rr_bind(fd);
dev->writel = my_rr_writel;
dev->readl = my_rr_readl;
dev->base_addr = base_addr;
return dev;
}
fdelay_device_t *fdelay_create_minibone(char *iface, char *mac_addr, uint32_t base_addr)
{
void *handle;
uint8_t target_mac[6];
fdelay_device_t *dev = malloc(sizeof(fdelay_device_t));
sscanf(mac_addr, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", &target_mac[0], &target_mac[1], &target_mac[2], &target_mac[3], &target_mac[4], &target_mac[5]);
handle = mbn_open(iface, target_mac);
if(handle == NULL)
return NULL;
// dbg("%s: remote @ %s [%02x:%02x:%02x:%02x:%02x:%02x], base 0x%08x\n",__FUNCTION__, iface,
// target_mac[0], target_mac[1], target_mac[2], target_mac[3], target_mac[4], target_mac[5], base_addr);
dev->writel = my_mb_writel;
dev->readl = my_mb_readl;
dev->base_addr = base_addr;
dev->priv_io = handle;
return dev;
}
int fdelay_load_firmware(const char *path)
{
fprintf(stderr,"Booting up the FPGA with %s.\n", path);
if(rr_load_bitstream_from_file(path) < 0)
{
fprintf(stderr,"Failed to load FPGA bitstream.\n");
return -1;
}
return 0;
}
\ No newline at end of file
/*
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"
#include "spec/speclib.h"
extern void dbg(const char *fmt, ...);
extern int64_t get_tics();
extern void udelay(uint32_t usecs);
/* WR core shell communication functions */
/* Waits for a WR Core and returns the output of the previous command in rv */
static void wait_prompt(fdelay_device_t *dev, char *rv)
{
char buf[16384],c;
int pos = 0;
memset(buf, 0, sizeof(buf));
while(pos < sizeof(buf))
{
if(spec_vuart_rx(dev->priv_io, &c, 1) == 1)
{
buf[pos++] = c;
if(pos >= 5 && !strcmp(buf + pos - 5, "wrc# "))
{
int old_pos;
if(!rv)
return;
while(pos>0) if(buf[pos]=='\n'||buf[pos]=='\r')
break;
else
pos--;
pos-=2;
old_pos = pos;
while(pos>0) if(buf[pos]=='\n'||buf[pos]=='\r')
break;
else
pos--;
strncpy(rv, buf+pos+1, old_pos - pos);
rv[old_pos-pos]=0;
return;
}
}
}
dbg("Failure: shell buffer overflow.\n");
exit(1);
}
/* executes a shell command on the associated WR core */
static int wrc_shell_exec(fdelay_device_t *dev, const char *cmd, char *retval)
{
char c;
while(spec_vuart_rx(dev->priv_io, &c, 1) == 1);
spec_vuart_tx(dev->priv_io, (char *)cmd, strlen(cmd));
spec_vuart_tx(dev->priv_io, "\r", 1);
wait_prompt(dev, retval);
if(retval)
dbg("wr_core exec '%s', retval: '%s'\n", cmd, retval);
return 0;
}
#define DMTD_N_AVGS 10 /* number of average samples */
#define DELAY_SETPOINT 500000 /* 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;
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;
}
void 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)*/\
hw->calib.zero_offset[channel-1] = 0;
fdelay_configure_output(dev, channel, 1, (int64_t)DELAY_SETPOINT, 200000LL, 0LL, 1);
/* Disable ALL outputs to prevent the calibration pulses from driving whatever
is connected to the board */
sgpio_set_pin(dev, SGPIO_OUTPUT_EN(0), 0);
sgpio_set_pin(dev, SGPIO_OUTPUT_EN(1), 0);
sgpio_set_pin(dev, SGPIO_OUTPUT_EN(2), 0);
sgpio_set_pin(dev, SGPIO_OUTPUT_EN(3), 0);
/* Select internal trigger */
sgpio_set_pin(dev, SGPIO_TRIG_SEL, 0);
for(i=1;i<=4;i++) /* disable all other channels */
if(channel != i)
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);
n_in = n_out = 0;
while(n_in < DMTD_N_AVGS || n_out < DMTD_N_AVGS) /* Get DMTD_N_AVGS samples to reduce error */
{
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;
}
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;
}
int fdelay_dmtd_calibration(fdelay_device_t *dev, double *offsets)
{
char resp[1024];
char c;
int i;
fd_decl_private(dev)
int64_t base = 0, prev_tag = -1;
if(spec_load_lm32(dev->priv_io, "wrc.bin", 0xc0000))
{
dbg("Failed to load LM32 firmware\n");
return -1;
}
// sleep(2);
/* Configure the WR core to produce a proper calibration clock: */
/* Disable PTP and enter free-running master mode */
wrc_shell_exec(dev, "ptp stop", resp);
wrc_shell_exec(dev, "mode master", resp);
/* 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 */
wrc_shell_exec(dev, "pll sdac 1 0", resp);
wrc_shell_exec(dev, "pll init 2 1 1", resp);
/* Wait until the PLL locks... */
while(1)
{
wrc_shell_exec(dev, "pll cl 0", resp);
if(!strcmp(resp, "1"))
break;
sleep(1);
}
double mean_out[4], std_out[4];
usleep(500000);
dbg("\n\nPerforming DDMTD delay calibration: \n");
for(i=1;i<=4;i++)
{
calibrate_channel(dev, i, &mean_out[i-1], &std_out[i-1]);
dbg("Channel %d: delay %.0f ps, std %.0f ps.\n", i, mean_out[i-1], std_out[i-1]);
}
return 0;
}
#include <stdio.h>
#include "fdelay_lib.h"
#include "fdelay_private.h"
#include "rr_io.h"
#include "i2c_master.h"
void my_writel(void *priv, uint32_t data, uint32_t addr)
{
rr_writel(data, addr);
}
uint32_t my_readl(void *priv, uint32_t addr)
{
uint32_t d = rr_readl(addr);
return d;
}
main()
{
fdelay_device_t dev;
struct fine_delay_calibration cal;
rr_init(RR_DEVSEL_UNUSED, RR_DEVSEL_UNUSED);
dev.writel = my_writel;
dev.readl = my_readl;
dev.base_addr = 0x84000;
if(fdelay_init(&dev) < 0)
return -1;
cal.magic = 0xf19ede1a;
cal.zero_offset[0] = 63000;
cal.zero_offset[1] = 63000;
cal.zero_offset[2] = 63000;
cal.zero_offset[3] = 63000;
cal.tdc_zero_offset = 35600;
cal.frr_poly[0] = -165202LL;
cal.frr_poly[1] = -29825595LL;
cal.frr_poly[2] = 3801939743082LL;
cal.tdc_zero_offset = 35600;
cal.atmcr_val = 2 | (1000 << 4);
cal.adsfr_val = 56648;
cal.acam_start_offset = 10000;
printf("Writing EEPROM...");
eeprom_write(&dev, EEPROM_ADDR, 0, &cal, sizeof(struct fine_delay_calibration));
printf(" done.\n");
}
......@@ -763,10 +763,8 @@ int calibrate_outputs(fdelay_device_t *dev)
fd_decl_private(dev)
int i, channel, temp;
#ifdef PERFORM_LONG_TESTS
if(test_delay_transfer_function(dev) < 0)
if(hw->do_long_tests && test_delay_transfer_function(dev) < 0)
return -1;
#endif
fd_writel( FD_GCR_BYPASS, FD_REG_GCR);
acam_configure(dev, ACAM_IMODE);
......@@ -856,7 +854,7 @@ static int read_calibration_eeprom(fdelay_device_t *dev, struct fine_delay_calib
*/
/* Initialize & self-calibrate the Fine Delay card */
int fdelay_init(fdelay_device_t *dev)
int fdelay_init(fdelay_device_t *dev, int init_flags)
{
int i, rv;
struct fine_delay_hw *hw;
......@@ -869,7 +867,8 @@ int fdelay_init(fdelay_device_t *dev)
dev->priv_fd = (void *) hw;
hw->raw_mode = 0;
hw->raw_mode = init_flags & FDELAY_RAW_READOUT ? 1 : 0;
hw->do_long_tests = init_flags & FDELAY_PERFORM_LONG_TESTS ? 1 : 0;
hw->base_addr = dev->base_addr;
hw->base_i2c = 0x100;
hw->base_onewire = dev->base_addr + 0x500;
......@@ -970,10 +969,8 @@ int fdelay_init(fdelay_device_t *dev)
fd_writel( FD_GCR_BYPASS, FD_REG_GCR);
#ifdef PERFORM_LONG_TESTS
if(test_pll_dac(dev) < 0)
if(hw->do_long_tests && test_pll_dac(dev) < 0)
return -1;
#endif
/* Test if ACAM addr/data lines are OK */
if(acam_test_bus(dev) < 0)
......@@ -1026,10 +1023,10 @@ int fdelay_configure_trigger(fdelay_device_t *dev, int enable, int termination)
if(termination)
{
dbg("%s: 50-ohm terminated mode\n", __FUNCTION__);
// dbg("%s: 50-ohm terminated mode\n", __FUNCTION__);
sgpio_set_pin(dev,SGPIO_TERM_EN,1);
} else {
dbg("%s: high impedance mode\n", __FUNCTION__);
// dbg("%s: high impedance mode\n", __FUNCTION__);
sgpio_set_pin(dev,SGPIO_TERM_EN,0);
};
......
#include <stdio.h>
#include "fdelay_lib.h"
int spec_fdelay_init(int argc, char *argv[], fdelay_device_t *dev);
main(int argc, char *argv[])
{
fdelay_device_t dev;
fdelay_time_t t_cur, t_start;
if(spec_fdelay_init(&dev, 5, 0) < 0)
return -1;
// Get the current time of the FD core - and program the card to start producing the PPS and 10 MHz one second later */
t_cur.utc = 0;
t_cur.coarse = 0;
fdelay_configure_sync(&dev, FDELAY_SYNC_LOCAL);
fdelay_set_time(&dev, t_cur);
printf("Current Time: %ld:%d\n", t_cur.utc, t_cur.coarse);
fdelay_get_time(&dev, &t_cur);
printf("Current Time: %ld:%d\n", t_cur.utc, t_cur.coarse);
t_start.coarse = 0;//t_cur.coarse;
t_start.utc = t_cur.utc + 3;
t_start.frac = 0;
fdelay_configure_pulse_gen(&dev, 1, 1, t_start, 48000LL, 100000LL, -1); /* Output 1, period = 100 ns, width = 48 ns - a bit asymmetric 10 MHz */
fdelay_configure_pulse_gen(&dev, 2, 1, t_start, 1000000000000LL/2LL, 1000000000000LL, -1); /* Output 2: period = 1 second, width = 48 ns - PPS signal */
while(!fdelay_channel_triggered(&dev, 1) || !fdelay_channel_triggered(&dev, 2))
usleep(10000); /* wait until both outputs have triggered*/;
return 0;
}
#include <stdio.h>
#include "fdelay_lib.h"
#include "rr_io.h"
void my_writel(void *priv, uint32_t data, uint32_t addr)
{
rr_writel(data, addr);
}
uint32_t my_readl(void *priv, uint32_t addr)
{
uint32_t d = rr_readl(addr);
return d;
}
main()
{
fdelay_device_t dev;
rr_init();
dev.writel = my_writel;
dev.readl = my_readl;
dev.base_addr = 0x84000;
if(fdelay_init(&dev) < 0)
return -1;
fdelay_configure_trigger(&dev, 1,1);
fdelay_configure_output(&dev,1,1,500000, 100000, 100000, 0);
fdelay_configure_output(&dev,2,1,500000, 100000, 100000, 0);
fdelay_configure_output(&dev,3,1,500000, 100000, 100000, 0);
fdelay_configure_output(&dev,4,1,500000, 100000, 100000, 0);
fdelay_configure_readout(&dev, 1);
// fd_update_spll(&dev);
int64_t prev = 0, dp, pmin=10000000000LL,pmax=0;
#if 0
for(;;)
{
fdelay_time_t ts;
if(fdelay_read(&dev, &ts, 1) == 1)
{
int64_t ts_p = fdelay_to_picos(ts), d;
d=ts_p - prev;
if(prev > 0)
{
if(d<pmin) pmin=d;
if(d>pmax) pmax=d;
fprintf(stderr,"Got it %lld:%d:%d delta %lld span %lld\n", ts.utc, ts.coarse, ts.frac, d, pmax-pmin);
}
prev = ts_p;
}
}
#endif
}
#include <stdio.h>
#include "fdelay_lib.h"
#include "rr_io.h"
extern int spec_fdelay_init(fdelay_device_t *dev, int pbus, int pdev);
main(int argc, char *argv[])
{
fdelay_device_t dev;
fdelay_time_t t;
/* Initialize the fine delay generator */
if(spec_fdelay_init(&dev, 0x05, 0x00) < 0)
{
// fdelay_show_test_results();
return -1;
}
return 0;
/* Enable trigger input and 50 ohm termination */
/* Enable all out