Commit 605c84cc authored by Tomasz Wlostowski's avatar Tomasz Wlostowski

software: fdelay-gs ported to use test library

parent 7b9803b4
......@@ -38,7 +38,7 @@ typedef struct fdelay_device
typedef struct
{
int32_t utc; /* TAI seconds */ /* FIXME: replace all UTCs with TAIs or seconds for clarity */
int64_t utc; /* TAI seconds */ /* FIXME: replace all UTCs with TAIs or seconds for clarity */
int32_t coarse; /* 125 MHz counter cycles */
int32_t frac; /* Fractional part (<8ns) */
uint16_t seq_id; /* Sequence ID to detect missed timestamps */
......@@ -113,5 +113,9 @@ int fdelay_configure_pulse_gen(fdelay_device_t *dev, int channel_mask, int enabl
/* (pulse mode only) Returns non-0 when all of the channels in channel mask have produced their programmed pulses */
int fdelay_outputs_triggered(fdelay_device_t *dev, int channel_mask, int blocking);
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);
#endif
......@@ -85,6 +85,7 @@ struct fine_delay_hw
int wr_enabled;
int wr_state;
struct fine_delay_calibration calib;
int64_t input_user_offset, output_user_offset;
};
/* some useful access/declaration macros */
......
......@@ -3,24 +3,16 @@ CFLAGS = -I../include -g -Imini_bone -Ispll
#uncomment for extra tests (DAC, output stage INL/DNL)
#CFLAGS += -DPERFORM_LONG_TESTS
OBJS_LIB = fdelay_lib.o fdelay_bus.o rr_io.o i2c_master.o onewire.o mini_bone/minibone_lib.o mini_bone/ptpd_netif.o spec_common.o
OBJS_LIB = fdelay_lib.o i2c_master.o onewire.o mini_bone/minibone_lib.o mini_bone/ptpd_netif.o spec_common.o
all: testprog lib testprog3 testprog4
all: testprog lib gs
lib: $(OBJS_LIB)
gcc -shared -o libfinedelay.so $(OBJS_LIB)
testprog: lib fdelay_test.o
gcc -o fdelay_test $(OBJS_LIB) fdelay_test.o -lm
#testprog2: lib fdelay_cal.o
# gcc -o fdelay_cal $(OBJS_LIB) fdelay_cal.o -lm
testprog3: lib fdelay_pps_demo.o
testprog: lib fdelay_pps_demo.o
gcc -o fdelay_pps_demo $(OBJS_LIB) fdelay_pps_demo.o -lm
testprog4: lib fdelay_eeprom.o
gcc -o fdelay_eeprom $(OBJS_LIB) fdelay_eeprom.o -lm
gs: lib fdelay-gs.o
gcc -o fdelay-gs $(OBJS_LIB) fdelay-gs.o -lm
clean:
rm -f libfinedelay.so $(OBJS_LIB)
\ No newline at end of file
/* Simple demo that reads samples using the read call */
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <unistd.h>
#include <signal.h>
#include <sys/select.h>
#define FDELAY_INTERNAL // for sysfs_get/set
#include "fdelay_lib.h"
#define PACKED __attribute__((packed))
#define TYPE_START_LOGGING 1
#define TYPE_END_LOGGING 2
#define TYPE_TIMESTAMP 3
struct fdelay_time {
uint64_t utc;
uint32_t coarse;
uint32_t frac;
uint32_t seq_id;
uint32_t channel;
};
PACKED struct binary_timestamp {
int32_t card_id;
uint8_t type;
PACKED struct fdelay_time ts;
};
#define MAX_BOARDS 64
struct board_def {
fdelay_device_t *b;
int term_on;
int64_t input_offset;
int64_t output_offset;
int hw_index;
int in_use;
int fd;
struct {
int64_t offset_pps, width, period;
int enabled;
} outs [4];
};
struct board_def boards[MAX_BOARDS];
static FILE *log_file = NULL;
void log_write(fdelay_time_t *t, int card_id)
{
struct binary_timestamp bt;
if(!log_file)
return;
bt.ts.utc = t->utc;
bt.ts.coarse = t->coarse;
bt.ts.frac = t->frac;
bt.ts.seq_id = t->seq_id;
bt.ts.channel = 0;
bt.type = TYPE_TIMESTAMP;
bt.card_id = card_id;
fwrite(&bt, sizeof(struct binary_timestamp), 1, log_file);
fflush(log_file);
}
void log_start(char *log_file_name)
{
struct binary_timestamp bt;
log_file = fopen(log_file_name, "a+");
if(!log_file)
{
fprintf(stderr, "Can't open the log file: %s\n", log_file_name);
exit(-1);
}
bt.card_id = 0;
bt.type = TYPE_START_LOGGING;
fwrite(&bt, sizeof(struct binary_timestamp), 1, log_file);
fflush(log_file);
}
void log_stop()
{
struct binary_timestamp bt;
if(!log_file)
return;
bt.card_id = 0;
bt.type = TYPE_END_LOGGING;
fwrite(&bt, sizeof(struct binary_timestamp), 1, log_file);
fflush(log_file);
fclose(log_file);
}
int64_t parse_num(const char *n_str)
{
struct {
char unit;
int64_t multiplier;
} units[] = {
{'p', 1LL},
{'n', 1000LL},
{'u', 1000000LL},
{'m', 1000000000LL},
{'s', 1000000000000LL},
{' ', 0}
};
int64_t n;
char unit;
int rv = sscanf(n_str,"%lli%c", &n, &unit);
if(rv == 1)
return n;
else if (rv == 2)
{
int i;
for(i=0; units[i].multiplier; i++)
if(units[i].unit == unit)
return units[i].multiplier * n;
fprintf(stderr,"Unrecognized numeric constant '%s' (wrong units?)\n", n_str);
exit(-1);
}
fprintf(stderr,"Unrecognized numeric constant '%s'\n", n_str);
exit(-1);
}
#define MAX_TOKENS 16
#define MAX_TOK_LENGTH 256
typedef char token_array[MAX_TOKENS][MAX_TOK_LENGTH];
/* returns: tokenized arguments to tokens, command (1st word) to cmd */
int next_command(FILE *f_config, char *cmd, token_array tokens)
{
char line [1024];
char *running;
const char *delims=" \n\r\t";
int i;
int n = 0;
do {
if(feof(f_config))
return -1;
fgets(line, sizeof(line), f_config);
running = strdupa(line);
while(*running == ' ' || *running == '\t') running++;
strncpy(cmd, strsep (&running , delims), MAX_TOK_LENGTH);
} while(cmd[0] == '#' || cmd[0] == ' ' || cmd[0] == '\n' || cmd[0] == '\r' || !cmd[0]);
for(i=0;i<MAX_TOKENS;i++)
{
char *token = strsep (&running , delims);
if(token == NULL)
return n;
if(strlen(token) > 0)
{
// printf("tok %p\n", tokens[0]);
strncpy(&tokens[n][0], token, MAX_TOK_LENGTH);
n++;
}
}
return 0;
}
#define CUR boards[current_board]
void load_config(const char *config_file)
{
token_array args;
char cmd [MAX_TOK_LENGTH];
int current_board = 0;
int n_args;
FILE *f_config=fopen(config_file, "r");
if(!f_config)
{
fprintf(stderr,"Can't open configuration file '%s'\n", config_file);
exit(-1);
}
memset(boards, 0, sizeof(boards));
while((n_args = next_command(f_config, cmd, args)) >= 0)
{
if(!strcmp(cmd, "board"))
{
current_board = parse_num(args[0]);
CUR.in_use = 1;
}
if(!strcmp(cmd, "hw_index"))
{
CUR.hw_index = parse_num(args[0]);
printf("Adding board %d, hw_index %x\n", current_board, CUR.hw_index);
}
if(!strcmp(cmd, "termination"))
CUR.term_on = parse_num(args[0]);
if(!strcmp(cmd, "input_offset"))
CUR.input_offset = parse_num(args[0]);
if(!strcmp(cmd, "output_offset"))
CUR.output_offset = parse_num(args[0]);
if(!strcmp(cmd, "out"))
{
int index = parse_num(args[0]) - 1;
if(index < 0 || index > 3)
{
fprintf(stderr,"Invalid output index\n");
exit(-1);
}
CUR.outs[index].offset_pps = parse_num(args[1]);
CUR.outs[index].width = parse_num(args[2]);
CUR.outs[index].period = parse_num(args[3]);
CUR.outs[index].enabled = 1;
// printf("OutCfg: %d %lli %lli %lli\n", index, CUR.outs[index].offset_pps, CUR.outs[index].width, CUR.outs[index].period);
}
if(!strcmp(cmd, "log_file"))
log_start(args[0]);
}
fclose(f_config);
}
#undef CUR
void enable_termination(fdelay_device_t *b, int enable)
{
int i;
fdelay_configure_trigger(b, 1, enable);
}
void enable_wr(fdelay_device_t *b, int index)
{
int lock_retries = 10;
printf("Locking to WR network [board=%d]...", index);
fflush(stdout);
fdelay_configure_sync(b, FDELAY_SYNC_LOCAL);
sleep(2);
fdelay_configure_sync(b, FDELAY_SYNC_WR);
while(fdelay_check_sync(b) <= 0)
{
printf(".");
fflush(stdout);
sleep(1);
if(lock_retries-- == 0)
{
fprintf(stderr," WR lock timed out\n");
exit(1);
}
}
printf("\n");
fflush(stdout);
}
/* Add two timestamps */
static fdelay_time_t ts_add(fdelay_time_t a, fdelay_time_t b)
{
a.frac += b.frac;
if(a.frac >= 4096)
{
a.frac -= 4096;
a.coarse++;
}
a.coarse += b.coarse;
if(a.coarse >= 125000000)
{
a.coarse -= 125000000;
a.utc ++;
}
a.utc += b.utc;
return a;
}
int configure_board(struct board_def *bdef)
{
fdelay_device_t *b = malloc(sizeof(fdelay_device_t));
int i;
if(spec_fdelay_init(b, bdef->hw_index >>8, bdef->hw_index & 0xff) < 0)
{
fprintf(stderr,"Can't open fdelay board @ hw_index %x\n", bdef->hw_index);
exit(-1);
}
bdef->b = b;
fdelay_configure_trigger(bdef->b, 0, bdef->term_on);
enable_wr(b, bdef->hw_index);
int val = bdef->input_offset;
// fdelay_sysfs_set((struct __fdelay_board *)b, "fd-input/user-offset", (uint32_t *)&val);
fdelay_set_user_offset(b, 1, val);
for(i=0;i<4;i++)
{
char path[1024];
int val = bdef->output_offset;
snprintf(path, sizeof(path), "fd-ch%d/user-offset", i+1);
fdelay_set_user_offset(b, 0, val);
// fdelay_sysfs_set((struct __fdelay_board *)b, path,(uint32_t *) &val);
}
for(i=0;i<4;i++)
{
if(bdef->outs[i].enabled)
{
fdelay_time_t t_cur, pps_offset, width;
// struct fdelay_pulse p;
fdelay_get_time(bdef->b, &t_cur);
// printf("Configure output %d [t_cur %d:%d]\n", i+1,t_cur.utc, t_cur.coarse);
// fdelay_pico_to_time(&bdef->outs[i].offset_pps, &pps_offset);
// fdelay_pico_to_time(&bdef->outs[i].width, &width);
t_cur.utc += 2;
t_cur.coarse = 0;
t_cur.frac = 0;
t_cur = ts_add(t_cur, fdelay_from_picos(bdef->outs[i].offset_pps));
//printf("Configure output %d [t_start %d:%d width %lld period %lld]\n", i+1,t_cur.utc, t_cur.coarse, bdef->outs[i].width, bdef->outs[i].period);
fdelay_configure_pulse_gen(bdef->b, i+1, 1, t_cur, bdef->outs[i].width, bdef->outs[i].period, -1);
// fdelay_configure_output_pulse()
/* p.rep = -1;
p.mode = FD_OUT_MODE_PULSE;
p.start = t_cur;
p.end = ts_add(t_cur, width);*/
// fdelay_pico_to_time(&bdef->outs[i].period, &p.loop);
// fdelay_config_pulse(bdef->b, i, &p);
}
}
/*enable input */
for(i=0;i<4;i++)
if(bdef->outs[i].enabled)
while(!fdelay_channel_triggered(bdef->b, i+1)) usleep(100000);
fdelay_configure_trigger(bdef->b, 1, bdef->term_on);
fdelay_configure_readout(bdef->b, 1);
printf("Configuration complete\n");
fflush(stdout);
return 0;
}
void handle_readout(struct board_def *bdef)
{
int64_t t_ps;
fdelay_time_t t;
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]\n", bdef->hw_index, t.seq_id, t.utc, t_ps / 1000LL, t_ps % 1000LL, t.coarse);
log_write(&t, bdef->hw_index);
}
}
void sighandler(int sig)
{
if(sig == SIGINT || sig== SIGTERM || sig==SIGKILL)
{
fprintf(stderr,"Cleaniung up...\n");
log_stop();
exit(0);
}
}
int main(int argc, char *argv[])
{
int i, maxfd = -1;
fd_set allset, curset;
signal(SIGINT, sighandler);
signal(SIGTERM, sighandler);
signal(SIGKILL, sighandler);
if(argc < 2)
{
printf("usage: %s <configuration-file>\n", argv[0]);
return 0;
}
load_config(argv[1]);
for(i=0;i<MAX_BOARDS;i++)
if(boards[i].in_use) {
int fd;
configure_board(&boards[i]);
}
for(;;)
{
for(i=0;i<MAX_BOARDS;i++) {
if(!boards[i].in_use)
continue;
// printf(".");
handle_readout(&boards[i]);
}
usleep(100);
}
}
/*
/* \
FmcDelay1ns4Cha (a.k.a. The Fine Delay Card)
User-space driver/library
......@@ -874,7 +874,8 @@ int fdelay_init(fdelay_device_t *dev)
hw->wr_enabled = 0;
hw->wr_state = FDELAY_FREE_RUNNING;
hw->acam_addr = 0xff;
hw->input_user_offset = 0;
hw->output_user_offset= 0;
dbg("%s: Initializing the Fine Delay Card\n", __FUNCTION__);
/* Read the Identification register and check if we are talking to a proper Fine Delay HDL Core */
......@@ -893,8 +894,9 @@ int fdelay_init(fdelay_device_t *dev)
}
rv = read_calibration_eeprom(dev, &hw->calib);
// rv = read_calibration_eeprom(dev, &hw->calib);
rv = 0;
if(rv < 0)
{
fail(TEST_SPI, "FMC EEPROM not detected.");
......@@ -1087,7 +1089,7 @@ static fdelay_time_t ts_add(fdelay_time_t a, fdelay_time_t b)
a.coarse++;
}
a.coarse += b.coarse;
if(b.coarse >= 125000000)
if(a.coarse >= 125000000)
{
a.coarse -= 125000000;
a.utc ++;
......@@ -1095,6 +1097,8 @@ static fdelay_time_t ts_add(fdelay_time_t a, fdelay_time_t b)
return a;
}
/* Converts a Fine Delay time stamp to plain picoseconds */
int64_t fdelay_to_picos(const fdelay_time_t t)
{
......@@ -1102,6 +1106,14 @@ int64_t fdelay_to_picos(const fdelay_time_t t)
return tp;
}
static fdelay_time_t ts_add_ps(fdelay_time_t a, int64_t b)
{
if(b < 0)
return ts_sub(a, fdelay_from_picos(-b));
else
return ts_add(a, fdelay_from_picos(b));
}
static int poll_rbuf(fdelay_device_t *dev)
{
fd_decl_private(dev)
......@@ -1150,9 +1162,9 @@ int fdelay_read(fdelay_device_t *dev, fdelay_time_t *timestamps, int how_many)
seq_frac = fd_readl(FD_REG_TSBR_FID);
ts.frac = FD_TSBR_FID_FINE_R(seq_frac);
ts.seq_id = FD_TSBR_FID_SEQID_R(seq_frac);
ts.channel = FD_TSBR_FID_CHANNEL_R(seq_frac);
// ts.channel = FD_TSBR_FID_CHANNEL_R(seq_frac);
*timestamps++ = ts_sub(ts, fdelay_from_picos(hw->calib.tdc_zero_offset));
*timestamps++ = ts_add_ps(ts, hw->input_user_offset);
how_many--;
n_read++;
......@@ -1222,32 +1234,29 @@ int fdelay_configure_output(fdelay_device_t *dev, int channel, int enable, int64
int fdelay_configure_pulse_gen(fdelay_device_t *dev, int channel, int enable, fdelay_time_t t_start, int64_t width_ps, int64_t delta_ps, int rep_count)
{
fd_decl_private(dev)
uint32_t base = (channel-1) * 0x20;
uint32_t dcr;
fdelay_time_t start, end, delta;
if(channel < 1 || channel > 4)
return -1;
start = ts_add_ps(t_start, hw->output_user_offset);
end = ts_add_ps(t_start, hw->output_user_offset + width_ps - 4000);
delta = fdelay_from_picos(delta_ps);
start = t_start;
end = fdelay_from_picos(fdelay_to_picos(start) + width_ps - 4000);
delta = fdelay_from_picos(delta_ps);
//start = t_start;
//end = ts_add(start, fdelay_from_picos(width_ps));
//delta = fdelay_from_picos(delta_ps);
//printf("Start: %lld: %d:%d\n", start.utc, start.coarse, start.frac);
//printf("width: %lld delta: %lld rep: %d\n", width_ps, delta_ps, rep_count);
printf("Channel: %d\n",channel);
printf("TStart: %d: %d:%d rep %d\n", t_start.utc, t_start.coarse, t_start.frac, rep_count);
printf("Start: %d: %d:%d rep %d\n", start.utc, start.coarse, start.frac, rep_count);
printf("End: %d: %d:%d rep %d\n", end.utc, end.coarse, end.frac, rep_count);
printf("Delta: %d: %d:%d rep %d\n", delta.utc, delta.coarse, delta.frac, rep_count);
chan_writel(hw->frr_cur[channel-1], FD_REG_FRR);
chan_writel(start.utc >> 32, FD_REG_U_STARTH);
chan_writel(0, FD_REG_U_STARTH);
chan_writel(start.utc & 0xffffffff, FD_REG_U_STARTL);
chan_writel(start.coarse, FD_REG_C_START);
chan_writel(start.frac, FD_REG_F_START);
chan_writel(end.utc >> 32, FD_REG_U_ENDH);
chan_writel(0, FD_REG_U_ENDH);
chan_writel(end.utc & 0xffffffff, FD_REG_U_ENDL);
chan_writel(end.coarse, FD_REG_C_END);
chan_writel(end.frac, FD_REG_F_END);
......@@ -1279,7 +1288,7 @@ int fdelay_channel_triggered(fdelay_device_t *dev, int channel)
{
fd_decl_private(dev)
uint32_t dcr= chan_readl(FD_REG_DCR);
printf("DCR%d %x\n", channel, dcr);
// printf("DCR%d %x\n", channel, dcr);
return dcr & FD_DCR_PG_TRIG ? 1: 0;
}
......@@ -1292,7 +1301,7 @@ int fdelay_set_time(fdelay_device_t *dev, const fdelay_time_t t)
fd_writel(0, FD_REG_GCR);
fd_writel(t.utc >> 32, FD_REG_TM_SECH);
fd_writel(0, FD_REG_TM_SECH);
fd_writel(t.utc & 0xffffffff, FD_REG_TM_SECL);
fd_writel(t.coarse, FD_REG_TM_CYCLES);
......@@ -1313,9 +1322,25 @@ int fdelay_get_time(fdelay_device_t *dev, fdelay_time_t *t)
fd_writel(tcr | FD_TCR_CAP_TIME, FD_REG_TCR);
t->utc = fd_readl(FD_REG_TM_SECL);
t->coarse = fd_readl(FD_REG_TM_CYCLES);
// printf("GetTime: %d %d\n", t->utc, t->coarse);
return 0;
}
void fdelay_set_user_offset(fdelay_device_t *dev,int input, int64_t offset)
{
fd_decl_private(dev)
if(input)
{
dbg("SetUserInputOffset %lld ps \n", offset);
hw->input_user_offset= offset;
}
else
{
dbg("SetUserOutputOffset %lld ps \n", offset);
hw->output_user_offset= offset;
}
}
/* To be rewritten to use interrupts and new WR FSM (see TCR register description).
Use the API provided in fdelay_lib.h */
......@@ -1325,11 +1350,11 @@ int fdelay_configure_sync(fdelay_device_t *dev, int mode)
if(mode == FDELAY_SYNC_LOCAL)
{
fd_writel(0, FD_REG_GCR);
// fd_writel(0, FD_REG_GCR);
fd_writel(0, FD_REG_TCR);
hw->wr_enabled = 0;
} else {
fd_writel(0, FD_REG_GCR);
// fd_writel(0, FD_REG_GCR);
fd_writel(FD_TCR_WR_ENABLE, FD_REG_TCR);
hw->wr_enabled = 1;
}
......@@ -1339,7 +1364,7 @@ int fdelay_check_sync(fdelay_device_t *dev)
{
fd_decl_private(dev)
fprintf(stderr, "TCR %x\n", fd_readl(FD_REG_TCR) & FD_TCR_WR_LOCKED);
// fprintf(stderr, "TCR %x\n", fd_readl(FD_REG_TCR) & FD_TCR_WR_LOCKED);
if(hw->wr_enabled && (fd_readl(FD_REG_TCR) & FD_TCR_WR_LOCKED))
return 1;
......
......@@ -9,16 +9,22 @@ main(int argc, char *argv[])
fdelay_device_t dev;
fdelay_time_t t_cur, t_start;
if(spec_fdelay_init(argc, argv, &dev) < 0)
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 */
fdelay_get_time(&dev, &t_cur);
printf("Current Time: %lld:%d\n", t_cur.utc, t_cur.coarse);
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+2;
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 */
......