Commit 970514ba authored by Federico Vaga's avatar Federico Vaga

*: propagate the HW timestamp format to userspace

This avoids local processing of all timestamps during acquisition
Signed-off-by: Federico Vaga's avatarFederico Vaga <federico.vaga@cern.ch>
parent 9bbd8088
...@@ -62,14 +62,26 @@ enum ft_command { ...@@ -62,14 +62,26 @@ enum ft_command {
FT_CMD_IDENTIFY_OFF FT_CMD_IDENTIFY_OFF
}; };
/* White Rabbit timestamp */ /* Hardware TDC timestamp */
struct ft_wr_timestamp { struct ft_hw_timestamp {
uint64_t seconds; uint32_t seconds; /* 1 second resolution */
uint32_t coarse; uint32_t coarse; /* 8 ns resolution */
uint32_t frac; uint32_t frac; /* In ACAM bins (81 ps) */
uint32_t channel; uint32_t metadata; /* channel, polarity, etc. */
uint32_t hseq_id; /* hardware channel sequence id */ } __packed;
};
#define FT_HW_TS_META_CHN_MASK 0x7
#define FT_HW_TS_META_CHN_SHIFT 0
#define FT_HW_TS_META_CHN(_meta) ((_meta & FT_HW_TS_META_CHN_MASK) >> FT_HW_TS_META_CHN_SHIFT)
#define FT_HW_TS_META_POL_MASK 0x8
#define FT_HW_TS_META_POL_SHIFT 3
#define FT_HW_TS_META_POL(_meta) ((_meta & FT_HW_TS_META_POL_MASK) >> FT_HW_TS_META_POL_SHIFT)
#define FT_HW_TS_META_SEQ_MASK 0xFFFFFFF0
#define FT_HW_TS_META_SEQ_SHIFT 4
#define FT_HW_TS_META_SEQ(_meta) ((_meta & FT_HW_TS_META_SEQ_MASK) >> FT_HW_TS_META_SEQ_SHIFT)
/* rest of the file is kernel-only */ /* rest of the file is kernel-only */
#ifdef __KERNEL__ #ifdef __KERNEL__
...@@ -115,33 +127,13 @@ struct ft_calibration { /* All of these are big endian in the EEPROM */ ...@@ -115,33 +127,13 @@ struct ft_calibration { /* All of these are big endian in the EEPROM */
int32_t wr_offset; int32_t wr_offset;
}; };
/* Hardware TDC timestamp */
struct ft_hw_timestamp {
uint32_t utc; /* 1 second resolution */
uint32_t coarse; /* 8 ns resolution */
uint32_t frac; /* In ACAM bins (81 ps) */
uint32_t metadata; /* channel, polarity, etc. */
} __packed;
#define FT_HW_TS_META_CHN_MASK 0x7
#define FT_HW_TS_META_CHN_SHIFT 0
#define FT_HW_TS_META_CHN(_meta) ((_meta & FT_HW_TS_META_CHN_MASK) >> FT_HW_TS_META_CHN_SHIFT)
#define FT_HW_TS_META_POL_MASK 0x8
#define FT_HW_TS_META_POL_SHIFT 3
#define FT_HW_TS_META_POL(_meta) ((_meta & FT_HW_TS_META_POL_MASK) >> FT_HW_TS_META_POL_SHIFT)
#define FT_HW_TS_META_SEQ_MASK 0xFFFFFFF0
#define FT_HW_TS_META_SEQ_SHIFT 4
#define FT_HW_TS_META_SEQ(_meta) ((_meta & FT_HW_TS_META_SEQ_MASK) >> FT_HW_TS_META_SEQ_SHIFT)
struct ft_channel_state { struct ft_channel_state {
unsigned long flags; unsigned long flags;
int delay_reference; int delay_reference;
int32_t user_offset; int32_t user_offset;
struct ft_wr_timestamp last_ts; /**< used to compute delay struct ft_hw_timestamp last_ts; /**< used to compute delay
between pulses */ between pulses */
int active_buffer; int active_buffer;
...@@ -232,8 +224,8 @@ int ft_read_temp(struct fmctdc_dev *ft, int verbose); ...@@ -232,8 +224,8 @@ int ft_read_temp(struct fmctdc_dev *ft, int verbose);
int ft_pll_init(struct fmctdc_dev *ft); int ft_pll_init(struct fmctdc_dev *ft);
void ft_pll_exit(struct fmctdc_dev *ft); void ft_pll_exit(struct fmctdc_dev *ft);
void ft_ts_apply_offset(struct ft_wr_timestamp *ts, int32_t offset_picos); void ft_ts_apply_offset(struct ft_hw_timestamp *ts, int32_t offset_picos);
void ft_ts_sub(struct ft_wr_timestamp *a, struct ft_wr_timestamp *b); void ft_ts_sub(struct ft_hw_timestamp *a, struct ft_hw_timestamp *b);
void ft_set_tai_time(struct fmctdc_dev *ft, uint64_t seconds, uint32_t coarse); void ft_set_tai_time(struct fmctdc_dev *ft, uint64_t seconds, uint32_t coarse);
void ft_get_tai_time(struct fmctdc_dev *ft, uint64_t * seconds, void ft_get_tai_time(struct fmctdc_dev *ft, uint64_t * seconds,
......
...@@ -92,65 +92,33 @@ static void ft_irq_enable_restore(struct fmctdc_dev *ft) ...@@ -92,65 +92,33 @@ static void ft_irq_enable_restore(struct fmctdc_dev *ft)
* @ts timestamp * @ts timestamp
*/ */
static void ft_timestamp_apply_offsets(struct fmctdc_dev *ft, static void ft_timestamp_apply_offsets(struct fmctdc_dev *ft,
struct ft_wr_timestamp *ts) struct ft_hw_timestamp *hwts)
{ {
struct ft_channel_state *st = &ft->channels[ts->channel]; unsigned int chan = FT_HW_TS_META_CHN(hwts->metadata);
struct ft_channel_state *st = &ft->channels[chan];
ft_ts_apply_offset(ts, ft->calib.zero_offset[ts->channel]); ft_ts_apply_offset(hwts, ft->calib.zero_offset[chan]);
ft_ts_apply_offset(ts, -ft->calib.wr_offset); ft_ts_apply_offset(hwts, -ft->calib.wr_offset);
if (st->user_offset) if (st->user_offset)
ft_ts_apply_offset(ts, st->user_offset); ft_ts_apply_offset(hwts, st->user_offset);
}
/**
* It converts an hardware timestamp into our local representation
* @ft FmcTdc device instance
* @hwts timestamp
* @wrts timestamp
*/
static void __ft_timestamp_hw_to_wr(struct fmctdc_dev *ft,
struct ft_wr_timestamp *wrts,
struct ft_hw_timestamp *hwts)
{
wrts->channel = FT_HW_TS_META_CHN(hwts->metadata);
wrts->seconds = hwts->utc;
wrts->coarse = hwts->coarse;
wrts->frac = hwts->frac;
wrts->hseq_id = FT_HW_TS_META_SEQ(hwts->metadata);
}
/**
* It proccess a given timestamp and when it correspond to a pulse it
* converts the timestamp from the hardware format to the white rabbit format
*/
static void ft_timestamp_hw_to_wr(struct fmctdc_dev *ft,
struct ft_wr_timestamp *wrts,
struct ft_hw_timestamp *hwts)
{
__ft_timestamp_hw_to_wr(ft, wrts, hwts);
ft_timestamp_apply_offsets(ft, wrts);
} }
/** /**
* It puts the given timestamp in the ZIO control * It puts the given timestamp in the ZIO control
* @cset ZIO cset instant * @cset ZIO cset instant
* @wrts the timestamp to convert * @hwts the timestamp to convert
*/ */
static void ft_timestap_wr_to_zio(struct zio_cset *cset, static void ft_timestap_wr_to_zio(struct zio_cset *cset,
struct ft_wr_timestamp *wrts) struct ft_hw_timestamp *hwts)
{ {
struct zio_device *zdev = cset->zdev; struct zio_device *zdev = cset->zdev;
struct fmctdc_dev *ft = zdev->priv_d; struct fmctdc_dev *ft = zdev->priv_d;
struct zio_control *ctrl; struct zio_control *ctrl;
struct zio_ti *ti = cset->ti; struct zio_ti *ti = cset->ti;
uint32_t *v; uint32_t *v;
struct ft_wr_timestamp ts = *wrts, *reflast; struct ft_hw_timestamp ts = *hwts, *reflast;
struct ft_channel_state *st; struct ft_channel_state *st;
dev_dbg(&ft->fmc->dev,
"Set in ZIO block ch %d: hseq %u: %llu %u %u\n",
ts.channel, ts.hseq_id, ts.seconds, ts.coarse, ts.frac);
st = &ft->channels[cset->index]; st = &ft->channels[cset->index];
ctrl = cset->chan->current_ctrl; ctrl = cset->chan->current_ctrl;
...@@ -160,7 +128,7 @@ static void ft_timestap_wr_to_zio(struct zio_cset *cset, ...@@ -160,7 +128,7 @@ static void ft_timestap_wr_to_zio(struct zio_cset *cset,
* Update last time stamp of the current channel, with the current * Update last time stamp of the current channel, with the current
* time-stamp * time-stamp
*/ */
memcpy(&st->last_ts, &ts, sizeof(struct ft_wr_timestamp)); memcpy(&st->last_ts, &ts, sizeof(struct ft_hw_timestamp));
/* /*
* If we are in delay mode, replace the time stamp with the delay from * If we are in delay mode, replace the time stamp with the delay from
...@@ -168,18 +136,18 @@ static void ft_timestap_wr_to_zio(struct zio_cset *cset, ...@@ -168,18 +136,18 @@ static void ft_timestap_wr_to_zio(struct zio_cset *cset,
*/ */
if (st->delay_reference) { if (st->delay_reference) {
reflast = &ft->channels[st->delay_reference - 1].last_ts; reflast = &ft->channels[st->delay_reference - 1].last_ts;
if (likely(ts.hseq_id > reflast->hseq_id)) { if (likely(FT_HW_TS_META_SEQ(ts.metadata) > FT_HW_TS_META_SEQ(reflast->metadata))) {
ft_ts_sub(&ts, reflast); ft_ts_sub(&ts, reflast);
v[FT_ATTR_TDC_DELAY_REF_SEQ] = reflast->hseq_id; v[FT_ATTR_TDC_DELAY_REF_SEQ] = FT_HW_TS_META_SEQ(reflast->metadata);
} else { } else {
/* /*
* It seems that we are not able to compute the delay. * It seems that we are not able to compute the delay.
* Inform the user by setting the time stamp to 0 * Inform the user by setting the time stamp to 0
*/ */
memset(&ts, 0, sizeof(struct ft_wr_timestamp)); memset(&ts, 0, sizeof(struct ft_hw_timestamp));
} }
} else { } else {
v[FT_ATTR_TDC_DELAY_REF_SEQ] = ts.hseq_id; v[FT_ATTR_TDC_DELAY_REF_SEQ] = FT_HW_TS_META_SEQ(ts.metadata);
} }
/* Write the timestamp in the trigger, it will reach the control */ /* Write the timestamp in the trigger, it will reach the control */
...@@ -189,13 +157,13 @@ static void ft_timestap_wr_to_zio(struct zio_cset *cset, ...@@ -189,13 +157,13 @@ static void ft_timestap_wr_to_zio(struct zio_cset *cset,
/* Synchronize ZIO sequence number with ours (ZIO does +1 on this) */ /* Synchronize ZIO sequence number with ours (ZIO does +1 on this) */
ctrl->seq_num = ts.hseq_id - 1; ctrl->seq_num = FT_HW_TS_META_SEQ(ts.metadata) - 1;
v[FT_ATTR_TDC_ZERO_OFFSET] = ft->calib.zero_offset[cset->index]; v[FT_ATTR_TDC_ZERO_OFFSET] = ft->calib.zero_offset[cset->index];
v[FT_ATTR_TDC_USER_OFFSET] = st->user_offset; v[FT_ATTR_TDC_USER_OFFSET] = st->user_offset;
if (cset->chan->active_block) { if (cset->chan->active_block) {
memcpy(cset->chan->active_block->data, wrts, memcpy(cset->chan->active_block->data, hwts,
ctrl->nsamples * ctrl->ssize); ctrl->nsamples * ctrl->ssize);
} }
} }
...@@ -282,7 +250,6 @@ static void ft_readout_dma_start(struct fmctdc_dev *ft, int channel) ...@@ -282,7 +250,6 @@ static void ft_readout_dma_start(struct fmctdc_dev *ft, int channel)
struct ft_channel_state *st = &ft->channels[channel]; struct ft_channel_state *st = &ft->channels[channel];
uint32_t base_cur; uint32_t base_cur;
struct ft_hw_timestamp *dma_buf; struct ft_hw_timestamp *dma_buf;
struct ft_wr_timestamp wrts;
const int ts_per_page = PAGE_SIZE / TDC_BYTES_PER_TIMESTAMP; const int ts_per_page = PAGE_SIZE / TDC_BYTES_PER_TIMESTAMP;
unsigned int count, transfer; unsigned int count, transfer;
struct zio_cset *cset; struct zio_cset *cset;
...@@ -309,18 +276,18 @@ static void ft_readout_dma_start(struct fmctdc_dev *ft, int channel) ...@@ -309,18 +276,18 @@ static void ft_readout_dma_start(struct fmctdc_dev *ft, int channel)
/* gn4124_dma_read(ft->fmc, 0, dma_buf, 16); */ /* gn4124_dma_read(ft->fmc, 0, dma_buf, 16); */
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
ft_timestamp_hw_to_wr(ft, &wrts, &dma_buf[i]); ft_timestamp_apply_offsets(ft, &dma_buf[i]);
dev_info(&ft->fmc->dev, "Ts %x %x %x %x - %llx %x %x\n",
dma_buf[i].utc, dma_buf[i].coarse, dev_info(&ft->fmc->dev, "Ts %x %x %x %x\n",
dma_buf[i].frac, dma_buf[i].metadata, dma_buf[i].seconds, dma_buf[i].coarse,
wrts.seconds, wrts.coarse, wrts.frac); dma_buf[i].frac, dma_buf[i].metadata);
if (!(ZIO_TI_ARMED & cset->ti->flags)) { if (!(ZIO_TI_ARMED & cset->ti->flags)) {
dev_warn(&cset->head.dev, dev_warn(&cset->head.dev,
"Time stamp lost, trigger was not armed\n"); "Time stamp lost, trigger was not armed\n");
break; break;
} }
/* there is an active block, store data there */ /* there is an active block, store data there */
ft_timestap_wr_to_zio(cset, &wrts); ft_timestap_wr_to_zio(cset, &dma_buf[i]);
zio_trigger_data_done(cset); zio_trigger_data_done(cset);
} }
...@@ -392,10 +359,9 @@ static void ft_readout_fifo_one(struct zio_cset *cset) ...@@ -392,10 +359,9 @@ static void ft_readout_fifo_one(struct zio_cset *cset)
{ {
struct fmctdc_dev *ft = cset->zdev->priv_d; struct fmctdc_dev *ft = cset->zdev->priv_d;
struct ft_hw_timestamp hwts; struct ft_hw_timestamp hwts;
struct ft_wr_timestamp wrts;
ft_timestap_get(cset, &hwts, 0); ft_timestap_get(cset, &hwts, 0);
ft_timestamp_hw_to_wr(ft, &wrts, &hwts); ft_timestamp_apply_offsets(ft, &hwts);
if (!(ZIO_TI_ARMED & cset->ti->flags)) { if (!(ZIO_TI_ARMED & cset->ti->flags)) {
dev_warn(&cset->head.dev, dev_warn(&cset->head.dev,
...@@ -403,7 +369,7 @@ static void ft_readout_fifo_one(struct zio_cset *cset) ...@@ -403,7 +369,7 @@ static void ft_readout_fifo_one(struct zio_cset *cset)
return; /* Nothing to do, ZIO was not ready */ return; /* Nothing to do, ZIO was not ready */
} }
/* there is an active block, store data there */ /* there is an active block, store data there */
ft_timestap_wr_to_zio(cset, &wrts); ft_timestap_wr_to_zio(cset, &hwts);
zio_trigger_data_done(cset); zio_trigger_data_done(cset);
} }
......
...@@ -19,14 +19,14 @@ ...@@ -19,14 +19,14 @@
/* WARNING: the seconds register name is a bit misleading - it is not UTC time /* WARNING: the seconds register name is a bit misleading - it is not UTC time
as the core is not aware of leap seconds, making it TAI time. */ as the core is not aware of leap seconds, making it TAI time. */
void ft_ts_from_picos(uint32_t picos, struct ft_wr_timestamp *result) void ft_ts_from_picos(uint32_t picos, struct ft_hw_timestamp *result)
{ {
result->frac = (picos % 8000) * 4096 / 8000; result->frac = (picos % 8000) * 4096 / 8000;
result->coarse = (picos / 8000); result->coarse = (picos / 8000);
result->seconds = 0; result->seconds = 0;
} }
void ft_ts_add(struct ft_wr_timestamp *a, struct ft_wr_timestamp *b) void ft_ts_add(struct ft_hw_timestamp *a, struct ft_hw_timestamp *b)
{ {
a->frac += b->frac; a->frac += b->frac;
...@@ -45,7 +45,7 @@ void ft_ts_add(struct ft_wr_timestamp *a, struct ft_wr_timestamp *b) ...@@ -45,7 +45,7 @@ void ft_ts_add(struct ft_wr_timestamp *a, struct ft_wr_timestamp *b)
a->seconds += b->seconds; a->seconds += b->seconds;
} }
void ft_ts_sub(struct ft_wr_timestamp *a, struct ft_wr_timestamp *b) void ft_ts_sub(struct ft_hw_timestamp *a, struct ft_hw_timestamp *b)
{ {
int32_t d_frac, d_coarse = 0; int32_t d_frac, d_coarse = 0;
...@@ -68,9 +68,9 @@ void ft_ts_sub(struct ft_wr_timestamp *a, struct ft_wr_timestamp *b) ...@@ -68,9 +68,9 @@ void ft_ts_sub(struct ft_wr_timestamp *a, struct ft_wr_timestamp *b)
a->seconds -= b->seconds; a->seconds -= b->seconds;
} }
void ft_ts_apply_offset(struct ft_wr_timestamp *ts, int32_t offset_picos) void ft_ts_apply_offset(struct ft_hw_timestamp *ts, int32_t offset_picos)
{ {
struct ft_wr_timestamp offset_ts; struct ft_hw_timestamp offset_ts;
ft_ts_from_picos(offset_picos < 0 ? -offset_picos : offset_picos, ft_ts_from_picos(offset_picos < 0 ? -offset_picos : offset_picos,
&offset_ts); &offset_ts);
......
...@@ -320,7 +320,7 @@ static struct zio_channel ft_chan_tmpl = { ...@@ -320,7 +320,7 @@ static struct zio_channel ft_chan_tmpl = {
.raw_io = ft_zio_input,\ .raw_io = ft_zio_input,\
.chan_template = &ft_chan_tmpl,\ .chan_template = &ft_chan_tmpl,\
.n_chan = 1,\ .n_chan = 1,\
.ssize = sizeof(struct ft_wr_timestamp), \ .ssize = sizeof(struct ft_hw_timestamp), \
.flags = ZIO_DISABLED | \ .flags = ZIO_DISABLED | \
ZIO_DIR_INPUT | \ ZIO_DIR_INPUT | \
ZIO_CSET_SELF_TIMED, \ ZIO_CSET_SELF_TIMED, \
......
...@@ -602,6 +602,14 @@ int fmctdc_read_last(struct fmctdc_board *userb, unsigned int channel, ...@@ -602,6 +602,14 @@ int fmctdc_read_last(struct fmctdc_board *userb, unsigned int channel,
} }
static void fmctdc_ts_convert(struct fmctdc_time *t, struct ft_hw_timestamp *o)
{
t->seconds = o->seconds;
t->coarse = o->coarse;
t->frac = o->frac;
t->seq_id = FT_HW_TS_META_SEQ(o->metadata);
}
/** /**
* It reads a given number of time-stamps from the driver. It will wait at * It reads a given number of time-stamps from the driver. It will wait at
* most once and return the number of samples that it received from a given * most once and return the number of samples that it received from a given
...@@ -636,7 +644,7 @@ int fmctdc_read(struct fmctdc_board *userb, unsigned int channel, ...@@ -636,7 +644,7 @@ int fmctdc_read(struct fmctdc_board *userb, unsigned int channel,
uint32_t *attrs; uint32_t *attrs;
int i, j; int i, j;
fd_set set; fd_set set;
struct ft_wr_timestamp data; struct ft_hw_timestamp data;
if (channel >= FMCTDC_NUM_CHANNELS) { if (channel >= FMCTDC_NUM_CHANNELS) {
errno = EINVAL; errno = EINVAL;
...@@ -649,20 +657,16 @@ int fmctdc_read(struct fmctdc_board *userb, unsigned int channel, ...@@ -649,20 +657,16 @@ int fmctdc_read(struct fmctdc_board *userb, unsigned int channel,
return -1; return -1;
if (j == sizeof(ctrl)) { if (j == sizeof(ctrl)) {
/* one sample: pick it */ /* one sample: pick it */
attrs = ctrl.attr_channel.ext_val;
t[i].seconds = ctrl.tstamp.secs;
t[i].coarse = ctrl.tstamp.ticks;
t[i].frac = ctrl.tstamp.bins;
t[i].seq_id = ctrl.seq_num;
t[i].ref_gseq_id = attrs[FT_ATTR_TDC_DELAY_REF_SEQ];
i++;
assert(sizeof(data) == ctrl.nsamples * ctrl.ssize); assert(sizeof(data) == ctrl.nsamples * ctrl.ssize);
if (sizeof(data) == ctrl.nsamples * ctrl.ssize) { if (sizeof(data) == ctrl.nsamples * ctrl.ssize) {
j = read(b->fdd[channel], &data, j = read(b->fdd[channel], &data,
ctrl.nsamples * ctrl.ssize); ctrl.nsamples * ctrl.ssize);
if (j == ctrl.nsamples * ctrl.ssize) if (j == ctrl.nsamples * ctrl.ssize) {
fmctdc_ts_convert(&t[i], &data);
attrs = ctrl.attr_channel.ext_val;
t[i].ref_gseq_id = attrs[FT_ATTR_TDC_DELAY_REF_SEQ];
continue; /* Everything is fine */ continue; /* Everything is fine */
}
} }
errno = EIO; errno = EIO;
......
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