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 outputs and set them to 500 ns delay, 100 ns pulse width, single output pulse per trigger */
/* fdelay_configure_output(&dev,1,1,500000, 100000, 100000, 1);
fdelay_configure_output(&dev,2,1,500000, 100000, 100000, 1);
fdelay_configure_output(&dev,3,1,500000, 100000, 100000, 1);
fdelay_configure_output(&dev,4,1,500000, 100000, 100000, 1);*/
t.utc = 0;
t.coarse = 0;
fdelay_set_time(&dev, t);
fdelay_configure_sync(&dev, FDELAY_SYNC_WR);
fprintf(stderr, "Syncing with WR Timebase...\n");
while(!fdelay_check_sync(&dev))
fprintf(stderr, ".");
fprintf(stderr, " locked!\n");
fdelay_configure_trigger(&dev, 1, 0);
fdelay_configure_readout(&dev, 1);//int enable)
int seq_prev = 0;
int64_t t_prev = 0;
#if 1
for(;;)
{
fdelay_time_t ts;
int64_t ts_i;
if(fdelay_read(&dev, &ts, 1) == 1)
{
ts_i = fdelay_to_picos(ts);
fprintf(stderr,"ts = %-20lld ps, delta = %-20lld, seq = %-6d seq_delta=%-6d\n",
ts_i, ts_i-t_prev, ts.seq_id, ts.seq_id-seq_prev);
seq_prev = ts.seq_id;
t_prev= ts_i;
}
}
#endif
// fdelay_
for(;;)
{
}
}
OBJS = minibone_lib.o ptpd_netif.o
all: $(OBJS)
gcc -o m $(OBJS)
\ No newline at end of file
/* MiniBone library. BUGGY CRAP CODE INTENDED FOR TESTING ONLY! */
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <sys/socket.h>
#include <sys/time.h>
#include "ptpd_netif.h"
#define F_SEL(x) (x & 0xf)
#define F_ERROR (1<<1)
#define F_READBACK (1<<0)
#define F_WRITE (1<<4)
#define RX_TIMEOUT 10
#define MBN_ETHERTYPE 0xa0a0
struct mbn_packet {
uint16_t flags ;
uint32_t a_d;
uint32_t d;
} __attribute__((packed));
struct mb_device {
mac_addr_t dest;
uint16_t ethertype;
wr_socket_t *sock;
int tx_packets, rx_packets, tx_retries, rx_retries;
};
typedef struct
{
uint64_t start_tics;
uint64_t timeout;
} timeout_t ;
static uint64_t get_tics()
{
struct timezone tz = {0, 0};
struct timeval tv;
gettimeofday(&tv, &tz);
return (uint64_t) tv.tv_sec * 1000000ULL + (uint64_t) tv.tv_usec;
}
static inline int tmo_init(timeout_t *tmo, uint32_t milliseconds)
{
tmo->start_tics = get_tics();
tmo->timeout = (uint64_t) milliseconds * 1000ULL;
return 0;
}
static inline int tmo_restart(timeout_t *tmo)
{
tmo->start_tics = get_tics();
return 0;
}
static inline int tmo_expired(timeout_t *tmo)
{
return (get_tics() - tmo->start_tics > tmo->timeout);
}
void *mbn_open(const char *if_name, mac_addr_t target)
{
struct mb_device *dev = malloc(sizeof(struct mb_device));
wr_sockaddr_t saddr;
if(!dev)
return NULL;
memset(dev, 0, sizeof(struct mb_device));
memcpy(dev->dest, target, 6);
strcpy(saddr.if_name, if_name);
memcpy(saddr.mac, target, 6);
saddr.ethertype = htons(MBN_ETHERTYPE);
saddr.family = PTPD_SOCK_RAW_ETHERNET;
dev->sock = ptpd_netif_create_socket(PTPD_SOCK_RAW_ETHERNET, 0, &saddr);
if(!dev->sock)
{
free(dev);
return NULL;
}
return (void *)dev;
}
static int mbn_send(void *priv, uint8_t *data, int size)
{
struct mb_device *dev = (struct mb_device *)priv;
wr_sockaddr_t to;
memcpy(to.mac, dev->dest, 6);
to.ethertype = MBN_ETHERTYPE;
return ptpd_netif_sendto(dev->sock, &to, (void*)data, size, NULL);
}
static int mbn_recv(void *handle, uint8_t *data, int size, int timeout)
{
struct mb_device *dev = (struct mb_device *)handle;
wr_sockaddr_t from;
timeout_t rx_tmo;
tmo_init(&rx_tmo, timeout);
do {
int n = ptpd_netif_recvfrom(dev->sock, &from, (void*)data, size, NULL);
if(n > 0 && from.ethertype == MBN_ETHERTYPE && !memcmp(from.mac, dev->dest, 6))
{
dev->rx_packets++;
return n;
}
// dev->rx_retries++;
} while(!tmo_expired(&rx_tmo));
return 0;
}
void mbn_writel(void *handle, uint32_t d, uint32_t a)
{
struct mb_device *dev = (struct mb_device *)handle;
int n_retries = 3;
struct mbn_packet pkt;
while(n_retries--)
{
pkt.flags = htons(F_SEL(0xf) | F_WRITE);
pkt.a_d= htonl(a);
pkt.d=htonl(d);
mbn_send(handle, (uint8_t *)&pkt, sizeof(pkt));
int n = mbn_recv(handle, (uint8_t *)&pkt, sizeof(pkt), RX_TIMEOUT);
pkt.flags = ntohs(pkt.flags);
if(n == sizeof(pkt) && ! (!(pkt.flags && F_READBACK) && !(pkt.flags & F_ERROR)))
{
int i;
fprintf(stderr,"\nBadPacket: ");
for(i=0;i<n; i++) fprintf(stderr,"%02x ", *(uint8_t*) (&pkt + i));
fprintf(stderr,"\n");
} if(n == sizeof(pkt) && !(pkt.flags && F_READBACK) && !(pkt.flags & F_ERROR))
{
int i;
// fprintf(stderr,"GoodFlags: %x\n", pkt.flags);
/*fprintf(stderr,"\nGoodPacket: ");
for(i=0;i<n; i++) fprintf(stderr,"%02x ", *(uint8_t*) (&pkt + i));
fprintf(stderr,"\n");*/
dev->tx_packets++;
return ;
}
dev->tx_retries++;
}
fprintf(stderr, "No ack.\n");
}
uint32_t mbn_readl(void *handle, uint32_t a)
{
int n_retries = 3;
struct mb_device *dev = (struct mb_device *)handle;
struct mbn_packet pkt;
pkt.flags = htons(F_SEL(0xf));
pkt.a_d= htonl(a);
while(n_retries--)
{
mbn_send(handle, (uint8_t *)&pkt, sizeof(pkt));
int n = mbn_recv(handle, (uint8_t *)&pkt, sizeof(pkt), RX_TIMEOUT);
pkt.flags = ntohs(pkt.flags);
if(n == sizeof(pkt) && (pkt.flags & F_READBACK) && !(pkt.flags & F_ERROR))
{
return ntohl(pkt.a_d);
}
dev->tx_retries++;
}
fprintf(stderr, "No ack.\n");
}
void mbn_stats(void *handle)
{
struct mb_device *dev = (struct mb_device *)handle;
fprintf(stderr,"Sent: %d [retries: %d], rcvd: %d [retries: %d]\n", dev->tx_packets, dev->tx_retries, dev->rx_packets, dev->rx_retries);
}
void mbn_close(void *handle)
{
struct mb_device *dev = (struct mb_device *)handle;
ptpd_netif_close_socket(dev->sock);
}
\ No newline at end of file
/* MiniBone library. BUGGY CRAP CODE INTENDED FOR TESTING ONLY! */
#ifndef __MINIBONE_LIB_H
#define __MINIBONE_LIB_H
#include <stdint.h>
void *mbn_open(const char *if_name, uint8_t target_mac[]);
void mbn_writel(void *handle, uint32_t d, uint32_t a);
uint32_t mbn_readl(void *handle, uint32_t a);
void mbn_close(void *handle);
#endif
// Supports only raw ethernet now.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#include <linux/if_arp.h>
#include <linux/errqueue.h>
#include <linux/sockios.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <asm/types.h>
#include <fcntl.h>
#include <errno.h>
#include <asm/socket.h>
#include "ptpd_netif.h"
#ifdef NETIF_VERBOSE
#define netif_dbg(...) printf(__VA_ARGS__)
#else
#define netif_dbg(...)
#endif
#define ETHER_MTU 1518
#define DMTD_UPDATE_INTERVAL 100
struct scm_timestamping {
struct timespec systime;
struct timespec hwtimetrans;
struct timespec hwtimeraw;
};
PACKED struct etherpacket {
struct ethhdr ether;
char data[ETHER_MTU];
};
struct tx_timestamp {
int valid;
wr_timestamp_t ts;
uint32_t tag;
uint64_t t_acq;
};
struct my_socket {
int fd;
wr_sockaddr_t bind_addr;
mac_addr_t local_mac;
int if_index;
// parameters for linearization of RX timestamps
uint32_t clock_period;
uint32_t phase_transition;
uint32_t dmtd_phase;
};
struct nasty_hack{
char if_name[20];
int clockedAsPrimary;
};
#ifdef MACIEK_HACKs
struct nasty_hack locking_hack;
#endif
wr_socket_t *ptpd_netif_create_socket(int sock_type, int flags,
wr_sockaddr_t *bind_addr)
{
struct my_socket *s;
struct sockaddr_ll sll;
struct ifreq f;
int fd;
// fprintf(stderr,"CreateSocket!\n");
if(sock_type != PTPD_SOCK_RAW_ETHERNET)
return NULL;
fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if(fd < 0)
{
perror("socket()");
return NULL;
}
fcntl(fd, F_SETFL, O_NONBLOCK);
// Put the controller in promiscious mode, so it receives everything
strcpy(f.ifr_name, bind_addr->if_name);
if(ioctl(fd, SIOCGIFFLAGS,&f) < 0) { perror("ioctl()"); return NULL; }
f.ifr_flags |= IFF_PROMISC;
if(ioctl(fd, SIOCSIFFLAGS,&f) < 0) { perror("ioctl()"); return NULL; }
// Find the inteface index
strcpy(f.ifr_name, bind_addr->if_name);
ioctl(fd, SIOCGIFINDEX, &f);
sll.sll_ifindex = f.ifr_ifindex;
sll.sll_family = AF_PACKET;
sll.sll_protocol = htons(bind_addr->ethertype);
sll.sll_halen = 6;
memcpy(sll.sll_addr, bind_addr->mac, 6);
if(bind(fd, (struct sockaddr *)&sll, sizeof(struct sockaddr_ll)) < 0)
{
close(fd);
perror("bind()");
return NULL;
}
s=calloc(sizeof(struct my_socket), 1);
s->if_index = f.ifr_ifindex;
// get interface MAC address
if (ioctl(fd, SIOCGIFHWADDR, &f) < 0) {
perror("ioctl()"); return NULL;
}
memcpy(s->local_mac, f.ifr_hwaddr.sa_data, 6);
memcpy(&s->bind_addr, bind_addr, sizeof(wr_sockaddr_t));
s->fd = fd;
return (wr_socket_t*)s;
}
int ptpd_netif_close_socket(wr_socket_t *sock)
{
struct my_socket *s = (struct my_socket *) sock;
if(!s)
return 0;
close(s->fd);
return 0;
}
int ptpd_netif_sendto(wr_socket_t *sock, wr_sockaddr_t *to, void *data,
size_t data_length, wr_timestamp_t *tx_ts)
{
struct etherpacket pkt;
struct my_socket *s = (struct my_socket *)sock;
struct sockaddr_ll sll;
int rval;
wr_timestamp_t ts;
if(s->bind_addr.family != PTPD_SOCK_RAW_ETHERNET)
return -ENOTSUP;
if(data_length > ETHER_MTU-8) return -EINVAL;
memset(&pkt, 0, sizeof(struct etherpacket));
memcpy(pkt.ether.h_dest, to->mac, 6);
memcpy(pkt.ether.h_source, s->local_mac, 6);
pkt.ether.h_proto =htons(to->ethertype);
memcpy(pkt.data, data, data_length);
size_t len = data_length + sizeof(struct ethhdr);
if(len < 72)
len = 72;
memset(&sll, 0, sizeof(struct sockaddr_ll));
sll.sll_ifindex = s->if_index;
sll.sll_family = AF_PACKET;
sll.sll_protocol = htons(to->ethertype);
sll.sll_halen = 6;
// fprintf(stderr,"fd %d ifi %d ethertype %d\n", s->fd,
// s->if_index, to->ethertype);
rval = sendto(s->fd, &pkt, len, 0, (struct sockaddr *)&sll,
sizeof(struct sockaddr_ll));
return rval;
}
int ptpd_netif_recvfrom(wr_socket_t *sock, wr_sockaddr_t *from, void *data,
size_t data_length, wr_timestamp_t *rx_timestamp)
{
struct my_socket *s = (struct my_socket *)sock;
struct etherpacket pkt;
struct msghdr msg;
struct iovec entry;
struct sockaddr_ll from_addr;
struct {
struct cmsghdr cm;
char control[1024];
} control;
struct cmsghdr *cmsg;
struct scm_timestamping *sts = NULL;
size_t len = data_length + sizeof(struct ethhdr);
memset(&msg, 0, sizeof(msg));
msg.msg_iov = &entry;
msg.msg_iovlen = 1;
entry.iov_base = &pkt;
entry.iov_len = len;
msg.msg_name = (caddr_t)&from_addr;
msg.msg_namelen = sizeof(from_addr);
msg.msg_control = &control;
msg.msg_controllen = sizeof(control);
int ret = recvmsg(s->fd, &msg, MSG_DONTWAIT);
if(ret < 0 && errno==EAGAIN) return 0; // would be blocking
if(ret == -EAGAIN) return 0;
if(ret <= 0) return ret;
memcpy(data, pkt.data, ret - sizeof(struct ethhdr));
from->ethertype = ntohs(pkt.ether.h_proto);
memcpy(from->mac, pkt.ether.h_source, 6);
memcpy(from->mac_dest, pkt.ether.h_dest, 6);
return ret - sizeof(struct ethhdr);
}
// Network API for WR-PTPd
#ifndef __PTPD_NETIF_H
#define __PTPD_NETIF_H
#include <stdio.h>
//#include <inttypes.h>
#define PTPD_SOCK_RAW_ETHERNET 1
#define PTPD_SOCK_UDP 2
#define PTPD_FLAGS_MULTICAST 0x1
// error codes (to be extended)
#define PTPD_NETIF_READY 1
#define PTPD_NETIF_OK 0
#define PTPD_NETIF_ERROR -1
#define PTPD_NETIF_NOT_READY -2
#define PTPD_NETIF_NOT_FOUND -3
// GCC-specific
#define PACKED __attribute__((packed))
#define PHYS_PORT_ANY (0xffff)
#define PTPD_NETIF_TX 1
#define PTPD_NETIF_RX 2
#define IFACE_NAME_LEN 16
#define SLAVE_PRIORITY_0 0
#define SLAVE_PRIORITY_1 1
#define SLAVE_PRIORITY_2 2
#define SLAVE_PRIORITY_3 3
#define SLAVE_PRIORITY_4 4
// Some system-independent definitions
typedef uint8_t mac_addr_t[6];
typedef uint32_t ipv4_addr_t;
// WhiteRabbit socket - it's void pointer as the real socket structure is private and probably platform-specific.
typedef void *wr_socket_t;
// Socket address for ptp_netif_ functions
typedef struct {
// Network interface name (eth0, ...)
char if_name[IFACE_NAME_LEN];
// Socket family (RAW ethernet/UDP)
int family;
// MAC address
mac_addr_t mac;
// Destination MASC address, filled by recvfrom() function on interfaces bound to multiple addresses
mac_addr_t mac_dest;
// IP address
ipv4_addr_t ip;
// UDP port
uint16_t port;
// RAW ethertype
uint16_t ethertype;
// physical port to bind socket to
uint16_t physical_port;
} wr_sockaddr_t;
typedef struct {
uint32_t v[4];
} wr_picoseconds_t;
// Precise WhiteRabbit timestamp
// TS[picoseconds] = utc * 1e12 + nsec * 1e3 + phase;
PACKED struct _wr_timestamp {
// UTC time value (seconds)
int64_t utc;
// Nanoseconds
int32_t nsec;
// Phase (in picoseconds), linearized for receive timestamps, zero for send timestamps
int32_t phase; // phase(picoseconds)
int32_t raw_phase;
int32_t raw_nsec;
int32_t raw_ahead;
//int cntr_ahead;
};
typedef struct _wr_timestamp wr_timestamp_t;
/* OK. These functions we'll develop along with network card driver. You can write your own UDP-based stubs for testing purposes. */
// Initialization of network interface:
// - opens devices
// - does necessary ioctls()
// - initializes connection with the mighty HAL daemon
int ptpd_netif_init();
// Creates UDP or Ethernet RAW socket (determined by sock_type) bound to bind_addr. If PTPD_FLAG_MULTICAST is set, the socket is
// automatically added to multicast group. User can specify physical_port field to bind the socket to specific switch port only.
wr_socket_t *ptpd_netif_create_socket(int sock_type, int flags, wr_sockaddr_t *bind_addr);
// Sends a UDP/RAW packet (data, data_length) to address provided in wr_sockaddr_t.
// For raw frames, mac/ethertype needs to be provided, for UDP - ip/port.
// Every transmitted frame has assigned a tag value, stored at tag parameter. This value is later used
// for recovering the precise transmit timestamp. If user doesn't need it, tag parameter can be left NULL.
int ptpd_netif_sendto(wr_socket_t *sock, wr_sockaddr_t *to, void *data, size_t data_length, wr_timestamp_t *tx_ts);
// Receives an UDP/RAW packet. Data is written to (data) and length is returned. Maximum buffer length can be specified
// by data_length parameter. Sender information is stored in structure specified in 'from'. All RXed packets are timestamped and the timestamp
// is stored in rx_timestamp (unless it's NULL).
int ptpd_netif_recvfrom(wr_socket_t *sock, wr_sockaddr_t *from, void *data, size_t data_length, wr_timestamp_t *rx_timestamp);
// Closes the socket.
int ptpd_netif_close_socket(wr_socket_t *sock);
int ptpd_netif_poll(wr_socket_t*);
int ptpd_netif_get_hw_addr(wr_socket_t *sock, mac_addr_t *mac);
/*
* Function start HW locking of freq on WR Slave
* return:
* PTPD_NETIF_ERROR - locking not started
* PTPD_NETIF_OK - locking started
*/
int ptpd_netif_locking_enable(int txrx, const char *ifaceName, int priority);
/*
*
* return:
*
* PTPD_NETIF_OK - locking started
*/
int ptpd_netif_locking_disable(int txrx, const char *ifaceName, int priority);
int ptpd_netif_locking_poll(int txrx, const char *ifaceName, int priority);
/*
* Function turns on calibration (measurement of delay)
* Tx or Rx depending on the txrx param
* return:
* PTPD_NETIF_NOT_READY - if there is calibratin going on on another port
* PTPD_NETIF_OK - calibration started
*/
int ptpd_netif_calibrating_enable(int txrx, const char *ifaceName);
/*
* Function turns off calibration (measurement of delay)
* Tx or Rx depending on the txrx param
* return:
* PTPD_NETIF_ERROR - if there is calibratin going on on another port
* PTPD_NETIF_OK - calibration started
*/
int ptpd_netif_calibrating_disable(int txrx, const char *ifaceName);
/*
* Function checks if Rx/Tx (depending on the param) calibration is finished
* if finished, returns measured delay in delta
* return:
*
* PTPD_NETIF_OK - locking started
*/
int ptpd_netif_calibrating_poll(int txrx, const char *ifaceName, uint64_t *delta);
/*
* Function turns on calibration pattern.
* return:
* PTPD_NETIF_NOT_READY - if WRSW is busy with calibration on other switch or error occured
* PTPD_NETIF_OK - calibration started
*/
int ptpd_netif_calibration_pattern_enable(const char *ifaceName, unsigned int calibrationPeriod, unsigned int calibrationPattern, unsigned int calibrationPatternLen);
/*
* Function turns off calibration pattern
* return:
* PTPD_NETIF_ERROR - turning off not successful
* PTPD_NETIF_OK - turning off successful
*/
int ptpd_netif_calibration_pattern_disable(const char *ifaceName);
/*
* Function read calibration data if it's available, used at the beginning of PTPWRd to check if
* HW knows already the interface's deltax, and therefore no need for calibration
* return:
* PTPD_NETIF_NOT_FOUND - if deltas are not known
* PTPD_NETIF_OK - if deltas are known, in such case, deltaTx and deltaRx have valid data
*/
int ptpd_netif_read_calibration_data(const char *ifaceName, uint64_t *deltaTx, uint64_t *deltaRx);
#define MACIEK_TMP
#ifdef MACIEK_TMP
int ptpd_netif_select(wr_socket_t*);
int ptpd_netif_get_hw_addr(wr_socket_t *sock, mac_addr_t *mac);
#endif
/*
* Function reads state of the given port (interface in our case), if the port is up, everything is OK, otherwise ERROR
* return:
* PTPD_NETIF_ERROR - if the port is down
* PTPD_NETIF_OK - if the port is up
*/
int ptpd_netif_get_port_state(const char *ifaceName);
/*
* Function looks for a port (interface) for the port number 'number'
* it will return in the argument ifname the port name
* return:
* PTPD_NETIF_ERROR - port not found
* PTPD_NETIF_OK - if the port found
*/
int ptpd_netif_get_ifName(char *ifname, int number);
/* Returns the millisecond "tics" counter value */
uint64_t ptpd_netif_get_msec_tics();
/*
* Function detects external source lock,
*
* return:
* HEXP_EXTSRC_STATUS_LOCKED 0
* HEXP_LOCK_STATUS_BUSY 1
* HEXP_EXTSRC_STATUS_NOSRC 2
*/
int ptpd_netif_extsrc_detection();
#endif
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <rawrabbit.h>
#include "rr_io.h"
#define DEVNAME "/dev/rawrabbit"
static int fd;
int rr_bind(int a_fd)
{
fd = a_fd;
return 0;
}
int rr_init(int bus, int devfn)
{
struct rr_devsel devsel;
int ret = -EINVAL;
devsel.bus = bus;
devsel.devfn = devfn;
devsel.subvendor = RR_DEVSEL_UNUSED;
devsel.vendor = 0x10dc;//RR_DEVSEL_UNUSED;
devsel.device = 0x18d; //RR_DEVSEL_UNUSED;
devsel.subdevice = RR_DEVSEL_UNUSED;
fd = open(DEVNAME, O_RDWR);
if (fd < 0) {
return -1;
}
if (ioctl(fd, RR_DEVSEL, &devsel) < 0) {
return -EIO;
}
return 0;
}
int rr_writel(uint32_t data, uint32_t addr)
{
struct rr_iocmd iocmd;
iocmd.datasize = 4;
iocmd.address = addr;
iocmd.address |= __RR_SET_BAR(0);
iocmd.data32 = data;
ioctl(fd, RR_WRITE, &iocmd);
}
uint32_t rr_readl(uint32_t addr)
{
struct rr_iocmd iocmd;
iocmd.datasize = 4;
iocmd.address = addr;
iocmd.address |= __RR_SET_BAR(0);
ioctl(fd, RR_READ, &iocmd);
return iocmd.data32;
}
static void gennum_writel(uint32_t data, uint32_t addr)
{
struct rr_iocmd iocmd;
iocmd.datasize = 4;
iocmd.address = addr;
iocmd.address |= __RR_SET_BAR(4);
iocmd.data32 = data;
ioctl(fd, RR_WRITE, &iocmd);
}
static uint32_t gennum_readl(uint32_t addr)
{
struct rr_iocmd iocmd;
iocmd.datasize = 4;
iocmd.address = addr;
iocmd.address |= __RR_SET_BAR(4);
ioctl(fd, RR_READ, &iocmd);
return iocmd.data32;
}
static inline int64_t get_tics()
{
struct timezone tz= {0,0};
struct timeval tv;
gettimeofday(&tv, &tz);
return (int64_t)tv.tv_sec * 1000000LL + (int64_t) tv.tv_usec;
}
/* These must be set to choose the FPGA configuration mode */
#define GPIO_BOOTSEL0 15
#define GPIO_BOOTSEL1 14
static inline uint8_t reverse_bits8(uint8_t x)
{
x = ((x >> 1) & 0x55) | ((x & 0x55) << 1);
x = ((x >> 2) & 0x33) | ((x & 0x33) << 2);
x = ((x >> 4) & 0x0f) | ((x & 0x0f) << 4);
return x;
}
static uint32_t unaligned_bitswap_le32(const uint32_t *ptr32)
{
static uint32_t tmp32;
static uint8_t *tmp8 = (uint8_t *) &tmp32;
static uint8_t *ptr8;
ptr8 = (uint8_t *) ptr32;
*(tmp8 + 0) = reverse_bits8(*(ptr8 + 0));
*(tmp8 + 1) = reverse_bits8(*(ptr8 + 1));
*(tmp8 + 2) = reverse_bits8(*(ptr8 + 2));
*(tmp8 + 3) = reverse_bits8(*(ptr8 + 3));
return tmp32;
}
static inline void gpio_out(int fd, const uint32_t addr, const int bit, const int value)
{
uint32_t reg;
reg = gennum_readl(addr);
if(value)
reg |= (1<<bit);
else
reg &= ~(1<<bit);
gennum_writel(reg, addr);
}
/*
* Unfortunately, most of the following is from fcl_gn4124.cpp, for which
* the license terms are at best ambiguous.
*/
int loader_low_level(int fd, const void *data, int size8)
{
int size32 = (size8 + 3) >> 2;
const uint32_t *data32 = data;
int ctrl = 0, i, done = 0, wrote = 0;
/* configure Gennum GPIO to select GN4124->FPGA configuration mode */
gpio_out(fd, GNGPIO_DIRECTION_MODE, GPIO_BOOTSEL0, 0);
gpio_out(fd, GNGPIO_DIRECTION_MODE, GPIO_BOOTSEL1, 0);
gpio_out(fd, GNGPIO_OUTPUT_ENABLE, GPIO_BOOTSEL0, 1);
gpio_out(fd, GNGPIO_OUTPUT_ENABLE, GPIO_BOOTSEL1, 1);
gpio_out(fd, GNGPIO_OUTPUT_VALUE, GPIO_BOOTSEL0, 1);
gpio_out(fd, GNGPIO_OUTPUT_VALUE, GPIO_BOOTSEL1, 0);
gennum_writel( 0x00, FCL_CLK_DIV);
gennum_writel( 0x40, FCL_CTRL); /* Reset */
i = gennum_readl( FCL_CTRL);
if (i != 0x40) {
printf("%s: %i: error\n", __func__, __LINE__);
return -EIO;
}
gennum_writel( 0x00, FCL_CTRL);
gennum_writel( 0x00, FCL_IRQ); /* clear pending irq */
switch(size8 & 3) {
case 3: ctrl = 0x116; break;
case 2: ctrl = 0x126; break;
case 1: ctrl = 0x136; break;
case 0: ctrl = 0x106; break;
}
gennum_writel( ctrl, FCL_CTRL);
gennum_writel( 0x00, FCL_CLK_DIV); /* again? maybe 1 or 2? */
gennum_writel( 0x00, FCL_TIMER_CTRL); /* "disable FCL timr fun" */
gennum_writel( 0x10, FCL_TIMER_0); /* "pulse width" */
gennum_writel( 0x00, FCL_TIMER_1);
/*
* Set delay before data and clock is applied by FCL
* after SPRI_STATUS is detected being assert.
*/
gennum_writel( 0x08, FCL_TIMER2_0); /* "delay before data/clk" */
gennum_writel( 0x00, FCL_TIMER2_1);
gennum_writel( 0x17, FCL_EN); /* "output enable" */
ctrl |= 0x01; /* "start FSM configuration" */
gennum_writel( ctrl, FCL_CTRL);
while(size32 > 0)
{
/* Check to see if FPGA configuation has error */
i = gennum_readl( FCL_IRQ);
if ( (i & 8) && wrote) {
done = 1;
printf("%s: %i: done after %i\n", __func__, __LINE__,
wrote);
} else if ( (i & 0x4) && !done) {
printf("%s: %i: error after %i\n", __func__, __LINE__,
wrote);
return -EIO;
}
/* Wait until at least 1/2 of the fifo is empty */
while (gennum_readl( FCL_IRQ) & (1<<5))
;
/* Write a few dwords into FIFO at a time. */
for (i = 0; size32 && i < 32; i++) {
gennum_writel( unaligned_bitswap_le32(data32),
FCL_FIFO);
data32++; size32--; wrote++;
}
}
gennum_writel( 0x186, FCL_CTRL); /* "last data written" */
/* Checking for the "interrupt" condition is left to the caller */
return wrote;
}
int rr_load_bitstream_from_file(const char *file_name)
{
uint8_t *buf;
FILE *f;
uint32_t size;
f=fopen(file_name,"rb");
if(!f) return -1;
fseek(f, 0, SEEK_END);
size = ftell(f);
buf = malloc(size);
if(!buf)
{
fclose(f);
return -1;
}
fseek(f, 0, SEEK_SET);
fread(buf, 1, size, f);
fclose(f);
int rval = loader_low_level(0, buf, size);
free(buf);
return rval;
}
......@@ -17,7 +17,7 @@ static uint32_t fd_spec_readl(void *priv, uint32_t addr)
return spec_readl(priv, addr);
}
int spec_fdelay_init_bd(fdelay_device_t *dev, int bus, int dev_fn, uint32_t base)
int spec_fdelay_create_bd(fdelay_device_t *dev, int bus, int dev_fn, uint32_t base)
{
dev->priv_io = spec_open(bus, dev_fn);
......@@ -33,13 +33,10 @@ int spec_fdelay_init_bd(fdelay_device_t *dev, int bus, int dev_fn, uint32_t base
spec_vuart_init(dev->priv_io, 0xe0500); /* for communication with WRCore during DMTD calibration */
if(fdelay_init(dev) < 0)
return -1;
return 0;
}
int spec_fdelay_init(fdelay_device_t *dev, int argc, char *argv[])
int spec_fdelay_create(fdelay_device_t *dev, int argc, char *argv[])
{
int bus = -1, dev_fn = -1, c;
uint32_t base = 0x80000;
......@@ -66,7 +63,7 @@ int spec_fdelay_init(fdelay_device_t *dev, int argc, char *argv[])
}
}
return spec_fdelay_init_bd(dev, bus, dev_fn, base);
return spec_fdelay_create_bd(dev, bus, dev_fn, base);
}
TESTS = dmtd_calibration gs_logger random_pulse_test sweep_test temperature_calibration
CFLAGS = -I../include
LDFLAGS = -L../lib -static -lfinedelay -lm
CC=gcc
.PHONY: all
all: $(TESTS)
define TEST_template =
$(1): $$($(1).o)
endef
$(foreach test,$(TESTS),$(eval $(call TEST_template,$(test))))
$(TESTS):
gcc $@.c -o $@ $(CFLAGS) $(LDFLAGS)
clean:
rm -f $(ALL_OBJS) $(TESTS)
\ No newline at end of file
#include <stdio.h>
#include "fdelay_lib.h"
main(int argc, char *argv[])
{
fdelay_device_t dev;
/* Initialize the fine delay generator */
if(spec_fdelay_create(&dev, argc, argv) < 0)
{
printf("Probe failed.\n");
return -1;
}
if(fdelay_init(&dev, 0) < 0)
{
printf("Init failed.\n");
return -1;
}
fdelay_dmtd_calibration(&dev, NULL);
return 0;
}
/* Simple demo that reads samples using the read call */
/* Fine Delay Logger/Pulse Generator program as used in Gran Sasso.
Treat as an example */
#define _GNU_SOURCE
......@@ -46,6 +47,7 @@ struct board_def {
int hw_index;
int in_use;
int fd;
int prev_seq;
struct {
int64_t offset_pps, width, period;
......@@ -265,7 +267,7 @@ void enable_wr(fdelay_device_t *b, int index)
printf("Locking to WR network [board=%d]...", index);
fflush(stdout);
fdelay_configure_sync(b, FDELAY_SYNC_LOCAL);
sleep(2);
/* sleep(2);
fdelay_configure_sync(b, FDELAY_SYNC_WR);
while(fdelay_check_sync(b) <= 0)
......@@ -281,7 +283,7 @@ void enable_wr(fdelay_device_t *b, int index)
}
printf("\n");
fflush(stdout);
fflush(stdout);*/
}
/* Add two timestamps */
......@@ -311,11 +313,17 @@ int configure_board(struct board_def *bdef)
if(spec_fdelay_init(b, bdef->hw_index >>8, bdef->hw_index & 0xff) < 0)
if(spec_fdelay_create_bd(b, bdef->hw_index >>8, bdef->hw_index & 0xff, 0x80000) < 0)
{
fprintf(stderr,"Can't open fdelay board @ hw_index %x\n", bdef->hw_index);
exit(-1);
}
if(fdelay_init(b, 0) < 0)
{
fprintf(stderr,"Can't initialize fdelay board @ hw_index %x\n", bdef->hw_index);
exit(-1);
}
bdef->b = b;
......@@ -390,20 +398,52 @@ int configure_board(struct board_def *bdef)
return 0;
}
/* Substract two timestamps */
static fdelay_time_t ts_sub(fdelay_time_t a, fdelay_time_t b)
{
a.frac -= b.frac;
if(a.frac < 0)
{
a.frac += 4096;
a.coarse--;
}
a.coarse -= b.coarse;
if(a.coarse < 0)
{
a.coarse += 125000000;
a.utc --;
}
a.utc -= b.utc;
return a;
}
void handle_readout(struct board_def *bdef)
{
int64_t t_ps;
fdelay_time_t t;
static fdelay_time_t t_prev;
static time_t start;
int done;
while(fdelay_read(bdef->b, &t, 1) == 1)
{
t_ps = (t.coarse * 8000LL) + ((t.frac * 8000LL) >> 12);
printf("card 0x%04x, seq %5i: time %lli s, %lli.%03lli ns [%x] ", bdef->hw_index, t.seq_id, t.utc, t_ps / 1000LL, t_ps % 1000LL, t.coarse);
printf("raw utc=%lld coarse=%d startoffs=%d suboffs=%d frac=%d [%x]\n", t.raw.utc, t.raw.coarse, t.raw.start_offset, t.raw.subcycle_offset, t.raw.frac- 30000, t.raw.frac);
printf("card 0x%04x, seq %5i: time %lli s, %lli.%03lli ns [count %d] ", bdef->hw_index, t.seq_id, t.utc, t_ps / 1000LL, t_ps % 1000LL, (t.raw.tsbcr >> 10) & 0x3ff);
// printf("raw utc=%lld coarse=%d startoffs=%d suboffs=%d frac=%d [%x]\n", t.raw.utc, t.raw.coarse, t.raw.start_offset, t.raw.subcycle_offset, t.raw.frac- 30000, t.raw.frac);
log_write(&t, bdef->hw_index);
if(((bdef->prev_seq + 1) & 0xffff) != (t.seq_id & 0xffff))
{
printf("MISMATCH\n");
} else printf("\n");
// printf("raw %d %d\n", t.raw.start_offset, t.raw.frac-30000);
// printf("delta %lld\n", fdelay_to_picos(ts_sub(t,t_prev)));
bdef->prev_seq = t.seq_id;
t_prev = t;
}
}
......@@ -453,6 +493,7 @@ int main(int argc, char *argv[])
fdelay_configure_readout(boards[i].b, 1);
fdelay_configure_trigger(boards[i].b, 1, boards[i].term_on);
boards[i].prev_seq = -1;
}
......@@ -469,7 +510,7 @@ int main(int argc, char *argv[])
if(fdelay_dbg_sync_lost(boards[i].b))
{
printf("Weird, sync lost @ board %x. Reconfiguring...\n", boards[i].b);
printf("Weird, sync lost @ board %p. Reconfiguring...\n", boards[i].b);
configure_board(&boards[i]);
}
......@@ -482,4 +523,3 @@ int main(int argc, char *argv[])
}
......@@ -19,30 +19,30 @@ termination 0
# WARNING 2: define these for ALL boards independently
input_offset -69100p
output_offset 20400p
input_offset -63100p
output_offset 14400p
# Output configuration
# out output_ID offset_from_pps[ps] width[ps] period[ps]
# out 1 = PPS signal, 1us pulse width
out 1 0 500u 1000u
out 1 0 100m 1s
out 2 0 500u 1000u
out 4 0 500u 1000u
out 3 0 250u 499999990p
out 4 0 10u 99999990p
# #######################
# # Select board 1
# #######################
#
board 1
hw_index 0x0500
#\board 1
#hw_index 0x0500
input_offset -69100p
output_offset 20400p
termination 0
#input_offset -63100p
#output_offset 14400p
#termination 1
out 1 0 500u 1000u
out 2 0 500u 1000u
out 4 0 500u 1000u
out 3 0 250u 499999990p
#out 1 0 500u 1000u
#out 2 0 500u 1000u
#out 4 0 500u 1000u
#out 3 0 100u 200u
......@@ -173,10 +173,16 @@ void enable_wr(fdelay_device_t *b, int index)
}
int configure_board(fdelay_device_t *b)
int configure_board(fdelay_device_t *b, int argc, char *argv[])
{
if(spec_fdelay_init(b,1,0) < 0)
if(spec_fdelay_create(b, 1, NULL) < 0)
{
printf("Probe failed\n");
exit(-1);
}
if(fdelay_init(b, 0) < 0)
{
printf("Init failed\n");
exit(-1);
......@@ -274,7 +280,7 @@ int main(int argc, char *argv[])
{
fdelay_device_t b;
configure_board(&b);
configure_board(&b, argc, argv);
pqueue_clear(&incoming);
pqueue_clear(&outgoing);
......
......@@ -19,7 +19,13 @@ int configure_board(fdelay_device_t *b, int argc, char *argv[])
{
fdelay_time_t t;
if(spec_fdelay_init(b,argc,argv) < 0)
if(spec_fdelay_create(b, argc, argv) < 0)
{
printf("Probe failed\n");
exit(-1);
}
if(fdelay_init(b, 0) < 0)
{
printf("Init failed\n");
exit(-1);
......@@ -82,6 +88,5 @@ int main(int argc, char *argv[])
fdelay_device_t b;
configure_board(&b, argc, argv);
handle_readout(&b, 40000);
}
/* Temperature calibration test program.
Requires a PWM-driven peltier cooler placed over the delay line chips, PWM drive connected to MOSI pin */
#include <stdio.h>
#include <stdint.h>
#include <math.h>
#include "fdelay_lib.h"
#include "fdelay_private.h"
#include "fd_main_regs.h"
#include "onewire.h"
#include "rr_io.h"
typedef struct {
float kp, ki, err, pwm, setpoint, i, bias;
......@@ -25,7 +29,7 @@ void pi_update(fdelay_device_t *dev, float temp)
dbg("t %.1f err:%.1f DRIVE: %d\n", temp, pi_state.err, (int)pi_state.pwm);
fd_writel(FD_I2CR_DBGOUT_W((int)pi_state.pwm), FD_REG_I2CR);
fd_writel((int)pi_state.pwm, FD_REG_TDER2);
}
extern int64_t get_tics();
......@@ -56,41 +60,34 @@ int pi_set_temp(fdelay_device_t *dev, float new_temp)
return fabs(pi_state.err) < 0.1 ? 1: 0;
}
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()
main(int argc, char *argv[])
{
fdelay_device_t *dev = malloc(sizeof(fdelay_device_t));
rr_init(RR_DEVSEL_UNUSED, RR_DEVSEL_UNUSED);
fdelay_device_t dev;
dev->writel = my_writel;
dev->readl = my_readl;
dev->base_addr = 0x80000;
if(fdelay_init(dev) < 0)
if(spec_fdelay_create(&dev, argc, argv) < 0)
{
fprintf(stderr,"Card probe failed.\n");
return -1;
}
if(fdelay_init(&dev, 0) < 0)
{
fprintf(stderr,"Card init failed.\n");
return -1;
}
float t_min = 40.0, t_max = 80.0, t_cur;
t_cur = t_min;
for(;;)
{
if(pi_set_temp(dev, t_cur))
if(pi_set_temp(&dev, t_cur))
{
fd_decl_private(dev);
fdelay_device_t *b = &dev;
fd_decl_private(b);
calibrate_outputs(dev);
calibrate_outputs(&dev);
fprintf(stderr, "> %.1f %d %d %d %d\n", t_cur, hw->frr_cur[0],
hw->frr_cur[1], hw->frr_cur[2], hw->frr_cur[3]);
t_cur += 1.0;
......
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