Commit 58e29b61 authored by Miguel Jimenez Lopez's avatar Miguel Jimenez Lopez

sw: First version for the fmc-dio driver and related tools.

parent a76d4009
# include parent_common.mk for buildsystem's defines
#use absolute path for REPO_PARENT
REPO_PARENT=$(shell /bin/pwd)/..
-include $(REPO_PARENT)/parent_common.mk
# export git tree for subdirs
GIT_VERSION ?= $(shell git describe --dirty --long --tags)
export GIT_VERSION
# by default use the fmc-bus within the repository
FMC_BUS ?= fmc-bus
# FMC_BUS_ABS has to be absolut path, due to beeing passed to the Kbuild
FMC_BUS_ABS ?= $(abspath $(FMC_BUS) )
export FMC_BUS_ABS
DIRS = $(FMC_BUS_ABS) kernel tools
.PHONY: all clean modules install modules_install $(DIRS)
all clean modules install modules_install: $(DIRS)
clean: TARGET = clean
modules: TARGET = modules
install: TARGET = install
modules_install: TARGET = modules_install
$(DIRS):
$(MAKE) -C $@ $(TARGET)
$(FMC_BUS_ABS): fmc-bus-init_repo
# init submodule if missing
fmc-bus-init_repo:
@test -d $(FMC_BUS_ABS)/doc || ( echo "Checking out submodule $(FMC_BUS_ABS)"; git submodule update --init $(FMC_BUS_ABS) )
kernel: $(FMC_BUS_ABS)
KBUILD_EXTRA_SYMBOLS := $(FMC_BUS_ABS)/kernel/Module.symvers
# add versions of supermodule. It is useful when spec-sw is included as sub-module
# of a bigger project that we want to track
ifdef CONFIG_SUPER_REPO
ifdef CONFIG_SUPER_REPO_VERSION
SUBMODULE_VERSIONS += MODULE_INFO(version_$(CONFIG_SUPER_REPO),\"$(CONFIG_SUPER_REPO_VERSION)\");
endif
endif
# add versions of used submodules
SUBMODULE_VERSIONS += MODULE_INFO(version_fmc_bus,\"$(FMC_BUS_VERSION)\");
ccflags-y += -DADDITIONAL_VERSIONS="$(SUBMODULE_VERSIONS)"
# The library includes <sdb.h>, so point -I directtly there
# include our header before to avoid conflicts with the kernel
LINUXINCLUDE := -I$(FMC_BUS_ABS)/kernel/include -I$(src)/include/linux $(LINUXINCLUDE)
ccflags-y += -I$(src)/include
ccflags-y += -DGIT_VERSION=\"$(GIT_VERSION)\"
obj-m += wr-dio.o
wr-dio-y = fmc-dio.o
wr-dio-y += fmc-dio-internal.o
wr-dio-y += fmc-dio-mdev.o
wr-dio-y += fmc-dio-gpio.o
\ No newline at end of file
# include parent_common.mk for buildsystem's defines
#use absolute path for REPO_PARENT
REPO_PARENT=$(shell /bin/pwd)/../..
-include $(REPO_PARENT)/parent_common.mk
LINUX ?= /lib/modules/$(shell uname -r)/build
# by default use the fmc-bus within the repository
FMC_BUS ?= ../fmc-bus/
# FMC_BUS_ABS has to be absolut path, due to beeing passed to the Kbuild
FMC_BUS_ABS ?= $(abspath $(FMC_BUS) )
GIT_VERSION ?= $(shell git describe --dirty --long --tags)
export GIT_VERSION
FMC_BUS_VERSION ?= $(shell cd $(FMC_BUS_ABS); git describe --always --dirty --long --tags)
export FMC_BUS_VERSION
all modules:
$(MAKE) -C $(LINUX) M=$(shell /bin/pwd) FMC_BUS_ABS=$(FMC_BUS_ABS) modules
install modules_install:
$(MAKE) -C $(LINUX) M=$(shell /bin/pwd) modules_install
# be able to run the "clean" rule even if $(LINUX) is not valid
clean:
rm -rf *.o *~ .*.cmd *.ko *.mod.c .tmp_versions Module.symvers \
Module.markers modules.order
\ No newline at end of file
/*
* Copyright (C) 2019 CERN (www.sevensols.com)
* Author: Miguel Jimenez Lopez <miguel.jimenez@sevensols.com>
*
* Released according to the GNU GPL, version 2 or any later version.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/slab.h>
#include <linux/fmc.h>
#include "fmc-dio.h"
static int fmc_dio_gpio_input(struct gpio_chip *chip, unsigned offset)
{
return -EAGAIN;
}
static int fmc_dio_gpio_output(struct gpio_chip *chip, unsigned offset, int value)
{
return -EAGAIN;
}
int fmc_dio_gpio_get(struct gpio_chip *chip, unsigned offset)
{
return -EAGAIN;
}
void fmc_dio_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
return;
}
static const char *fmc_dio_gpio_names[] = {
"dire", "fare", "baciare", "lettera", "testamento"
};
static struct gpio_chip fmc_dio_gpio_template = {
.label = "fmc-dio",
.owner = THIS_MODULE,
/* FIXME: request, free, for multi-function operation */
.direction_input = fmc_dio_gpio_input,
.direction_output = fmc_dio_gpio_output,
.get = fmc_dio_gpio_get,
.set = fmc_dio_gpio_set,
.base = -1, /* request dynamic */
.ngpio = 5,
.names = fmc_dio_gpio_names,
};
int fmc_dio_gpio_init(struct fmc_dio *dev)
{
struct fmc_device *fmc = dev->fmc;
struct gpio_chip *gc;
int ret;
gc = devm_kzalloc(&dev->fmc->dev, sizeof(*gc), GFP_KERNEL);
if (!gc)
return -ENOMEM;
*gc = fmc_dio_gpio_template;
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,5,0)
gc->dev = &fmc->dev;
#else
gc->parent = &fmc->dev;
#endif
ret = gpiochip_add(gc);
if (ret < 0)
goto out_free;
dev->gc = gc;
/* FIXME: program the DAC for each port (sysfs attributes?) */
return 0;
out_free:
kfree(gc);
return ret;
}
void fmc_dio_gpio_exit(struct fmc_dio *dev)
{
struct gpio_chip *gc = dev->gc;
#if LINUX_VERSION_CODE > KERNEL_VERSION(3,17,0)
gpiochip_remove(gc);
#else
int ret;
ret = gpiochip_remove(gc);
if (ret)
dev_err(&dio->pdev->dev, "DANGER %i! gpio chip can't be removed\n",
ret);
#endif
return;
}
/*
* Copyright (C) 2019 CERN (www.sevensols.com)
* Author: Miguel Jimenez Lopez <miguel.jimenez@sevensols.com>
*
* Released according to the GNU GPL, version 2 or any later version.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#include <linux/module.h>
#include <linux/version.h>
#if KERNEL_VERSION(4,11,0) > LINUX_VERSION_CODE
#include <linux/sched.h>
#else
#include <linux/sched/signal.h>
#endif
#include <linux/slab.h>
#include <linux/wait.h>
#include <linux/ktime.h>
#include <linux/atomic.h>
#include <linux/platform_device.h>
#include <linux/uaccess.h>
#include <linux/fmc.h>
#include <linux/fmc-sdb.h>
#include "fmc-dio.h"
#ifdef DIO_STAT
#define fmc_dio_stat 1
#else
#define fmc_dio_stat 0
#endif
/* We need a clear mapping for the registers of the various bits */
struct regmap {
int trig_l;
int trig_h;
int cycle;
int pulse;
int fifo_tai_l;
int fifo_tai_h;
int fifo_cycle;
int fifo_status;
};
#define R(x) (offsetof(struct DIO_WB, x))
static struct regmap regmap[] = {
{
.trig_l = R(TRIG0),
.trig_h = R(TRIGH0),
.cycle = R(CYC0),
.pulse = R(PROG0_PULSE),
.fifo_tai_l = R(TSF0_R0),
.fifo_tai_h = R(TSF0_R1),
.fifo_cycle = R(TSF0_R2),
.fifo_status = R(TSF0_CSR),
}, {
.trig_l = R(TRIG1),
.trig_h = R(TRIGH1),
.cycle = R(CYC1),
.pulse = R(PROG1_PULSE),
.fifo_tai_l = R(TSF1_R0),
.fifo_tai_h = R(TSF1_R1),
.fifo_cycle = R(TSF1_R2),
.fifo_status = R(TSF1_CSR),
}, {
.trig_l = R(TRIG2),
.trig_h = R(TRIGH2),
.cycle = R(CYC2),
.pulse = R(PROG2_PULSE),
.fifo_tai_l = R(TSF2_R0),
.fifo_tai_h = R(TSF2_R1),
.fifo_cycle = R(TSF2_R2),
.fifo_status = R(TSF2_CSR),
}, {
.trig_l = R(TRIG3),
.trig_h = R(TRIGH3),
.cycle = R(CYC3),
.pulse = R(PROG3_PULSE),
.fifo_tai_l = R(TSF3_R0),
.fifo_tai_h = R(TSF3_R1),
.fifo_cycle = R(TSF3_R2),
.fifo_status = R(TSF3_CSR),
}, {
.trig_l = R(TRIG4),
.trig_h = R(TRIGH4),
.cycle = R(CYC4),
.pulse = R(PROG4_PULSE),
.fifo_tai_l = R(TSF4_R0),
.fifo_tai_h = R(TSF4_R1),
.fifo_cycle = R(TSF4_R2),
.fifo_status = R(TSF4_CSR),
}
};
#define FMC_DIO_IRQ_MASK \
(DIO_EIC_ISR_NEMPTY_0 \
| DIO_EIC_ISR_NEMPTY_1 \
| DIO_EIC_ISR_NEMPTY_2 \
| DIO_EIC_ISR_NEMPTY_3\
| DIO_EIC_ISR_NEMPTY_4)
/* This is the structure we need to manage interrupts and loop internally */
#define FMC_DIO_BUFFER_LEN 512
struct dio_channel {
struct timespec tsbuf[FMC_DIO_BUFFER_LEN];
int bhead, btail;
wait_queue_head_t q;
/* The input event may fire a new pulse on this or another channel */
struct timespec prevts, delay;
atomic_t count;
int target_channel;
};
struct dio_device {
struct dio_channel ch[5];
};
/* Instead of timespec_sub, just subtract the nanos */
static inline void fmc_dio_int_ts_sub(struct timespec *ts, int nano)
{
set_normalized_timespec(ts, ts->tv_sec, ts->tv_nsec - nano);
}
/* This programs a new pulse without changing the width */
static void __fmc_dio_int_new_pulse(struct fmc_dio *dev, int ch,
struct timespec *ts)
{
struct DIO_WB __iomem *dio = dev->dio;
void __iomem *base = dio;
struct regmap *map;
map = regmap + ch;
fmc_dio_int_ts_sub(ts, 8); /* 1 cycle, to account for output latencies */
writel(ts->tv_nsec / 8, base + map->cycle);
writel(GET_HI32(ts->tv_sec), base + map->trig_h);
writel(ts->tv_sec, base + map->trig_l);
writel(1 << ch, &dio->R_LATCH);
}
static int fmc_dio_int_cmd_pulse(struct fmc_dio *dev,
struct wr_dio_cmd *cmd)
{
struct DIO_WB __iomem *dio = dev->dio;
void __iomem *base = dio;
struct PPSG_WB __iomem *ppsg = dev->ppsg;
struct dio_device *d = dev->priv;
struct dio_channel *c;
struct regmap *map;
struct timespec *ts;
uint32_t reg;
int ch;
ch = cmd->channel;
if (ch > 4)
return -EINVAL; /* mask not supported */
c = d->ch + ch;
map = regmap + ch;
ts = cmd->t;
/* First, configure this bit as DIO output */
reg = readl(&dio->IOMODE);
writel(reg | (1 << 4*ch), &dio->IOMODE);
writel(ts[1].tv_nsec / 8, base + map->pulse); /* width */
if (cmd->flags & WR_DIO_F_NOW) {
/* if "now" we are done */
writel(1 << ch, &dio->PULSE);
return 0;
}
/* if relative, add current 40-bit second to timespec */
if (cmd->flags & WR_DIO_F_REL) {
uint32_t h1, l, h2;
unsigned long now;
h1 = readl(&ppsg->CNTR_UTCHI);
l = readl(&ppsg->CNTR_UTCLO);
h2 = readl(&ppsg->CNTR_UTCHI);
if (h2 != h1)
l = readl(&ppsg->CNTR_UTCLO);
now = l;
SET_HI32(now, h2);
ts->tv_sec += now;
}
if (cmd->flags & WR_DIO_F_LOOP) {
c->target_channel = ch;
/* c->count is used after the pulse, so remove the first */
if (cmd->value > 0)
cmd->value--;
atomic_set(&c->count, cmd->value);
c->prevts = ts[0]; /* our current setpoint */
c->delay = ts[2];
}
__fmc_dio_int_new_pulse(dev, ch, ts);
return 0;
}
static int fmc_dio_int_cmd_stamp(struct fmc_dio *dev,
struct wr_dio_cmd *cmd)
{
struct dio_device *d = dev->priv;
struct dio_channel *c = 0;
struct timespec *ts = cmd->t;
struct regmap *map;
int mask, ch, last;
int nstamp = 0;
if ((cmd->flags & (WR_DIO_F_MASK || WR_DIO_F_WAIT))
== (WR_DIO_F_MASK || WR_DIO_F_WAIT))
return -EINVAL; /* wait on several channels not supported */
again:
if (cmd->flags & WR_DIO_F_MASK) {
ch = 0;
last = 4;
mask = cmd->channel;
} else {
ch = cmd->channel;
last = ch;
mask = (1 << ch);
}
/* handle the 1-channel and mask case in the same loop */
c = d->ch + ch;
for (; ch <= last; ch++, c++) {
if (((1 << ch) & mask) == 0)
continue;
map = regmap + ch;
while (1) {
if (nstamp == WR_DIO_N_STAMP)
break;
if (c->bhead == c->btail)
break;
*ts = c->tsbuf[c->btail];
c->btail = (c->btail + 1) % FMC_DIO_BUFFER_LEN;
nstamp++;
ts++;
}
if (nstamp) {
cmd->channel = ch;
break;
}
}
cmd->nstamp = nstamp;
/* The user may asketo wait for timestamps, but for 1 channel only */
if (!nstamp && cmd->flags & WR_DIO_F_WAIT) {
ch--; c--; /* The for above incremeted them */
try_module_get(THIS_MODULE);
wait_event_interruptible(c->q, c->bhead != c->btail);
module_put(THIS_MODULE);
if (signal_pending(current))
return -ERESTARTSYS;
goto again;
}
if (!nstamp)
return -EAGAIN;
return 0;
}
static int fmc_dio_int_cmd_inout(struct fmc_dio *dev,
struct wr_dio_cmd *cmd)
{
struct DIO_WB __iomem *dio = dev->dio;
struct fmc_dio_gpio_block __iomem *gpio = dev->gpio;
int mask, ch, last, bits;
uint32_t reg, iomode;
if (cmd->flags & WR_DIO_F_MASK) {
ch = 0;
last = 4;
mask = cmd->channel;
} else {
ch = cmd->channel;
last = ch;
mask = (1 << ch);
cmd->value <<= ch;
}
/* handle the 1-channel and mask case in the same loop */
for (; ch <= last; ch++) {
if (((1 << ch) & mask) == 0)
continue;
/* select the bits by shifting back the value field */
bits = cmd->value >> ch;
/* Obtain the current value in iomode */
reg = readl(&dio->IOMODE) & ~(0xF << 4*ch);
/* Select IO mode */
if (bits & WR_DIO_INOUT_DIO) {
if(bits & WR_DIO_INOUT_VALUE)
iomode = 2; /* WRPC connection */
else
iomode = 1; /* DIO connection */
} else {
iomode = 0; /* GPIO connection */
/* Output value is bit 0 (0x1) */
if (bits & WR_DIO_INOUT_VALUE)
writel(FMC_DIO_GPIO_VALUE(ch), &gpio->set);
else
writel(FMC_DIO_GPIO_VALUE(ch), &gpio->clear);
}
/* Appends to iomode TERM and OUTPUT_ENABLE_N bits */
iomode |= (((bits & WR_DIO_INOUT_TERM) != 0) << 3)
| (((bits & WR_DIO_INOUT_OUTPUT) == 0) << 2);
writel(reg | (iomode << 4*ch), &dio->IOMODE);
}
return 0;
}
int fmc_dio_int_ioctl(struct fmc_dio *dev, unsigned int ioctlcmd,
unsigned long arg)
{
struct wr_dio_cmd *cmd;
struct fmc_device *fmc = dev->fmc;
ktime_t t, t0;
int ret;
if (ioctlcmd == PRIV_MEZZANINE_ID)
return -EAGAIN; /* Special marker */
if (ioctlcmd != PRIV_MEZZANINE_CMD)
return -ENOIOCTLCMD;
if (fmc_dio_stat) {
t0 = ktime_get();
}
/* The cmd struct can't fit in the stack, so allocate it */
cmd = kmalloc(sizeof(*cmd), GFP_KERNEL);
if (!cmd)
return -ENOMEM;
ret = -EFAULT;
if (copy_from_user(cmd, (struct wr_dio_cmd *) arg, sizeof(*cmd)))
goto out;
switch(cmd->command) {
case WR_DIO_CMD_PULSE:
ret = fmc_dio_int_cmd_pulse(dev, cmd);
break;
case WR_DIO_CMD_STAMP:
ret = fmc_dio_int_cmd_stamp(dev, cmd);
break;
case WR_DIO_CMD_INOUT:
ret = fmc_dio_int_cmd_inout(dev, cmd);
break;
case WR_DIO_CMD_DAC:
ret = -ENOTSUPP;
goto out;
default:
ret = -EINVAL;
goto out;
}
if (copy_to_user((struct wr_dio_cmd *)arg, cmd, sizeof(*cmd)))
return -EFAULT;
out:
kfree(cmd);
if (fmc_dio_stat) {
t = ktime_sub(ktime_get(), t0);
dev_info(fmc->hwdev, "ioctl: %li ns\n", (long)ktime_to_ns(t));
}
return ret;
}
/* This is called from the interrupt handler to program a new pulse */
static void fmc_dio_int_trig_next_pulse(struct fmc_dio *dev, int ch,
struct dio_channel *c, struct timespec *ts)
{
struct timespec newts;
if (c->target_channel == ch) {
c->prevts = timespec_add(c->prevts, c->delay);
newts = c->prevts;
} else {
newts = timespec_add(*ts, c->delay);
}
__fmc_dio_int_new_pulse(dev, c->target_channel, &newts);
/* If the count is not-infinite, decrement it */
if (atomic_read(&c->count) > 0)
atomic_dec(&c->count);
}
irqreturn_t fmc_dio_int_interrupt(struct fmc_dio *dev)
{
struct DIO_WB __iomem *dio = dev->dio;
struct fmc_device *fmc = dev->fmc;
void __iomem *base = dev->dio;
struct dio_device *d = dev->priv;
static ktime_t t_ini, t_end;
static int rate_avg;
struct dio_channel *c;
struct timespec *ts;
struct regmap *map;
uint32_t mask, reg;
int ch, chm;
if (unlikely(!fmc->eeprom)) {
dev_err(fmc->hwdev, "WR-DIO: No mezzanine, disabling irqs\n");
writel(~0, &dio->EIC_IDR);
writel(~0, &dio->EIC_ISR);
return IRQ_NONE;
}
/* Protect against interrupts taking 100% of cpu time */
if (ktime_to_ns(t_end)) {
int rate;
u64 offtime, ontime;
ontime = ktime_to_ns(t_end) - ktime_to_ns(t_ini);
t_ini = ktime_get();
offtime = ktime_to_ns(t_ini) - ktime_to_ns(t_end);
/* avoid __udivdi3 */
if (offtime > 100 * ontime)
rate = 0;
else
rate = ((int)ontime * 100) / (int)offtime;
rate_avg = (rate_avg * 1023 + rate) / 1024;
if (rate_avg > 80) {
dev_warn(fmc->hwdev, "DIO irq takes > 80%% CPU time: "
"disabling\n");
disable_irq(dev->irq);
}
}
mask = readl(&dio->EIC_ISR) & FMC_DIO_IRQ_MASK;
/* Three indexes: channel, channel-mask, channel pointer */
for (ch = 0, chm = 1, c = d->ch; mask; ch++, chm <<= 1, c++) {
int h;
if (!(mask & chm))
continue;
mask &= ~chm;
/* Pull the FIFOs to the device structure */
map = regmap + ch;
ts = NULL;
while (1) {
reg = readl(base + map->fifo_status);
if (reg & 0x20000) /* empty */
break;
h = c->bhead;
ts = c->tsbuf + h;
c->bhead = (h + 1) % FMC_DIO_BUFFER_LEN;
if (c->bhead == c->btail)
c->btail = (c->btail + 1) % FMC_DIO_BUFFER_LEN;
/*
* fifo is not-empty, pick one sample. Read
* cycles last, as that operation pops the FIFO
*/
ts->tv_sec = 0;
SET_HI32(ts->tv_sec, readl(base + map->fifo_tai_h));
ts->tv_sec |= readl(base + map->fifo_tai_l);
ts->tv_nsec = 8 * readl(base + map->fifo_cycle);
/* subtract 5 cycles lost in input sync circuits */
fmc_dio_int_ts_sub(ts, 40);
}
writel(chm, &dio->EIC_ISR); /* ack */
if (ts && atomic_read(&c->count) != 0) {
fmc_dio_int_trig_next_pulse(dev, ch, c, ts);
}
wake_up_interruptible(&c->q);
}
t_end = ktime_get();
return IRQ_HANDLED;
}
/* Init and exit below are called when a netdevice is created/destroyed */
int fmc_dio_internal_create(struct fmc_dio *dev)
{
struct DIO_WB __iomem *dio = dev->dio;
struct dio_device *d;
int i;
/* Allocate the data structure and enable interrupts for stamping */
d = kzalloc(sizeof(*d), GFP_KERNEL);
if (!d)
return -ENOMEM;
for (i = 0; i < ARRAY_SIZE(d->ch); i++)
init_waitqueue_head(&d->ch[i].q);
dev->priv = d;
/*
* Enable interrupts for FIFO, if there's no mezzanine the
* handler will notice and disable the interrupts
*/
writel(FMC_DIO_IRQ_MASK, &dio->EIC_IER);
return 0;
}
void fmc_dio_internal_destroy(struct fmc_dio *dev)
{
struct DIO_WB __iomem *dio = dev->dio;
writel(~0, &dio->EIC_IDR);
if (dev->priv)
kfree(dev->priv);
}
/*
* Copyright (C) 2019 CERN (www.sevensols.com)
* Author: Miguel Jimenez Lopez <miguel.jimenez@sevensols.com>
*
* Released according to the GNU GPL, version 2 or any later version.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#include <linux/module.h>
#include <linux/version.h>
#include "fmc-dio.h"
static int fmc_dio_mdev_open(struct inode *inode, struct file *file)
{
struct miscdevice *mdev_ptr = file->private_data;
// WARN: Update the private_data pointer to point to fmc_dio structure
file->private_data = container_of(mdev_ptr, struct fmc_dio, mdev);
return 0;
}
static int fmc_dio_mdev_release(struct inode *inode, struct file *file)
{
return 0;
}
static ssize_t fmc_dio_mdev_write(struct file *file, const char __user *buf,
size_t count, loff_t *offp)
{
return 0;
}
static ssize_t fmc_dio_mdev_read (struct file *file, char __user *buf,
size_t count, loff_t *offp)
{
return 0;
}
static long fmc_dio_mdev_unlocked_ioctl (struct file *file, unsigned int cmd,
unsigned long arg)
{
struct fmc_dio *dio = file->private_data;
return fmc_dio_int_ioctl(dio,cmd,arg);
}
static const struct file_operations fmc_dio_mdev_fops = {
.owner = THIS_MODULE,
.open = fmc_dio_mdev_open,
.release = fmc_dio_mdev_release,
.write = fmc_dio_mdev_write,
.read = fmc_dio_mdev_read,
.unlocked_ioctl = fmc_dio_mdev_unlocked_ioctl,
};
int fmc_dio_mdev_create(struct fmc_dio *dio)
{
dio->mdev.minor = MISC_DYNAMIC_MINOR;
dio->mdev.fops = &fmc_dio_mdev_fops;
dio->mdev.name = dio->name;
return misc_register(&dio->mdev);
}
void fmc_dio_mdev_destroy(struct fmc_dio *dio)
{
misc_deregister(&dio->mdev);
}
/*
* Copyright (C) 2019 Seven Solutions (www.sevensols.com)
* Author: Miguel Jimenez Lopez <miguel.jimenez@sevensols.com>
*
* Released according to the GNU GPL, version 2 or any later version.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/firmware.h>
#include <linux/delay.h>
#include <linux/fmc.h>
#include <linux/fmc-sdb.h>
#include "fmc-dio.h"
/* Temporary pointer to the last FMC DIO */
static struct fmc_dio *last_dio;
/**
* FMC DIO Interrupt Service Routing (ISR)
*
* @param irq IRQ number
* @param dev_id Device identifier for the ISR
*
* @return IRQ_HANDLED if interrupt was handled properly and IRQ_NONE otherwise
*/
irqreturn_t fmc_dio_interrupt(int irq, void *dev_id)
{
irqreturn_t ret;
ret = fmc_dio_int_interrupt(dev_id);
return ret;
}
// Macros for the memory-mapped hardware resources
#define FMC_DIO_RESOURCE_DIO 0
#define FMC_DIO_RESOURCE_GPIO 1
#define FMC_DIO_RESOURCE_PPSG 2
/**
* This function creates the hardware resources for the FMC DIO.
*
* @param dio FMC DIO device
*
* @return 0 if success and a negative error code otherwise
*/
static int fmc_dio_resource_map(struct fmc_dio *dio)
{
int i,err;
struct platform_device *dev = dio->pdev;
struct fmc_device *fmc = dio->fmc;
struct resource *r;
void __iomem *mem;
// Map the resources
for(i = 0 ; i < dev->num_resources ; i++) {
r = platform_get_resource(dev, IORESOURCE_MEM, i);
if (!r || !r->start)
continue;
mem = ioremap(r->start, r->end + 1 - r->start);
if (!mem) {
dev_err(&dev->dev, "Remap for res %i %pr failed\n",
i, r);
return -ENOMEM;
}
switch(i)
{
case FMC_DIO_RESOURCE_DIO:
dio->dio = mem;
break;
case FMC_DIO_RESOURCE_GPIO:
dio->gpio = mem;
break;
case FMC_DIO_RESOURCE_PPSG:
dio->ppsg = mem;
break;
default:
/* Ugh? What is this? */
break;
};
}
// If some hardware resource is not detected, fail
if(!dio->dio || !dio->gpio || !dio->ppsg) {
printk("%s: ERROR in FPGA base pointers initialization. \n",__func__);
return -EINVAL;
}
// Map the IRQ
dio->irq = platform_get_irq(dev,0);
if(dio->irq < 0) {
printk("%s: ERROR in FMC DIO IRQ extraction. \n",__func__);
return -EINVAL;
}
err = request_any_context_irq(dio->irq, fmc_dio_interrupt,
IRQF_TRIGGER_HIGH, dio->name, dio);
if(err < 0) {
printk("%s: ERROR in request IRQ (%d). \n",__func__,err);
return err;
}
return 0;
}
/**
* This function releases the hardware resources for the FMC DIO.
*
* @param dio FMC DIO device
*
* @return 0 if success and a negative error code otherwise
*/
static int fmc_dio_resource_release(struct fmc_dio *dio)
{
// Release IRQ for FMC DIO
free_irq(dio->irq, dio);
return 0;
}
/**
* FMC DIO probe function for the platform_device
*
* @param dev FMC DIO platform device
*
* @return 0 if success and a negative error code otherwise
*/
static int fmc_dio_pl_probe(struct platform_device *dev)
{
/* NOTE: fmc_device is created by the same driver than platform_device
* and it had filled the private data structure pointer of the
* platform_device before registering it.
*/
struct fmc_device *fmc;
struct fmc_dio *dio;
int ret = 0;
unsigned int devfn;
unsigned char busn;
dio = last_dio;
if(!dio) {
printk("%s: DIO internal structure is NULL \n",__func__);
ret = -EINVAL;
goto exit;
}
fmc = dio->fmc;
if(!fmc) {
printk("%s: FMC device is NULL \n",__func__);
ret = -EINVAL;
goto exit;
}
/* Set up the FMC DIO name for the Linux Miscdevice */
busn = (unsigned char) (fmc->device_id >> 8);
devfn = (unsigned int) (fmc->device_id & 0xFF);
snprintf(dio->name,FMC_DIO_NAME_MAX,
FMC_DIO_NAME_PATTERN,
busn,devfn);
// Specific board info
dio->board = dev->id_entry->driver_data;
dio->pdev = dev;
/* Map the resources for the FMC DIO */
ret = fmc_dio_resource_map(dio);
if(ret)
goto exit;
ret = fmc_dio_internal_create(dio);
if(ret)
goto exit;
/* Create a Linux Miscdevice for the FMC DIO */
ret = fmc_dio_mdev_create(dio);
if(ret)
goto exit;
/* Create Linux GPIO for the FMC DIO */
ret = fmc_dio_gpio_init(dio);
if(ret)
goto exit;
exit:
return ret;
}
/**
* FMC DIO remove function for the platform_device
*
* @param dev FMC DIO platform device
*
* @return 0 if success and a negative error code otherwise
*/
static int fmc_dio_pl_remove(struct platform_device *dev)
{
struct fmc_device *fmc;
struct fmc_dio *dio;
int ret = 0;
dio = last_dio;
fmc = dio->fmc;
/* Release Linux GPIO for the FMC DIO */
fmc_dio_gpio_exit(dio);
/* Destroy the Linux Miscdevice for the FMC DIO */
fmc_dio_mdev_destroy(dio);
/* Destroy internal DIO structure */
fmc_dio_internal_destroy(dio);
/* Release the resources for the FMC DIO */
ret = fmc_dio_resource_release(dio);
if(ret)
return ret;
return ret;
}
/* Forward declarations */
int fmc_dio_probe(struct fmc_device *fmc);
int fmc_dio_remove(struct fmc_device *fmc);
/**
* List of supported platform
*/
static const struct platform_device_id fmc_dio_id_table[] = {
{ /* SPEC compatible */
.name = "fmc-dio-spec",
.driver_data = FMC_DIO_BOARD_SPEC,
},
{},
};
// FMC DIO platform driver
static struct platform_driver fmc_dio_pl_drv = {
.probe = fmc_dio_pl_probe,
.remove = fmc_dio_pl_remove,
.id_table = fmc_dio_id_table,
/* No suspend or resume by now */
.driver = {
.name = "fmc-dio",
.owner = THIS_MODULE,
},
};
// FMC DIO FRU identifier
static struct fmc_fru_id fd_fru_id[] = {
{
.product_name = "FmcDio5cha",
},
};
// FMC DIO driver structure (FMC)
static struct fmc_driver fmc_dio_drv = {
.version = FMC_VERSION,
.driver.name = KBUILD_MODNAME,
.probe = fmc_dio_probe,
.remove = fmc_dio_remove,
.id_table = {
.fru_id = fd_fru_id,
.fru_id_nr = ARRAY_SIZE(fd_fru_id),
},
};
/**
* FMC DIO probe function
*
* This function initializes the FMC DIO data structures.
*
* @param fmc FMC DIO device
*
* @return 0 if success and a negative error code otherwise
*/
int fmc_dio_probe(struct fmc_device *fmc)
{
struct fmc_dio *dio;
int ret = 0;
/* Driver data */
dio = devm_kzalloc(&fmc->dev, sizeof(*dio), GFP_KERNEL);
if (!dio)
return -ENOMEM;
fmc_set_drvdata(fmc, dio);
dio->fmc = fmc;
/* FIXME: Recover the DIO internal structure pointer for a global temporary var */
last_dio = dio;
/* Now register the platform_driver */
ret = platform_driver_register(&fmc_dio_pl_drv);
if (ret < 0) {
fmc_driver_unregister(&fmc_dio_drv);
}
return ret;
}
/**
* FMC DIO remove function
*
* This function releases the FMC DIO data structures.
*
* @param fmc FMC DIO device
*
* @return 0 if success and a negative error code otherwise
*/
int fmc_dio_remove(struct fmc_device *fmc)
{
//struct fmc_dio *dio = fmc_get_drvdata(fmc);
/* Free FMC DIO internal structure */
/* NOTE: Here is not necessary to free the memory because
* we use devm_kmalloc in the probe function
*/
platform_driver_unregister(&fmc_dio_pl_drv);
return 0;
}
/**
* FMC DIO init function
*
* This function is the entry point for the FMC DIO driver.
*
* @return 0 if success and a negative error code otherwise
*/
static int fmc_dio_init(void)
{
int ret;
ret = fmc_driver_register(&fmc_dio_drv);
return ret;
}
/**
* FMC DIO init function
*
* This function is the exit point for the FMC DIO driver.
*
*/
static void fmc_dio_exit(void)
{
fmc_driver_unregister(&fmc_dio_drv);
}
module_init(fmc_dio_init);
module_exit(fmc_dio_exit);
/* If no gpio lib is there, this weak applies */
/**
* FMC DIO GPIO init function
*
* @param dev FMC DIO device
*
* @return 0 if success and a negative error code otherwise
*/
int __weak fmc_dio_gpio_init(struct fmc_dio *dev)
{
return 0;
}
/**
* FMC DIO GPIO exit function
*
* @param dev FMC DIO device
*/
void __weak fmc_dio_gpio_exit(struct fmc_dio *dev)
{
return;
}
MODULE_VERSION(GIT_VERSION);
MODULE_LICENSE("GPL");
/*
* Copyright (C) 2019 CERN (www.sevensols.com)
* Author: Miguel Jimenez Lopez <miguel.jimenez@sevensols.com>
*
* Released according to the GNU GPL, version 2 or any later version.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#ifdef __KERNEL__
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#include <linux/gpio.h>
#include <linux/irqreturn.h>
#include <linux/fmc.h>
//FIXME: Back hack to maintain the IOCTL command numbers
#include <linux/sockios.h>
#include "hw/wr-dio-regs.h"
#include "hw/ppsg-regs.h"
/* Forward declaration */
struct fmc_dio;
/* This is somehow generic, but I find no better place at this time */
#ifndef SET_HI32
# if BITS_PER_LONG > 32
# define SET_HI32(var, value) ((var) |= (long)(value) << 32)
# define GET_HI32(var) ((var) >> 32)
# else
# define SET_HI32(var, value) ((var) |= 0)
# define GET_HI32(var) 0
# endif
#endif
/* For GPIO we have no wb-gen header */
struct fmc_dio_gpio_block {
uint32_t clear;
uint32_t set;
uint32_t dir;
uint32_t status;
};
/* And this is our bit mapping */
#define FMC_DIO_GPIO_VALUE(bit) (1 << ((4 * (bit)) + 0))
/* FMC DIO name string pattern */
#define FMC_DIO_NAME_PATTERN "fmc-dio-%d:%d"
/* Size of the name field of the fmc_dio structure */
#define FMC_DIO_NAME_MAX 20
/* Board constants */
#define FMC_DIO_BOARD_SPEC 0
/**
* @brief FMC DIO structure
*
* This contains information regarding FMC DIO.
*/
struct fmc_dio {
char name[FMC_DIO_NAME_MAX]; /**< Name of the FMC DIO */
struct miscdevice mdev; /**< Linux Miscdevice for the FMC DIO */
struct platform_device *pdev; /**< Linux platform_device for the FMC DIO */
struct fmc_device *fmc; /** FMC device for the FMC DIO */
struct gpio_chip *gc; /**< Linux GPIO chip */
void __iomem *dio; /**< DIO IP block pointer */
void __iomem *gpio; /**< GPIO IP block pointer */
void __iomem *ppsg; /**< PPSG IP block pointer */
const char *irqdomain_name; /**< IRQ domain name */
int irq; /**< IRQ number for DIO FMC */
int board; /**< Board info */
void *priv; /**< Private data for FMC DIO */
};
// From fmc-dio-internal.c
extern irqreturn_t fmc_dio_int_interrupt(struct fmc_dio *dev);
extern int fmc_dio_int_ioctl(struct fmc_dio *dev, unsigned int ioctlcmd,
unsigned long arg);
extern int fmc_dio_internal_create(struct fmc_dio *dev);
extern void fmc_dio_internal_destroy(struct fmc_dio *dev);
// From fmc-dio-mdev.c
extern int fmc_dio_mdev_create(struct fmc_dio *dio);
extern void fmc_dio_mdev_destroy(struct fmc_dio *dio);
// From fmc-dio-gpio.c
extern int fmc_dio_gpio_init(struct fmc_dio *dev);
extern void fmc_dio_gpio_exit(struct fmc_dio *dev);
#endif /* __KERNEL__ */
// FIXME: Move IOCTL command numbers?
#define PRIV_MEZZANINE_ID (SIOCDEVPRIVATE + 14)
#define PRIV_MEZZANINE_CMD (SIOCDEVPRIVATE + 15)
// FMC DIO Commands
enum wr_dio_cmd_name {
WR_DIO_CMD_PULSE,
WR_DIO_CMD_STAMP,
WR_DIO_CMD_DAC,
WR_DIO_CMD_INOUT,
};
/*
* This is how parameters are used (K == reply from kernel):
*
* CMD_PULSE:
* cmd->flags: F_NOW, F_REL, F_LOOP
* cmd->channel: the channel or the mask
* cmd->t[]: either 2 or 3 values (start, duration, loop)
* cmd->value: count of loops (0 to turn off)
*
* CMD_STAMP:
* cmd->flags: F_MASK, F_WAIT
* cmd->channel: the channel or the mask
* K: cmd->channel: the channel where we had stamps
* K: cmd->nstamp: number of valid stamps
* K: cmd->t[]: the stamps
*
* CMD_DAC:
* cmd->flags: none
* cmd->channel: which one
* cmd->value: the value
*
* CMD_INOUT:
* cmd->flags: F_MASK
* cmd->channel: the channel or the mask
* cmd->value: bits 0..4: WR-DIO, 8..12 value, 16..20 OEN, 24..28 term
*
*/
#define WR_DIO_INOUT_DIO (1 << 0)
#define WR_DIO_INOUT_VALUE (1 << 8)
#define WR_DIO_INOUT_OUTPUT (1 << 16)
#define WR_DIO_INOUT_TERM (1 << 24)
#define WR_DIO_N_STAMP 16 /* At least 5 * 3 */
struct wr_dio_cmd {
uint16_t command; /* from user */
uint16_t channel; /* 0..4 or mask from user */
uint32_t value; /* for DAC or I/O */
uint32_t flags;
uint32_t nstamp; /* from kernel, if IN_STAMP */
struct timespec t[WR_DIO_N_STAMP]; /* may be from user */
};
#define WR_DIO_F_NOW 0x01 /* Output is now, t[0] ignored */
#define WR_DIO_F_REL 0x02 /* t[0].tv_sec is relative */
#define WR_DIO_F_MASK 0x04 /* Channel is 0x00..0x1f */
#define WR_DIO_F_LOOP 0x08 /* Output should loop: t[2] is looping*/
#define WR_DIO_F_WAIT 0x10 /* Wait for event */
# This Makefile is used to reproduce the headers from svn checkout.
# You need to have "wbgen2" in your command search path and the white-rabbit
# repos (wr-cores and wr-switch-hdl) Git checkout in $REPOS.
# Since this is only meant to be used by me (or Tom) no serious checking is done.
# List of input files in Git checkout
MODULES_WRS ?= $(REPOS)/wr-switch-hdl/modules
MODULES_WRC ?= $(REPOS)/wr-cores/modules
#SPECS = $(HW_REPO)/trunk/documentation/specifications
WB_PPSG = $(MODULES_WRC)/wr_pps_gen/pps_gen_wb.wb
HEADERS = ppsg-regs.h
WBINPUT = $(HEADERS:.h=wb)
# No default, for people who types "make" everywhere (like me)
all:
@echo "This is for developer's use, see Makefile for details"
exit 1
# The headers rule regenerates headers from local wb files
headers: $(HEADERS)
%.h: %.wb
wbgen2 --cstyle=struct --co=$@ $<
sed -i 's,#include <inttypes.h>,#ifdef __KERNEL__\n#include <linux/types.h>\n#else\n#include <stdint.h>\n#endif\n,' $@
sed -i '/ Created *: .*20[0-9][0-9]$$/ d' $@
sed -i 's/-REGS_WB//' $@
# The wbinput rule just copies here stuff from svn.
# Do it silent so errors stand out
wbinput:
@cp $(WB_ENDPOINT) endpoint-regs.wb
@cp $(WB_MDIO) endpoint-mdio.wb
@cp $(WB_PPSG) ppsg-regs.wb
@cp $(WB_TSTAMP) tstamp-regs.wb
@cp $(WB_RTU) rtu-regs.wb
@cp $(WB_NIC) nic-regs.wb
@cp $(WB_SOFTPLL) softpll-regs.wb
@echo "Copied input files from subversions to local directory"
The headers have been derived from what is in svn:
documentation/specifications/hdlspec/memory_map/
In that directory you find the html generated from the wb files.
Here I import the relevant headers. The overall register
map is in ../nic/nic-hardware.h .
The .wb files whence the headers are generated come from different
plases in the white rabbit svn. To ease myself I wrote this in
the Makefile. You can "make wbinput" to get a fresh copy of them,
provided you have SVN set in your environment (point to the root
checkout, before "trunk" subdirectory). If unset or wrong, cp fails.
With "make headers" you can regenerate the headers from the wb input
files. Each generated file is postprocesses with sed to fix these
issues:
* generated files include <inttypes.h> as they use uint32_t. We want
<linux/types.h> instead, as no <inttypes.h> nor <stdint.h> is there
* generated files include the date of creation. This brings to noise
in the "git diff" or plain "diff", so I'd better have no date.
* creation of "#ifdef __THIS_HEADER__" fails on the dash, so I remove
the dash and trailing part with sed (as of writing, it has issues with
pathnames too, this is one the reasons why I copy the wb here first).
/*
Register definitions for slave core: WR Switch PPS generator and RTC
* File : ppsg-regs.h
* Author : auto-generated by wbgen2 from ppsg-regs.wb
* Standard : ANSI C
THIS FILE WAS GENERATED BY wbgen2 FROM SOURCE FILE ppsg-regs.wb
DO NOT HAND-EDIT UNLESS IT'S ABSOLUTELY NECESSARY!
*/
#ifndef __WBGEN2_REGDEFS_PPSG
#define __WBGEN2_REGDEFS_PPSG
#ifdef __KERNEL__
#include <linux/types.h>
#else
#include <stdint.h>
#endif
#if defined( __GNUC__)
#define PACKED __attribute__ ((packed))
#else
#error "Unsupported compiler?"
#endif
#ifndef __WBGEN2_MACROS_DEFINED__
#define __WBGEN2_MACROS_DEFINED__
#define WBGEN2_GEN_MASK(offset, size) (((1<<(size))-1) << (offset))
#define WBGEN2_GEN_WRITE(value, offset, size) (((value) & ((1<<(size))-1)) << (offset))
#define WBGEN2_GEN_READ(reg, offset, size) (((reg) >> (offset)) & ((1<<(size))-1))
#define WBGEN2_SIGN_EXTEND(value, bits) (((value) & (1<<bits) ? ~((1<<(bits))-1): 0 ) | (value))
#endif
/* definitions for register: Control Register */
/* definitions for field: Reset counter in reg: Control Register */
#define PPSG_CR_CNT_RST WBGEN2_GEN_MASK(0, 1)
/* definitions for field: Enable counter in reg: Control Register */
#define PPSG_CR_CNT_EN WBGEN2_GEN_MASK(1, 1)
/* definitions for field: Adjust offset in reg: Control Register */
#define PPSG_CR_CNT_ADJ WBGEN2_GEN_MASK(2, 1)
/* definitions for field: Set time in reg: Control Register */
#define PPSG_CR_CNT_SET WBGEN2_GEN_MASK(3, 1)
/* definitions for field: PPS Pulse width in reg: Control Register */
#define PPSG_CR_PWIDTH_MASK WBGEN2_GEN_MASK(4, 28)
#define PPSG_CR_PWIDTH_SHIFT 4
#define PPSG_CR_PWIDTH_W(value) WBGEN2_GEN_WRITE(value, 4, 28)
#define PPSG_CR_PWIDTH_R(reg) WBGEN2_GEN_READ(reg, 4, 28)
/* definitions for register: Nanosecond counter register */
/* definitions for register: UTC Counter register (least-significant part) */
/* definitions for register: UTC Counter register (most-significant part) */
/* definitions for register: Nanosecond adjustment register */
/* definitions for register: UTC Adjustment register (least-significant part) */
/* definitions for register: UTC Adjustment register (most-significant part) */
/* definitions for register: External sync control register */
/* definitions for field: Sync to external PPS input in reg: External sync control register */
#define PPSG_ESCR_SYNC WBGEN2_GEN_MASK(0, 1)
/* definitions for field: PPS output valid in reg: External sync control register */
#define PPSG_ESCR_PPS_VALID WBGEN2_GEN_MASK(1, 1)
/* definitions for field: Timecode output(UTC+cycles) valid in reg: External sync control register */
#define PPSG_ESCR_TM_VALID WBGEN2_GEN_MASK(2, 1)
PACKED struct PPSG_WB {
/* [0x0]: REG Control Register */
uint32_t CR;
/* [0x4]: REG Nanosecond counter register */
uint32_t CNTR_NSEC;
/* [0x8]: REG UTC Counter register (least-significant part) */
uint32_t CNTR_UTCLO;
/* [0xc]: REG UTC Counter register (most-significant part) */
uint32_t CNTR_UTCHI;
/* [0x10]: REG Nanosecond adjustment register */
uint32_t ADJ_NSEC;
/* [0x14]: REG UTC Adjustment register (least-significant part) */
uint32_t ADJ_UTCLO;
/* [0x18]: REG UTC Adjustment register (most-significant part) */
uint32_t ADJ_UTCHI;
/* [0x1c]: REG External sync control register */
uint32_t ESCR;
};
#endif
-- -*- Mode: LUA; tab-width: 2 -*-
peripheral {
name = "WR Switch PPS generator and RTC";
description = "Unit generating PPS signals and acting as a UTC real-time clock";
hdl_entity = "pps_gen_wb";
prefix = "ppsg";
reg {
name = "Control Register";
prefix = "CR";
field {
name = "Reset counter";
description = "write 1: resets the counter\
write 0: no effect";
prefix = "CNT_RST";
type = MONOSTABLE;
clock = "refclk_i";
};
field {
name = "Enable counter";
description = "1: PPS counter is enabled";
prefix = "CNT_EN";
type = BIT;
access_bus = READ_WRITE;
access_dev = READ_ONLY;
clock = "refclk_i";
};
field {
name = "Adjust offset";
description = "write 1: Starts adjusting PPS/UTC offsets by adding the values taken from ADJ_NSEC, ADJ_UTCLO, ADJ_UTCHI registers to the current PPS counter value. These registers need to be programmed prior to update.\
write 0: no effect\
read 0: adjustment operation is done\
read 1: adjustment operation is in progress";
prefix = "CNT_ADJ";
type = BIT;
access_bus = READ_WRITE;
access_dev = READ_WRITE;
load = LOAD_EXT;
clock = "refclk_i";
};
field {
name = "Set time";
description = "write 1: Sets the UTC/PPS counter to values taken from ADJ_NSEC, ADJ_UTCLO, ADJ_UTCHI registers";
prefix = "CNT_SET";
type = MONOSTABLE;
clock = "refclk_i";
};
field {
name = "PPS Pulse width";
description = "Width of generated PPS pulses in 125 MHz refernce clock cycles";
prefix = "PWIDTH";
size = 28;
type = SLV;
access_bus = READ_WRITE;
access_dev = READ_ONLY;
clock= "refclk_i";
};
};
reg {
name = "Nanosecond counter register";
description = "Nanosecond part of current time, expressed as number of 125 MHz reference clock cycles";
prefix = "CNTR_NSEC";
field {
name = "Nanosecond counter";
type = SLV;
size = 28;
access_bus = READ_ONLY;
access_dev = WRITE_ONLY;
clock = "refclk_i";
};
};
reg {
name = "UTC Counter register (least-significant part)";
description = "Lower 32 bits of current UTC time";
prefix = "CNTR_UTCLO";
field {
name = "UTC Counter";
type = SLV;
size = 32;
access_bus = READ_ONLY;
access_dev = WRITE_ONLY;
clock = "refclk_i";
};
};
reg {
name = "UTC Counter register (most-significant part)";
description = "Highest 8 bits of current UTC time";
prefix = "CNTR_UTCHI";
field {
name = "UTC Counter";
type = SLV;
size = 8;
access_bus = READ_ONLY;
access_dev = WRITE_ONLY;
clock = "refclk_i";
};
};
reg {
name = "Nanosecond adjustment register";
description = "Adjustment value for nanosecond counter";
prefix = "ADJ_NSEC";
field {
name = "Nanosecond adjustment";
type = PASS_THROUGH;
size = 28;
};
};
reg {
name = "UTC Adjustment register (least-significant part)";
description = "Lower 32 bits of adjustment value for UTC";
prefix = "ADJ_UTCLO";
field {
name = "UTC Counter adjustment";
type = PASS_THROUGH;
size = 32;
};
};
reg {
name = "UTC Adjustment register (most-significant part)";
description = "Highest 8 bits of adjustment value for UTC";
prefix = "ADJ_UTCHI";
field {
name = "UTC Counter adjustment";
type = PASS_THROUGH;
size = 8;
};
};
reg {
name = "External sync control register";
prefix = "ESCR";
field {
name = "Sync to external PPS input";
description = "write 1: Waits until a pulse on external PPS input arrives and re-synchronizes the PPS counter to it\
write 0: no effect\
read 1: external synchronization done\
read 0: external synchronization in progress";
type = BIT;
prefix = "SYNC";
access_bus = READ_WRITE;
access_dev = READ_WRITE;
load = LOAD_EXT;
clock = "refclk_i";
};
field {
name = "PPS output valid";
description = "write 1: PPS output provides reliable 1-PPS signal\
write 0: PPS output is invalid";
prefix = "PPS_VALID";
type = BIT;
access_bus = READ_WRITE;
access_dev = READ_ONLY;
clock = "refclk_i";
};
field {
name = "Timecode output(UTC+cycles) valid";
description = "write 1: Timecode output provides valid time\
write 0: Timecode output does not provide valid time";
prefix = "TM_VALID";
type = BIT;
access_bus = READ_WRITE;
access_dev = READ_ONLY;
clock = "refclk_i";
};
};
};
/*
Register definitions for slave core: FMC-DIO-5chttla
* File : wr-dio-regs.h
* Author : auto-generated by wbgen2 from wr-dio-regs.wb
* Standard : ANSI C
THIS FILE WAS GENERATED BY wbgen2 FROM SOURCE FILE wr-dio-regs.wb
DO NOT HAND-EDIT UNLESS IT'S ABSOLUTELY NECESSARY!
*/
#ifndef __WBGEN2_REGDEFS_WR_DIO
#define __WBGEN2_REGDEFS_WR_DIO
#ifdef __KERNEL__
#include <linux/types.h>
#else
#include <stdint.h>
#endif
#if defined( __GNUC__)
#define PACKED __attribute__ ((packed))
#else
#error "Unsupported compiler?"
#endif
#ifndef __WBGEN2_MACROS_DEFINED__
#define __WBGEN2_MACROS_DEFINED__
#define WBGEN2_GEN_MASK(offset, size) (((1<<(size))-1) << (offset))
#define WBGEN2_GEN_WRITE(value, offset, size) (((value) & ((1<<(size))-1)) << (offset))
#define WBGEN2_GEN_READ(reg, offset, size) (((reg) >> (offset)) & ((1<<(size))-1))
#define WBGEN2_SIGN_EXTEND(value, bits) (((value) & (1<<bits) ? ~((1<<(bits))-1): 0 ) | (value))
#endif
/* definitions for register: fmc-dio 0 seconds-based trigger for pulse generation */
/* definitions for field: seconds field in reg: fmc-dio 0 seconds-based trigger for pulse generation */
#define DIO_TRIG0_SECONDS_MASK WBGEN2_GEN_MASK(0, 32)
#define DIO_TRIG0_SECONDS_SHIFT 0
#define DIO_TRIG0_SECONDS_W(value) WBGEN2_GEN_WRITE(value, 0, 32)
#define DIO_TRIG0_SECONDS_R(reg) WBGEN2_GEN_READ(reg, 0, 32)
/* definitions for register: fmc-dio 0 seconds-based trigger for pulse generation */
/* definitions for field: seconds field in reg: fmc-dio 0 seconds-based trigger for pulse generation */
#define DIO_TRIGH0_SECONDS_MASK WBGEN2_GEN_MASK(0, 8)
#define DIO_TRIGH0_SECONDS_SHIFT 0
#define DIO_TRIGH0_SECONDS_W(value) WBGEN2_GEN_WRITE(value, 0, 8)
#define DIO_TRIGH0_SECONDS_R(reg) WBGEN2_GEN_READ(reg, 0, 8)
/* definitions for register: fmc-dio 0 cycles to trigger a pulse generation */
/* definitions for field: cycles field in reg: fmc-dio 0 cycles to trigger a pulse generation */
#define DIO_CYC0_CYC_MASK WBGEN2_GEN_MASK(0, 28)
#define DIO_CYC0_CYC_SHIFT 0
#define DIO_CYC0_CYC_W(value) WBGEN2_GEN_WRITE(value, 0, 28)
#define DIO_CYC0_CYC_R(reg) WBGEN2_GEN_READ(reg, 0, 28)
/* definitions for register: fmc-dio 1 seconds-based trigger for pulse generation */
/* definitions for field: seconds field in reg: fmc-dio 1 seconds-based trigger for pulse generation */
#define DIO_TRIG1_SECONDS_MASK WBGEN2_GEN_MASK(0, 32)
#define DIO_TRIG1_SECONDS_SHIFT 0
#define DIO_TRIG1_SECONDS_W(value) WBGEN2_GEN_WRITE(value, 0, 32)
#define DIO_TRIG1_SECONDS_R(reg) WBGEN2_GEN_READ(reg, 0, 32)
/* definitions for register: fmc-dio 1 seconds-based trigger for pulse generation */
/* definitions for field: seconds field in reg: fmc-dio 1 seconds-based trigger for pulse generation */
#define DIO_TRIGH1_SECONDS_MASK WBGEN2_GEN_MASK(0, 8)
#define DIO_TRIGH1_SECONDS_SHIFT 0
#define DIO_TRIGH1_SECONDS_W(value) WBGEN2_GEN_WRITE(value, 0, 8)
#define DIO_TRIGH1_SECONDS_R(reg) WBGEN2_GEN_READ(reg, 0, 8)
/* definitions for register: fmc-dio 1 cycles to trigger a pulse generation */
/* definitions for field: cycles field in reg: fmc-dio 1 cycles to trigger a pulse generation */
#define DIO_CYC1_CYC_MASK WBGEN2_GEN_MASK(0, 28)
#define DIO_CYC1_CYC_SHIFT 0
#define DIO_CYC1_CYC_W(value) WBGEN2_GEN_WRITE(value, 0, 28)
#define DIO_CYC1_CYC_R(reg) WBGEN2_GEN_READ(reg, 0, 28)
/* definitions for register: fmc-dio 2 seconds-based trigger for pulse generation */
/* definitions for field: seconds field in reg: fmc-dio 2 seconds-based trigger for pulse generation */
#define DIO_TRIG2_SECONDS_MASK WBGEN2_GEN_MASK(0, 32)
#define DIO_TRIG2_SECONDS_SHIFT 0
#define DIO_TRIG2_SECONDS_W(value) WBGEN2_GEN_WRITE(value, 0, 32)
#define DIO_TRIG2_SECONDS_R(reg) WBGEN2_GEN_READ(reg, 0, 32)
/* definitions for register: fmc-dio 2 seconds-based trigger for pulse generation */
/* definitions for field: seconds field in reg: fmc-dio 2 seconds-based trigger for pulse generation */
#define DIO_TRIGH2_SECONDS_MASK WBGEN2_GEN_MASK(0, 8)
#define DIO_TRIGH2_SECONDS_SHIFT 0
#define DIO_TRIGH2_SECONDS_W(value) WBGEN2_GEN_WRITE(value, 0, 8)
#define DIO_TRIGH2_SECONDS_R(reg) WBGEN2_GEN_READ(reg, 0, 8)
/* definitions for register: fmc-dio 2 cycles to trigger a pulse generation */
/* definitions for field: cycles field in reg: fmc-dio 2 cycles to trigger a pulse generation */
#define DIO_CYC2_CYC_MASK WBGEN2_GEN_MASK(0, 28)
#define DIO_CYC2_CYC_SHIFT 0
#define DIO_CYC2_CYC_W(value) WBGEN2_GEN_WRITE(value, 0, 28)
#define DIO_CYC2_CYC_R(reg) WBGEN2_GEN_READ(reg, 0, 28)
/* definitions for register: fmc-dio 3 seconds-based trigger for pulse generation */
/* definitions for field: seconds field in reg: fmc-dio 3 seconds-based trigger for pulse generation */
#define DIO_TRIG3_SECONDS_MASK WBGEN2_GEN_MASK(0, 32)
#define DIO_TRIG3_SECONDS_SHIFT 0
#define DIO_TRIG3_SECONDS_W(value) WBGEN2_GEN_WRITE(value, 0, 32)
#define DIO_TRIG3_SECONDS_R(reg) WBGEN2_GEN_READ(reg, 0, 32)
/* definitions for register: fmc-dio 3 seconds-based trigger for pulse generation */
/* definitions for field: seconds field in reg: fmc-dio 3 seconds-based trigger for pulse generation */
#define DIO_TRIGH3_SECONDS_MASK WBGEN2_GEN_MASK(0, 8)
#define DIO_TRIGH3_SECONDS_SHIFT 0
#define DIO_TRIGH3_SECONDS_W(value) WBGEN2_GEN_WRITE(value, 0, 8)
#define DIO_TRIGH3_SECONDS_R(reg) WBGEN2_GEN_READ(reg, 0, 8)
/* definitions for register: fmc-dio 3 cycles to trigger a pulse generation */
/* definitions for field: cycles field in reg: fmc-dio 3 cycles to trigger a pulse generation */
#define DIO_CYC3_CYC_MASK WBGEN2_GEN_MASK(0, 28)
#define DIO_CYC3_CYC_SHIFT 0
#define DIO_CYC3_CYC_W(value) WBGEN2_GEN_WRITE(value, 0, 28)
#define DIO_CYC3_CYC_R(reg) WBGEN2_GEN_READ(reg, 0, 28)
/* definitions for register: fmc-dio 4 seconds-based trigger for pulse generation */
/* definitions for field: seconds field in reg: fmc-dio 4 seconds-based trigger for pulse generation */
#define DIO_TRIG4_SECONDS_MASK WBGEN2_GEN_MASK(0, 32)
#define DIO_TRIG4_SECONDS_SHIFT 0
#define DIO_TRIG4_SECONDS_W(value) WBGEN2_GEN_WRITE(value, 0, 32)
#define DIO_TRIG4_SECONDS_R(reg) WBGEN2_GEN_READ(reg, 0, 32)
/* definitions for register: fmc-dio 4 seconds-based trigger for pulse generation */
/* definitions for field: seconds field in reg: fmc-dio 4 seconds-based trigger for pulse generation */
#define DIO_TRIGH4_SECONDS_MASK WBGEN2_GEN_MASK(0, 8)
#define DIO_TRIGH4_SECONDS_SHIFT 0
#define DIO_TRIGH4_SECONDS_W(value) WBGEN2_GEN_WRITE(value, 0, 8)
#define DIO_TRIGH4_SECONDS_R(reg) WBGEN2_GEN_READ(reg, 0, 8)
/* definitions for register: fmc-dio 4 cycles to trigger a pulse generation */
/* definitions for field: cycles field in reg: fmc-dio 4 cycles to trigger a pulse generation */
#define DIO_CYC4_CYC_MASK WBGEN2_GEN_MASK(0, 28)
#define DIO_CYC4_CYC_SHIFT 0
#define DIO_CYC4_CYC_W(value) WBGEN2_GEN_WRITE(value, 0, 28)
#define DIO_CYC4_CYC_R(reg) WBGEN2_GEN_READ(reg, 0, 28)
/* definitions for register: FMC-DIO input/output configuration register. */
/* definitions for field: channel0 in reg: FMC-DIO input/output configuration register. */
#define DIO_IOMODE_CH0_MASK WBGEN2_GEN_MASK(0, 4)
#define DIO_IOMODE_CH0_SHIFT 0
#define DIO_IOMODE_CH0_W(value) WBGEN2_GEN_WRITE(value, 0, 4)
#define DIO_IOMODE_CH0_R(reg) WBGEN2_GEN_READ(reg, 0, 4)
/* definitions for field: channel1 in reg: FMC-DIO input/output configuration register. */
#define DIO_IOMODE_CH1_MASK WBGEN2_GEN_MASK(4, 4)
#define DIO_IOMODE_CH1_SHIFT 4
#define DIO_IOMODE_CH1_W(value) WBGEN2_GEN_WRITE(value, 4, 4)
#define DIO_IOMODE_CH1_R(reg) WBGEN2_GEN_READ(reg, 4, 4)
/* definitions for field: channel2 in reg: FMC-DIO input/output configuration register. */
#define DIO_IOMODE_CH2_MASK WBGEN2_GEN_MASK(8, 4)
#define DIO_IOMODE_CH2_SHIFT 8
#define DIO_IOMODE_CH2_W(value) WBGEN2_GEN_WRITE(value, 8, 4)
#define DIO_IOMODE_CH2_R(reg) WBGEN2_GEN_READ(reg, 8, 4)
/* definitions for field: channel3 in reg: FMC-DIO input/output configuration register. */
#define DIO_IOMODE_CH3_MASK WBGEN2_GEN_MASK(12, 4)
#define DIO_IOMODE_CH3_SHIFT 12
#define DIO_IOMODE_CH3_W(value) WBGEN2_GEN_WRITE(value, 12, 4)
#define DIO_IOMODE_CH3_R(reg) WBGEN2_GEN_READ(reg, 12, 4)
/* definitions for field: channel4 in reg: FMC-DIO input/output configuration register. */
#define DIO_IOMODE_CH4_MASK WBGEN2_GEN_MASK(16, 4)
#define DIO_IOMODE_CH4_SHIFT 16
#define DIO_IOMODE_CH4_W(value) WBGEN2_GEN_WRITE(value, 16, 4)
#define DIO_IOMODE_CH4_R(reg) WBGEN2_GEN_READ(reg, 16, 4)
/* definitions for register: Time-programmable output strobe signal */
/* definitions for field: Sincle-cycle strobe in reg: Time-programmable output strobe signal */
#define DIO_LATCH_TIME_CH0 WBGEN2_GEN_MASK(0, 1)
/* definitions for field: Sincle-cycle strobe in reg: Time-programmable output strobe signal */
#define DIO_LATCH_TIME_CH1 WBGEN2_GEN_MASK(1, 1)
/* definitions for field: Sincle-cycle strobe in reg: Time-programmable output strobe signal */
#define DIO_LATCH_TIME_CH2 WBGEN2_GEN_MASK(2, 1)
/* definitions for field: Sincle-cycle strobe in reg: Time-programmable output strobe signal */
#define DIO_LATCH_TIME_CH3 WBGEN2_GEN_MASK(3, 1)
/* definitions for field: Sincle-cycle strobe in reg: Time-programmable output strobe signal */
#define DIO_LATCH_TIME_CH4 WBGEN2_GEN_MASK(4, 1)
/* definitions for register: FMC-DIO time trigger is ready to accept a new trigger generation request */
/* definitions for field: trig_rdy field in reg: FMC-DIO time trigger is ready to accept a new trigger generation request */
#define DIO_TRIG_RDY_MASK WBGEN2_GEN_MASK(0, 5)
#define DIO_TRIG_RDY_SHIFT 0
#define DIO_TRIG_RDY_W(value) WBGEN2_GEN_WRITE(value, 0, 5)
#define DIO_TRIG_RDY_R(reg) WBGEN2_GEN_READ(reg, 0, 5)
/* definitions for register: fmc-dio channel 0 Programmable/immediate output pulse length */
/* definitions for field: number of ticks field for channel 0 in reg: fmc-dio channel 0 Programmable/immediate output pulse length */
#define DIO_PROG0_PULSE_LENGTH_MASK WBGEN2_GEN_MASK(0, 28)
#define DIO_PROG0_PULSE_LENGTH_SHIFT 0
#define DIO_PROG0_PULSE_LENGTH_W(value) WBGEN2_GEN_WRITE(value, 0, 28)
#define DIO_PROG0_PULSE_LENGTH_R(reg) WBGEN2_GEN_READ(reg, 0, 28)
/* definitions for register: fmc-dio channel 1 Programmable/immediate output pulse length */
/* definitions for field: number of ticks field for channel 1 in reg: fmc-dio channel 1 Programmable/immediate output pulse length */
#define DIO_PROG1_PULSE_LENGTH_MASK WBGEN2_GEN_MASK(0, 28)
#define DIO_PROG1_PULSE_LENGTH_SHIFT 0
#define DIO_PROG1_PULSE_LENGTH_W(value) WBGEN2_GEN_WRITE(value, 0, 28)
#define DIO_PROG1_PULSE_LENGTH_R(reg) WBGEN2_GEN_READ(reg, 0, 28)
/* definitions for register: fmc-dio channel 2 Programmable/immediate output pulse length */
/* definitions for field: number of ticks field for channel 2 in reg: fmc-dio channel 2 Programmable/immediate output pulse length */
#define DIO_PROG2_PULSE_LENGTH_MASK WBGEN2_GEN_MASK(0, 28)
#define DIO_PROG2_PULSE_LENGTH_SHIFT 0
#define DIO_PROG2_PULSE_LENGTH_W(value) WBGEN2_GEN_WRITE(value, 0, 28)
#define DIO_PROG2_PULSE_LENGTH_R(reg) WBGEN2_GEN_READ(reg, 0, 28)
/* definitions for register: fmc-dio channel 3 Programmable/immediate output pulse length */
/* definitions for field: number of ticks field for channel 3 in reg: fmc-dio channel 3 Programmable/immediate output pulse length */
#define DIO_PROG3_PULSE_LENGTH_MASK WBGEN2_GEN_MASK(0, 28)
#define DIO_PROG3_PULSE_LENGTH_SHIFT 0
#define DIO_PROG3_PULSE_LENGTH_W(value) WBGEN2_GEN_WRITE(value, 0, 28)
#define DIO_PROG3_PULSE_LENGTH_R(reg) WBGEN2_GEN_READ(reg, 0, 28)
/* definitions for register: fmc-dio channel 4 Programmable/immediate output pulse length */
/* definitions for field: number of ticks field for channel 4 in reg: fmc-dio channel 4 Programmable/immediate output pulse length */
#define DIO_PROG4_PULSE_LENGTH_MASK WBGEN2_GEN_MASK(0, 28)
#define DIO_PROG4_PULSE_LENGTH_SHIFT 0
#define DIO_PROG4_PULSE_LENGTH_W(value) WBGEN2_GEN_WRITE(value, 0, 28)
#define DIO_PROG4_PULSE_LENGTH_R(reg) WBGEN2_GEN_READ(reg, 0, 28)
/* definitions for register: Pulse generate immediately */
/* definitions for field: pulse_gen_now_0 in reg: Pulse generate immediately */
#define DIO_PULSE_IMM_0 WBGEN2_GEN_MASK(0, 1)
/* definitions for field: pulse_gen_now_1 in reg: Pulse generate immediately */
#define DIO_PULSE_IMM_1 WBGEN2_GEN_MASK(1, 1)
/* definitions for field: pulse_gen_now_2 in reg: Pulse generate immediately */
#define DIO_PULSE_IMM_2 WBGEN2_GEN_MASK(2, 1)
/* definitions for field: pulse_gen_now_3 in reg: Pulse generate immediately */
#define DIO_PULSE_IMM_3 WBGEN2_GEN_MASK(3, 1)
/* definitions for field: pulse_gen_now_4 in reg: Pulse generate immediately */
#define DIO_PULSE_IMM_4 WBGEN2_GEN_MASK(4, 1)
/* definitions for register: Interrupt disable register */
/* definitions for field: dio fifo not-empty 0 in reg: Interrupt disable register */
#define DIO_EIC_IDR_NEMPTY_0 WBGEN2_GEN_MASK(0, 1)
/* definitions for field: dio fifo not-empty 1 in reg: Interrupt disable register */
#define DIO_EIC_IDR_NEMPTY_1 WBGEN2_GEN_MASK(1, 1)
/* definitions for field: dio fifo not-empty 2 in reg: Interrupt disable register */
#define DIO_EIC_IDR_NEMPTY_2 WBGEN2_GEN_MASK(2, 1)
/* definitions for field: dio fifo not-empty 3 in reg: Interrupt disable register */
#define DIO_EIC_IDR_NEMPTY_3 WBGEN2_GEN_MASK(3, 1)
/* definitions for field: dio fifo not-empty 4 in reg: Interrupt disable register */
#define DIO_EIC_IDR_NEMPTY_4 WBGEN2_GEN_MASK(4, 1)
/* definitions for field: Channel 0 trigger ready interrupt in reg: Interrupt disable register */
#define DIO_EIC_IDR_TRIGGER_READY_0 WBGEN2_GEN_MASK(5, 1)
/* definitions for field: Channel 1 trigger ready interrupt in reg: Interrupt disable register */
#define DIO_EIC_IDR_TRIGGER_READY_1 WBGEN2_GEN_MASK(6, 1)
/* definitions for field: Channel 2 trigger ready interrupt in reg: Interrupt disable register */
#define DIO_EIC_IDR_TRIGGER_READY_2 WBGEN2_GEN_MASK(7, 1)
/* definitions for field: Channel 3 trigger ready interrupt in reg: Interrupt disable register */
#define DIO_EIC_IDR_TRIGGER_READY_3 WBGEN2_GEN_MASK(8, 1)
/* definitions for field: Channel 4 trigger ready interrupt in reg: Interrupt disable register */
#define DIO_EIC_IDR_TRIGGER_READY_4 WBGEN2_GEN_MASK(9, 1)
/* definitions for register: Interrupt enable register */
/* definitions for field: dio fifo not-empty 0 in reg: Interrupt enable register */
#define DIO_EIC_IER_NEMPTY_0 WBGEN2_GEN_MASK(0, 1)
/* definitions for field: dio fifo not-empty 1 in reg: Interrupt enable register */
#define DIO_EIC_IER_NEMPTY_1 WBGEN2_GEN_MASK(1, 1)
/* definitions for field: dio fifo not-empty 2 in reg: Interrupt enable register */
#define DIO_EIC_IER_NEMPTY_2 WBGEN2_GEN_MASK(2, 1)
/* definitions for field: dio fifo not-empty 3 in reg: Interrupt enable register */
#define DIO_EIC_IER_NEMPTY_3 WBGEN2_GEN_MASK(3, 1)
/* definitions for field: dio fifo not-empty 4 in reg: Interrupt enable register */
#define DIO_EIC_IER_NEMPTY_4 WBGEN2_GEN_MASK(4, 1)
/* definitions for field: Channel 0 trigger ready interrupt in reg: Interrupt enable register */
#define DIO_EIC_IER_TRIGGER_READY_0 WBGEN2_GEN_MASK(5, 1)
/* definitions for field: Channel 1 trigger ready interrupt in reg: Interrupt enable register */
#define DIO_EIC_IER_TRIGGER_READY_1 WBGEN2_GEN_MASK(6, 1)
/* definitions for field: Channel 2 trigger ready interrupt in reg: Interrupt enable register */
#define DIO_EIC_IER_TRIGGER_READY_2 WBGEN2_GEN_MASK(7, 1)
/* definitions for field: Channel 3 trigger ready interrupt in reg: Interrupt enable register */
#define DIO_EIC_IER_TRIGGER_READY_3 WBGEN2_GEN_MASK(8, 1)
/* definitions for field: Channel 4 trigger ready interrupt in reg: Interrupt enable register */
#define DIO_EIC_IER_TRIGGER_READY_4 WBGEN2_GEN_MASK(9, 1)
/* definitions for register: Interrupt mask register */
/* definitions for field: dio fifo not-empty 0 in reg: Interrupt mask register */
#define DIO_EIC_IMR_NEMPTY_0 WBGEN2_GEN_MASK(0, 1)
/* definitions for field: dio fifo not-empty 1 in reg: Interrupt mask register */
#define DIO_EIC_IMR_NEMPTY_1 WBGEN2_GEN_MASK(1, 1)
/* definitions for field: dio fifo not-empty 2 in reg: Interrupt mask register */
#define DIO_EIC_IMR_NEMPTY_2 WBGEN2_GEN_MASK(2, 1)
/* definitions for field: dio fifo not-empty 3 in reg: Interrupt mask register */
#define DIO_EIC_IMR_NEMPTY_3 WBGEN2_GEN_MASK(3, 1)
/* definitions for field: dio fifo not-empty 4 in reg: Interrupt mask register */
#define DIO_EIC_IMR_NEMPTY_4 WBGEN2_GEN_MASK(4, 1)
/* definitions for field: Channel 0 trigger ready interrupt in reg: Interrupt mask register */
#define DIO_EIC_IMR_TRIGGER_READY_0 WBGEN2_GEN_MASK(5, 1)
/* definitions for field: Channel 1 trigger ready interrupt in reg: Interrupt mask register */
#define DIO_EIC_IMR_TRIGGER_READY_1 WBGEN2_GEN_MASK(6, 1)
/* definitions for field: Channel 2 trigger ready interrupt in reg: Interrupt mask register */
#define DIO_EIC_IMR_TRIGGER_READY_2 WBGEN2_GEN_MASK(7, 1)
/* definitions for field: Channel 3 trigger ready interrupt in reg: Interrupt mask register */
#define DIO_EIC_IMR_TRIGGER_READY_3 WBGEN2_GEN_MASK(8, 1)
/* definitions for field: Channel 4 trigger ready interrupt in reg: Interrupt mask register */
#define DIO_EIC_IMR_TRIGGER_READY_4 WBGEN2_GEN_MASK(9, 1)
/* definitions for register: Interrupt status register */
/* definitions for field: dio fifo not-empty 0 in reg: Interrupt status register */
#define DIO_EIC_ISR_NEMPTY_0 WBGEN2_GEN_MASK(0, 1)
/* definitions for field: dio fifo not-empty 1 in reg: Interrupt status register */
#define DIO_EIC_ISR_NEMPTY_1 WBGEN2_GEN_MASK(1, 1)
/* definitions for field: dio fifo not-empty 2 in reg: Interrupt status register */
#define DIO_EIC_ISR_NEMPTY_2 WBGEN2_GEN_MASK(2, 1)
/* definitions for field: dio fifo not-empty 3 in reg: Interrupt status register */
#define DIO_EIC_ISR_NEMPTY_3 WBGEN2_GEN_MASK(3, 1)
/* definitions for field: dio fifo not-empty 4 in reg: Interrupt status register */
#define DIO_EIC_ISR_NEMPTY_4 WBGEN2_GEN_MASK(4, 1)
/* definitions for field: Channel 0 trigger ready interrupt in reg: Interrupt status register */
#define DIO_EIC_ISR_TRIGGER_READY_0 WBGEN2_GEN_MASK(5, 1)
/* definitions for field: Channel 1 trigger ready interrupt in reg: Interrupt status register */
#define DIO_EIC_ISR_TRIGGER_READY_1 WBGEN2_GEN_MASK(6, 1)
/* definitions for field: Channel 2 trigger ready interrupt in reg: Interrupt status register */
#define DIO_EIC_ISR_TRIGGER_READY_2 WBGEN2_GEN_MASK(7, 1)
/* definitions for field: Channel 3 trigger ready interrupt in reg: Interrupt status register */
#define DIO_EIC_ISR_TRIGGER_READY_3 WBGEN2_GEN_MASK(8, 1)
/* definitions for field: Channel 4 trigger ready interrupt in reg: Interrupt status register */
#define DIO_EIC_ISR_TRIGGER_READY_4 WBGEN2_GEN_MASK(9, 1)
/* definitions for register: FIFO 'Timestamp FIFO 0' data output register 0 */
/* definitions for field: seconds time in reg: FIFO 'Timestamp FIFO 0' data output register 0 */
#define DIO_TSF0_R0_TAG_SECONDS_MASK WBGEN2_GEN_MASK(0, 32)
#define DIO_TSF0_R0_TAG_SECONDS_SHIFT 0
#define DIO_TSF0_R0_TAG_SECONDS_W(value) WBGEN2_GEN_WRITE(value, 0, 32)
#define DIO_TSF0_R0_TAG_SECONDS_R(reg) WBGEN2_GEN_READ(reg, 0, 32)
/* definitions for register: FIFO 'Timestamp FIFO 0' data output register 1 */
/* definitions for field: seconds time H in reg: FIFO 'Timestamp FIFO 0' data output register 1 */
#define DIO_TSF0_R1_TAG_SECONDSH_MASK WBGEN2_GEN_MASK(0, 8)
#define DIO_TSF0_R1_TAG_SECONDSH_SHIFT 0
#define DIO_TSF0_R1_TAG_SECONDSH_W(value) WBGEN2_GEN_WRITE(value, 0, 8)
#define DIO_TSF0_R1_TAG_SECONDSH_R(reg) WBGEN2_GEN_READ(reg, 0, 8)
/* definitions for register: FIFO 'Timestamp FIFO 0' data output register 2 */
/* definitions for field: Sub-second accuracy in reg: FIFO 'Timestamp FIFO 0' data output register 2 */
#define DIO_TSF0_R2_TAG_CYCLES_MASK WBGEN2_GEN_MASK(0, 28)
#define DIO_TSF0_R2_TAG_CYCLES_SHIFT 0
#define DIO_TSF0_R2_TAG_CYCLES_W(value) WBGEN2_GEN_WRITE(value, 0, 28)
#define DIO_TSF0_R2_TAG_CYCLES_R(reg) WBGEN2_GEN_READ(reg, 0, 28)
/* definitions for register: FIFO 'Timestamp FIFO 0' control/status register */
/* definitions for field: FIFO full flag in reg: FIFO 'Timestamp FIFO 0' control/status register */
#define DIO_TSF0_CSR_FULL WBGEN2_GEN_MASK(16, 1)
/* definitions for field: FIFO empty flag in reg: FIFO 'Timestamp FIFO 0' control/status register */
#define DIO_TSF0_CSR_EMPTY WBGEN2_GEN_MASK(17, 1)
/* definitions for field: FIFO counter in reg: FIFO 'Timestamp FIFO 0' control/status register */
#define DIO_TSF0_CSR_USEDW_MASK WBGEN2_GEN_MASK(0, 8)
#define DIO_TSF0_CSR_USEDW_SHIFT 0
#define DIO_TSF0_CSR_USEDW_W(value) WBGEN2_GEN_WRITE(value, 0, 8)
#define DIO_TSF0_CSR_USEDW_R(reg) WBGEN2_GEN_READ(reg, 0, 8)
/* definitions for register: FIFO 'Timestamp FIFO 1' data output register 0 */
/* definitions for field: seconds time in reg: FIFO 'Timestamp FIFO 1' data output register 0 */
#define DIO_TSF1_R0_TAG_SECONDS_MASK WBGEN2_GEN_MASK(0, 32)
#define DIO_TSF1_R0_TAG_SECONDS_SHIFT 0
#define DIO_TSF1_R0_TAG_SECONDS_W(value) WBGEN2_GEN_WRITE(value, 0, 32)
#define DIO_TSF1_R0_TAG_SECONDS_R(reg) WBGEN2_GEN_READ(reg, 0, 32)
/* definitions for register: FIFO 'Timestamp FIFO 1' data output register 1 */
/* definitions for field: seconds time H in reg: FIFO 'Timestamp FIFO 1' data output register 1 */
#define DIO_TSF1_R1_TAG_SECONDSH_MASK WBGEN2_GEN_MASK(0, 8)
#define DIO_TSF1_R1_TAG_SECONDSH_SHIFT 0
#define DIO_TSF1_R1_TAG_SECONDSH_W(value) WBGEN2_GEN_WRITE(value, 0, 8)
#define DIO_TSF1_R1_TAG_SECONDSH_R(reg) WBGEN2_GEN_READ(reg, 0, 8)
/* definitions for register: FIFO 'Timestamp FIFO 1' data output register 2 */
/* definitions for field: Sub-second accuracy in reg: FIFO 'Timestamp FIFO 1' data output register 2 */
#define DIO_TSF1_R2_TAG_CYCLES_MASK WBGEN2_GEN_MASK(0, 28)
#define DIO_TSF1_R2_TAG_CYCLES_SHIFT 0
#define DIO_TSF1_R2_TAG_CYCLES_W(value) WBGEN2_GEN_WRITE(value, 0, 28)
#define DIO_TSF1_R2_TAG_CYCLES_R(reg) WBGEN2_GEN_READ(reg, 0, 28)
/* definitions for register: FIFO 'Timestamp FIFO 1' control/status register */
/* definitions for field: FIFO full flag in reg: FIFO 'Timestamp FIFO 1' control/status register */
#define DIO_TSF1_CSR_FULL WBGEN2_GEN_MASK(16, 1)
/* definitions for field: FIFO empty flag in reg: FIFO 'Timestamp FIFO 1' control/status register */
#define DIO_TSF1_CSR_EMPTY WBGEN2_GEN_MASK(17, 1)
/* definitions for field: FIFO counter in reg: FIFO 'Timestamp FIFO 1' control/status register */
#define DIO_TSF1_CSR_USEDW_MASK WBGEN2_GEN_MASK(0, 8)
#define DIO_TSF1_CSR_USEDW_SHIFT 0
#define DIO_TSF1_CSR_USEDW_W(value) WBGEN2_GEN_WRITE(value, 0, 8)
#define DIO_TSF1_CSR_USEDW_R(reg) WBGEN2_GEN_READ(reg, 0, 8)
/* definitions for register: FIFO 'Timestamp FIFO 2' data output register 0 */
/* definitions for field: seconds time in reg: FIFO 'Timestamp FIFO 2' data output register 0 */
#define DIO_TSF2_R0_TAG_SECONDS_MASK WBGEN2_GEN_MASK(0, 32)
#define DIO_TSF2_R0_TAG_SECONDS_SHIFT 0
#define DIO_TSF2_R0_TAG_SECONDS_W(value) WBGEN2_GEN_WRITE(value, 0, 32)
#define DIO_TSF2_R0_TAG_SECONDS_R(reg) WBGEN2_GEN_READ(reg, 0, 32)
/* definitions for register: FIFO 'Timestamp FIFO 2' data output register 1 */
/* definitions for field: seconds time H in reg: FIFO 'Timestamp FIFO 2' data output register 1 */
#define DIO_TSF2_R1_TAG_SECONDSH_MASK WBGEN2_GEN_MASK(0, 8)
#define DIO_TSF2_R1_TAG_SECONDSH_SHIFT 0
#define DIO_TSF2_R1_TAG_SECONDSH_W(value) WBGEN2_GEN_WRITE(value, 0, 8)
#define DIO_TSF2_R1_TAG_SECONDSH_R(reg) WBGEN2_GEN_READ(reg, 0, 8)
/* definitions for register: FIFO 'Timestamp FIFO 2' data output register 2 */
/* definitions for field: Sub-second accuracy in reg: FIFO 'Timestamp FIFO 2' data output register 2 */
#define DIO_TSF2_R2_TAG_CYCLES_MASK WBGEN2_GEN_MASK(0, 28)
#define DIO_TSF2_R2_TAG_CYCLES_SHIFT 0
#define DIO_TSF2_R2_TAG_CYCLES_W(value) WBGEN2_GEN_WRITE(value, 0, 28)
#define DIO_TSF2_R2_TAG_CYCLES_R(reg) WBGEN2_GEN_READ(reg, 0, 28)
/* definitions for register: FIFO 'Timestamp FIFO 2' control/status register */
/* definitions for field: FIFO full flag in reg: FIFO 'Timestamp FIFO 2' control/status register */
#define DIO_TSF2_CSR_FULL WBGEN2_GEN_MASK(16, 1)
/* definitions for field: FIFO empty flag in reg: FIFO 'Timestamp FIFO 2' control/status register */
#define DIO_TSF2_CSR_EMPTY WBGEN2_GEN_MASK(17, 1)
/* definitions for field: FIFO counter in reg: FIFO 'Timestamp FIFO 2' control/status register */
#define DIO_TSF2_CSR_USEDW_MASK WBGEN2_GEN_MASK(0, 8)
#define DIO_TSF2_CSR_USEDW_SHIFT 0
#define DIO_TSF2_CSR_USEDW_W(value) WBGEN2_GEN_WRITE(value, 0, 8)
#define DIO_TSF2_CSR_USEDW_R(reg) WBGEN2_GEN_READ(reg, 0, 8)
/* definitions for register: FIFO 'Timestamp FIFO 3' data output register 0 */
/* definitions for field: seconds time in reg: FIFO 'Timestamp FIFO 3' data output register 0 */
#define DIO_TSF3_R0_TAG_SECONDS_MASK WBGEN2_GEN_MASK(0, 32)
#define DIO_TSF3_R0_TAG_SECONDS_SHIFT 0
#define DIO_TSF3_R0_TAG_SECONDS_W(value) WBGEN2_GEN_WRITE(value, 0, 32)
#define DIO_TSF3_R0_TAG_SECONDS_R(reg) WBGEN2_GEN_READ(reg, 0, 32)
/* definitions for register: FIFO 'Timestamp FIFO 3' data output register 1 */
/* definitions for field: seconds time H in reg: FIFO 'Timestamp FIFO 3' data output register 1 */
#define DIO_TSF3_R1_TAG_SECONDSH_MASK WBGEN2_GEN_MASK(0, 8)
#define DIO_TSF3_R1_TAG_SECONDSH_SHIFT 0
#define DIO_TSF3_R1_TAG_SECONDSH_W(value) WBGEN2_GEN_WRITE(value, 0, 8)
#define DIO_TSF3_R1_TAG_SECONDSH_R(reg) WBGEN2_GEN_READ(reg, 0, 8)
/* definitions for register: FIFO 'Timestamp FIFO 3' data output register 2 */
/* definitions for field: Sub-second accuracy in reg: FIFO 'Timestamp FIFO 3' data output register 2 */
#define DIO_TSF3_R2_TAG_CYCLES_MASK WBGEN2_GEN_MASK(0, 28)
#define DIO_TSF3_R2_TAG_CYCLES_SHIFT 0
#define DIO_TSF3_R2_TAG_CYCLES_W(value) WBGEN2_GEN_WRITE(value, 0, 28)
#define DIO_TSF3_R2_TAG_CYCLES_R(reg) WBGEN2_GEN_READ(reg, 0, 28)
/* definitions for register: FIFO 'Timestamp FIFO 3' control/status register */
/* definitions for field: FIFO full flag in reg: FIFO 'Timestamp FIFO 3' control/status register */
#define DIO_TSF3_CSR_FULL WBGEN2_GEN_MASK(16, 1)
/* definitions for field: FIFO empty flag in reg: FIFO 'Timestamp FIFO 3' control/status register */
#define DIO_TSF3_CSR_EMPTY WBGEN2_GEN_MASK(17, 1)
/* definitions for field: FIFO counter in reg: FIFO 'Timestamp FIFO 3' control/status register */
#define DIO_TSF3_CSR_USEDW_MASK WBGEN2_GEN_MASK(0, 8)
#define DIO_TSF3_CSR_USEDW_SHIFT 0
#define DIO_TSF3_CSR_USEDW_W(value) WBGEN2_GEN_WRITE(value, 0, 8)
#define DIO_TSF3_CSR_USEDW_R(reg) WBGEN2_GEN_READ(reg, 0, 8)
/* definitions for register: FIFO 'Timestamp FIFO 4' data output register 0 */
/* definitions for field: seconds time in reg: FIFO 'Timestamp FIFO 4' data output register 0 */
#define DIO_TSF4_R0_TAG_SECONDS_MASK WBGEN2_GEN_MASK(0, 32)
#define DIO_TSF4_R0_TAG_SECONDS_SHIFT 0
#define DIO_TSF4_R0_TAG_SECONDS_W(value) WBGEN2_GEN_WRITE(value, 0, 32)
#define DIO_TSF4_R0_TAG_SECONDS_R(reg) WBGEN2_GEN_READ(reg, 0, 32)
/* definitions for register: FIFO 'Timestamp FIFO 4' data output register 1 */
/* definitions for field: seconds time H in reg: FIFO 'Timestamp FIFO 4' data output register 1 */
#define DIO_TSF4_R1_TAG_SECONDSH_MASK WBGEN2_GEN_MASK(0, 8)
#define DIO_TSF4_R1_TAG_SECONDSH_SHIFT 0
#define DIO_TSF4_R1_TAG_SECONDSH_W(value) WBGEN2_GEN_WRITE(value, 0, 8)
#define DIO_TSF4_R1_TAG_SECONDSH_R(reg) WBGEN2_GEN_READ(reg, 0, 8)
/* definitions for register: FIFO 'Timestamp FIFO 4' data output register 2 */
/* definitions for field: Sub-second accuracy in reg: FIFO 'Timestamp FIFO 4' data output register 2 */
#define DIO_TSF4_R2_TAG_CYCLES_MASK WBGEN2_GEN_MASK(0, 28)
#define DIO_TSF4_R2_TAG_CYCLES_SHIFT 0
#define DIO_TSF4_R2_TAG_CYCLES_W(value) WBGEN2_GEN_WRITE(value, 0, 28)
#define DIO_TSF4_R2_TAG_CYCLES_R(reg) WBGEN2_GEN_READ(reg, 0, 28)
/* definitions for register: FIFO 'Timestamp FIFO 4' control/status register */
/* definitions for field: FIFO full flag in reg: FIFO 'Timestamp FIFO 4' control/status register */
#define DIO_TSF4_CSR_FULL WBGEN2_GEN_MASK(16, 1)
/* definitions for field: FIFO empty flag in reg: FIFO 'Timestamp FIFO 4' control/status register */
#define DIO_TSF4_CSR_EMPTY WBGEN2_GEN_MASK(17, 1)
/* definitions for field: FIFO counter in reg: FIFO 'Timestamp FIFO 4' control/status register */
#define DIO_TSF4_CSR_USEDW_MASK WBGEN2_GEN_MASK(0, 8)
#define DIO_TSF4_CSR_USEDW_SHIFT 0
#define DIO_TSF4_CSR_USEDW_W(value) WBGEN2_GEN_WRITE(value, 0, 8)
#define DIO_TSF4_CSR_USEDW_R(reg) WBGEN2_GEN_READ(reg, 0, 8)
PACKED struct DIO_WB {
/* [0x0]: REG fmc-dio 0 seconds-based trigger for pulse generation */
uint32_t TRIG0;
/* [0x4]: REG fmc-dio 0 seconds-based trigger for pulse generation */
uint32_t TRIGH0;
/* [0x8]: REG fmc-dio 0 cycles to trigger a pulse generation */
uint32_t CYC0;
/* [0xc]: REG fmc-dio 1 seconds-based trigger for pulse generation */
uint32_t TRIG1;
/* [0x10]: REG fmc-dio 1 seconds-based trigger for pulse generation */
uint32_t TRIGH1;
/* [0x14]: REG fmc-dio 1 cycles to trigger a pulse generation */
uint32_t CYC1;
/* [0x18]: REG fmc-dio 2 seconds-based trigger for pulse generation */
uint32_t TRIG2;
/* [0x1c]: REG fmc-dio 2 seconds-based trigger for pulse generation */
uint32_t TRIGH2;
/* [0x20]: REG fmc-dio 2 cycles to trigger a pulse generation */
uint32_t CYC2;
/* [0x24]: REG fmc-dio 3 seconds-based trigger for pulse generation */
uint32_t TRIG3;
/* [0x28]: REG fmc-dio 3 seconds-based trigger for pulse generation */
uint32_t TRIGH3;
/* [0x2c]: REG fmc-dio 3 cycles to trigger a pulse generation */
uint32_t CYC3;
/* [0x30]: REG fmc-dio 4 seconds-based trigger for pulse generation */
uint32_t TRIG4;
/* [0x34]: REG fmc-dio 4 seconds-based trigger for pulse generation */
uint32_t TRIGH4;
/* [0x38]: REG fmc-dio 4 cycles to trigger a pulse generation */
uint32_t CYC4;
/* [0x3c]: REG FMC-DIO input/output configuration register. */
uint32_t IOMODE;
/* [0x40]: REG Time-programmable output strobe signal */
uint32_t R_LATCH;
/* [0x44]: REG FMC-DIO time trigger is ready to accept a new trigger generation request */
uint32_t TRIG;
/* [0x48]: REG fmc-dio channel 0 Programmable/immediate output pulse length */
uint32_t PROG0_PULSE;
/* [0x4c]: REG fmc-dio channel 1 Programmable/immediate output pulse length */
uint32_t PROG1_PULSE;
/* [0x50]: REG fmc-dio channel 2 Programmable/immediate output pulse length */
uint32_t PROG2_PULSE;
/* [0x54]: REG fmc-dio channel 3 Programmable/immediate output pulse length */
uint32_t PROG3_PULSE;
/* [0x58]: REG fmc-dio channel 4 Programmable/immediate output pulse length */
uint32_t PROG4_PULSE;
/* [0x5c]: REG Pulse generate immediately */
uint32_t PULSE;
/* [0x60]: REG Interrupt disable register */
uint32_t EIC_IDR;
/* [0x64]: REG Interrupt enable register */
uint32_t EIC_IER;
/* [0x68]: REG Interrupt mask register */
uint32_t EIC_IMR;
/* [0x6c]: REG Interrupt status register */
uint32_t EIC_ISR;
/* [0x70]: REG FIFO 'Timestamp FIFO 0' data output register 0 */
uint32_t TSF0_R0;
/* [0x74]: REG FIFO 'Timestamp FIFO 0' data output register 1 */
uint32_t TSF0_R1;
/* [0x78]: REG FIFO 'Timestamp FIFO 0' data output register 2 */
uint32_t TSF0_R2;
/* [0x7c]: REG FIFO 'Timestamp FIFO 0' control/status register */
uint32_t TSF0_CSR;
/* [0x80]: REG FIFO 'Timestamp FIFO 1' data output register 0 */
uint32_t TSF1_R0;
/* [0x84]: REG FIFO 'Timestamp FIFO 1' data output register 1 */
uint32_t TSF1_R1;
/* [0x88]: REG FIFO 'Timestamp FIFO 1' data output register 2 */
uint32_t TSF1_R2;
/* [0x8c]: REG FIFO 'Timestamp FIFO 1' control/status register */
uint32_t TSF1_CSR;
/* [0x90]: REG FIFO 'Timestamp FIFO 2' data output register 0 */
uint32_t TSF2_R0;
/* [0x94]: REG FIFO 'Timestamp FIFO 2' data output register 1 */
uint32_t TSF2_R1;
/* [0x98]: REG FIFO 'Timestamp FIFO 2' data output register 2 */
uint32_t TSF2_R2;
/* [0x9c]: REG FIFO 'Timestamp FIFO 2' control/status register */
uint32_t TSF2_CSR;
/* [0xa0]: REG FIFO 'Timestamp FIFO 3' data output register 0 */
uint32_t TSF3_R0;
/* [0xa4]: REG FIFO 'Timestamp FIFO 3' data output register 1 */
uint32_t TSF3_R1;
/* [0xa8]: REG FIFO 'Timestamp FIFO 3' data output register 2 */
uint32_t TSF3_R2;
/* [0xac]: REG FIFO 'Timestamp FIFO 3' control/status register */
uint32_t TSF3_CSR;
/* [0xb0]: REG FIFO 'Timestamp FIFO 4' data output register 0 */
uint32_t TSF4_R0;
/* [0xb4]: REG FIFO 'Timestamp FIFO 4' data output register 1 */
uint32_t TSF4_R1;
/* [0xb8]: REG FIFO 'Timestamp FIFO 4' data output register 2 */
uint32_t TSF4_R2;
/* [0xbc]: REG FIFO 'Timestamp FIFO 4' control/status register */
uint32_t TSF4_CSR;
};
#endif
-- -*- Mode: LUA; tab-width: 2 -*-
-- White-Rabbit dio spec
-- author: JDiaz <jdiaz@atc.ugr.es>
--
-- Use wbgen2 to generate code, documentation and more.
-- wbgen2 is available at:
-- http://www.ohwr.org/projects/wishbone-gen
--
peripheral {
name = "FMC-DIO-5chttla",
description = "This core for adding timing information to a standard GPIO based on WR-CORE information. \
\
Operation \
~~~~~~~~~~ \
The registers used on this module allows to time-stamping input values, generate and immediate output or programmed output at a given time. \
\
* Programmable output: Use seconds and cycles registers for specify trigger time for each channel. Strobe signal is mandatory to latch these values otherwise no output will be generated. \
* Immediate output could be generate by making active the corresponding bits of the 'Pulse generate immediately' register. \
* Pulse length can be adjusted by writing a integer value at corresponding registers. The duration will be its value x 8 ns. \
* There are some few clock cycles that the system is not ready to latch new time information to triggers. This could be checked by checking dio trigger signals. In addition to pooling, interrupts are generated. Note that because is no ready time is about 200 ns, it would almost always available for the PC. \
* To activate programmable or immediate output generation, please remember to set corresponding bits of the output configuration registers. Otherwise this system behaves as normal GPIO without additional timing features. \
* FIFOs store seconds and cycles values of time-stamped events. Note that the FIFO depth is 256 and that output generated signals will be also stored in the FIFOs in the same why that external input do. \
* Interrupts are handle based on EIC registers. FIFOs not empty as well as ready signals of each GPIO are the interrupt sources. \
\
Todo \
~~~~ \
* Improve documentation. \
\
Known issues \
~~~~~~~~~~~ \
* None";
prefix="dio";
hdl_entity="wrsw_dio_wb";
----------------------------------------------------
-- FIFOS & INTERRUPTS FOR INPUT EVENT TIME STAMPING
----------------------------------------------------
-- CHANNEL 0 INPUT FIFO
fifo_reg {
size = 256; -- or more. We'll see :)
direction = CORE_TO_BUS;
prefix = "tsf0";
name = "Timestamp FIFO 0";
description = "This FIFO holds the DIO input timestamps for each input channel. Each entry contains a single timestamp value consisting of 2 numbers:\
- tag_seconds - seconds time\
- tag_cycles - counter value for sub-second accuracy";
flags_bus = {FIFO_FULL, FIFO_EMPTY, FIFO_COUNT};
flags_dev = {FIFO_FULL, FIFO_EMPTY};
field {
name = "seconds time";
descritpion = "seconds time (LSB)";
prefix = "tag_seconds";
type = SLV;
size = 32;
};
field {
name = "seconds time H";
descritpion = "time in seconds (MSB)";
prefix = "tag_secondsH";
type = SLV;
size = 8;
};
field {
name = "Sub-second accuracy";
descritpion = "Sub-second accuracy based on systme clock counters";
prefix = "tag_cycles";
type = SLV;
size = 28;
};
};
-- CHANNEL 0 INTERRUPTS
irq {
name = "dio fifo not-empty 0";
description = "Interrupt active when dio input FIFO contains any timestamps.";
prefix = "nempty_0";
trigger = LEVEL_1;
};
-- CHANNEL 1 INPUT FIFO
fifo_reg {
size = 256; -- or more. We'll see :)
direction = CORE_TO_BUS;
prefix = "tsf1";
name = "Timestamp FIFO 1";
description = "This FIFO holds the DIO input timestamps for each input channel. Each entry contains a single timestamp value consisting of 2 numbers:\
- tag_seconds - time in seconds\
- tag_cycles - counter value for sub-second accuracy";
flags_bus = {FIFO_FULL, FIFO_EMPTY, FIFO_COUNT};
flags_dev = {FIFO_FULL, FIFO_EMPTY};
field {
name = "seconds time";
descritpion = "time in seconds (LSB)";
prefix = "tag_seconds";
type = SLV;
size = 32;
};
field {
name = "seconds time H";
descritpion = "time in seconds (MSB)";
prefix = "tag_secondsH";
type = SLV;
size = 8;
};
field {
name = "Sub-second accuracy";
descritpion = "Sub-second accuracy based on systme clock counters";
prefix = "tag_cycles";
type = SLV;
size = 28;
};
};
-- CHANNEL 1 INTERRUPTS
irq {
name = "dio fifo not-empty 1";
description = "Interrupt active when dio input FIFO contains any timestamps.";
prefix = "nempty_1";
trigger = LEVEL_1;
};
-- CHANNEL 2 INPUT FIFO
fifo_reg {
size = 256; -- or more. We'll see :)
direction = CORE_TO_BUS;
prefix = "tsf2";
name = "Timestamp FIFO 2";
description = "This FIFO holds the DIO input timestamps for each input channel. Each entry contains a single timestamp value consisting of 2 numbers:\
- tag_seconds - time in seconds\
- tag_cycles - counter value for sub-second accuracy";
flags_bus = {FIFO_FULL, FIFO_EMPTY, FIFO_COUNT};
flags_dev = {FIFO_FULL, FIFO_EMPTY};
field {
name = "seconds time";
descritpion = "time in seconds (LSB)";
prefix = "tag_seconds";
type = SLV;
size = 32;
};
field {
name = "seconds time H";
descritpion = "time in seconds (MSB)";
prefix = "tag_secondsH";
type = SLV;
size = 8;
};
field {
name = "Sub-second accuracy";
descritpion = "Sub-second accuracy based on systme clock counters";
prefix = "tag_cycles";
type = SLV;
size = 28;
};
};
-- CHANNEL 2 INTERRUPTS
irq {
name = "dio fifo not-empty 2";
description = "Interrupt active when dio input FIFO contains any timestamps.";
prefix = "nempty_2";
trigger = LEVEL_1;
};
-- CHANNEL 3 INPUT FIFO
fifo_reg {
size = 256; -- or more. We'll see :)
direction = CORE_TO_BUS;
prefix = "tsf3";
name = "Timestamp FIFO 3";
description = "This FIFO holds the DIO input timestamps for each input channel. Each entry contains a single timestamp value consisting of 2 numbers:\
- tag_seconds - time in seconds\
- tag_cycles - counter value for sub-second accuracy";
flags_bus = {FIFO_FULL, FIFO_EMPTY, FIFO_COUNT};
flags_dev = {FIFO_FULL, FIFO_EMPTY};
field {
name = "seconds time";
descritpion = "time in seconds (LSB)";
prefix = "tag_seconds";
type = SLV;
size = 32;
};
field {
name = "seconds time H";
descritpion = "time in seconds (MSB)";
prefix = "tag_secondsH";
type = SLV;
size = 8;
};
field {
name = "Sub-second accuracy";
descritpion = "Sub-second accuracy based on systme clock counters";
prefix = "tag_cycles";
type = SLV;
size = 28;
};
};
-- CHANNEL 3 INTERRUPTS
irq {
name = "dio fifo not-empty 3";
description = "Interrupt active when dio input FIFO contains any timestamps.";
prefix = "nempty_3";
trigger = LEVEL_1;
};
-- CHANNEL 4 INPUT FIFO
fifo_reg {
size = 256; -- or more. We'll see :)
direction = CORE_TO_BUS;
prefix = "tsf4";
name = "Timestamp FIFO 4";
description = "This FIFO holds the DIO input timestamps for each input channel. Each entry contains a single timestamp value consisting of 2 numbers:\
- tag_seconds - time in seconds\
- tag_cycles - counter value for sub-second accuracy";
flags_bus = {FIFO_FULL, FIFO_EMPTY, FIFO_COUNT};
flags_dev = {FIFO_FULL, FIFO_EMPTY};
field {
name = "seconds time";
descritpion = "time in seconds (LSB)";
prefix = "tag_seconds";
type = SLV;
size = 32;
};
field {
name = "seconds time H";
descritpion = "time in seconds (MSB)";
prefix = "tag_secondsH";
type = SLV;
size = 8;
};
field {
name = "Sub-second accuracy";
descritpion = "Sub-second accuracy based on systme clock counters";
prefix = "tag_cycles";
type = SLV;
size = 28;
};
};
-- CHANNEL 4 INTERRUPTS
irq {
name = "dio fifo not-empty 4";
description = "Interrupt active when dio input FIFO contains any timestamps.";
prefix = "nempty_4";
trigger = LEVEL_1;
};
-------------------------------------------------
-- REGISTERS FOR OUTPUT EVENT TIME-BASED TRIGGER
-------------------------------------------------
-- DIO CHANNEL 0: seconds value . Readable-writable the bus, readble from the device.
reg {
name = "fmc-dio 0 seconds-based trigger for pulse generation";
description = "trigger seconds value for dio output (LSB).";
prefix = "trig0";
field {
name = "seconds field";
description = "TBD";
prefix = "seconds";
type = SLV;
size = 32;
access_bus = READ_WRITE;
access_dev = READ_ONLY;
};
};
reg {
name = "fmc-dio 0 seconds-based trigger for pulse generation";
description = "trigger seconds value for dio output (MSB).";
prefix = "trigH0";
field {
name = "seconds field";
description = "Number of seconds";
prefix = "seconds";
type = SLV;
size = 8;
access_bus = READ_WRITE;
access_dev = READ_ONLY;
};
};
-- DIO CHANNEL 0: Cycles value. Readable-writable the bus, readble from the device.
reg {
name = "fmc-dio 0 cycles to trigger a pulse generation";
description = "sub-second accuracy values (clock cycles) to trigger dio output channels.";
prefix = "cyc0";
field {
name = "cycles field";
description = "Number of cycles in one second (depends on current clock frequency)";
prefix = "cyc";
type = SLV;
size = 28;
access_bus = READ_WRITE;
access_dev = READ_ONLY;
};
};
-- DIO CHANNEL 1: seconds value . Readable-writable the bus, readble from the device.
reg {
name = "fmc-dio 1 seconds-based trigger for pulse generation";
description = "trigger seconds value for dio output (LSB).";
prefix = "trig1";
field {
name = "seconds field";
description = "TBD";
prefix = "seconds";
type = SLV;
size = 32;
access_bus = READ_WRITE;
access_dev = READ_ONLY;
};
};
reg {
name = "fmc-dio 1 seconds-based trigger for pulse generation";
description = "trigger seconds value for dio output (MSB).";
prefix = "trigH1";
field {
name = "seconds field";
description = "Number of seconds";
prefix = "seconds";
type = SLV;
size = 8;
access_bus = READ_WRITE;
access_dev = READ_ONLY;
};
};
-- DIO CHANNEL 1: Cycles value. Readable-writable the bus, readble from the device.
reg {
name = "fmc-dio 1 cycles to trigger a pulse generation";
description = "sub-second accuracy values (clock cycles) to trigger dio output channels.";
prefix = "cyc1";
field {
name = "cycles field";
description = "Number of cycles in one second (depends on current clock frequency)";
prefix = "cyc";
type = SLV;
size = 28;
access_bus = READ_WRITE;
access_dev = READ_ONLY;
};
};
-- DIO CHANNEL 2: seconds value . Readable-writable the bus, readble from the device.
reg {
name = "fmc-dio 2 seconds-based trigger for pulse generation";
description = "trigger seconds value for dio output (LSB).";
prefix = "trig2";
field {
name = "seconds field";
description = "TBD";
prefix = "seconds";
type = SLV;
size = 32;
access_bus = READ_WRITE;
access_dev = READ_ONLY;
};
};
reg {
name = "fmc-dio 2 seconds-based trigger for pulse generation";
description = "trigger seconds value for dio output (MSB).";
prefix = "trigH2";
field {
name = "seconds field";
description = "Number of seconds";
prefix = "seconds";
type = SLV;
size = 8;
access_bus = READ_WRITE;
access_dev = READ_ONLY;
};
};
-- DIO CHANNEL 2: Cycles value. Readable-writable the bus, readble from the device.
reg {
name = "fmc-dio 2 cycles to trigger a pulse generation";
description = "sub-second accuracy values (clock cycles) to trigger dio output channels.";
prefix = "cyc2";
field {
name = "cycles field";
description = "Number of cycles in one second (depends on current clock frequency)";
prefix = "cyc";
type = SLV;
size = 28;
access_bus = READ_WRITE;
access_dev = READ_ONLY;
};
};
-- DIO CHANNEL 3: seconds value . Readable-writable the bus, readble from the device.
reg {
name = "fmc-dio 3 seconds-based trigger for pulse generation";
description = "trigger seconds value for dio output (LSB).";
prefix = "trig3";
field {
name = "seconds field";
description = "TBD";
prefix = "seconds";
type = SLV;
size = 32;
access_bus = READ_WRITE;
access_dev = READ_ONLY;
};
};
reg {
name = "fmc-dio 3 seconds-based trigger for pulse generation";
description = "trigger seconds value for dio output (MSB).";
prefix = "trigH3";
field {
name = "seconds field";
description = "Number of seconds";
prefix = "seconds";
type = SLV;
size = 8;
access_bus = READ_WRITE;
access_dev = READ_ONLY;
};
};
-- DIO CHANNEL 3: Cycles value. Readable-writable the bus, readble from the device.
reg {
name = "fmc-dio 3 cycles to trigger a pulse generation";
description = "sub-second accuracy values (clock cycles) to trigger dio output channels.";
prefix = "cyc3";
field {
name = "cycles field";
description = "Number of cycles in one second (depends on current clock frequency)";
prefix = "cyc";
type = SLV;
size = 28;
access_bus = READ_WRITE;
access_dev = READ_ONLY;
};
};
-- DIO CHANNEL 4: seconds value . Readable-writable the bus, readble from the device.
reg {
name = "fmc-dio 4 seconds-based trigger for pulse generation";
description = "trigger seconds value for dio output (LSB).";
prefix = "trig4";
field {
name = "seconds field";
description = "TBD";
prefix = "seconds";
type = SLV;
size = 32;
access_bus = READ_WRITE;
access_dev = READ_ONLY;
};
};
reg {
name = "fmc-dio 4 seconds-based trigger for pulse generation";
description = "trigger seconds value for dio output (MSB).";
prefix = "trigH4";
field {
name = "seconds field";
description = "Number of seconds";
prefix = "seconds";
type = SLV;
size = 8;
access_bus = READ_WRITE;
access_dev = READ_ONLY;
};
};
-- DIO CHANNEL 4: Cycles value. Readable-writable the bus, readble from the device.
reg {
name = "fmc-dio 4 cycles to trigger a pulse generation";
description = "sub-second accuracy values (clock cycles) to trigger dio output channels.";
prefix = "cyc4";
field {
name = "cycles field";
description = "Number of cycles in one second (depends on current clock frequency)";
prefix = "cyc";
type = SLV;
size = 28;
access_bus = READ_WRITE;
access_dev = READ_ONLY;
};
};
-----------------------------------------
-- OUTPUT CONFIGURATION/CONTROL REGISTERS
-----------------------------------------
-- Programmable IO mode selection
reg {
name = "FMC-DIO input/output configuration register. ";
description = "It allows to choose the I/0 mode for each channel. \
- [0-1]: The two first bit correspond to which signal its connected: 0 (00) GPIO, 1 (01) DIO core, 2 (10) WRPC core, 3 Undefined\
- [2]: Output Enable Negative (Input enable)\
- [3]: 50 Ohm termination enable";
prefix = "iomode";
field {
name = "channel0";
description = "Channel 0";
prefix = "ch0";
type = SLV;
size = 4;
access_bus = READ_WRITE;
access_dev = READ_WRITE;
load = LOAD_EXT;
};
field {
name = "channel1";
description = "Channel 1";
prefix = "ch1";
type = SLV;
size = 4;
access_bus = READ_WRITE;
access_dev = READ_WRITE;
load = LOAD_EXT;
};
field {
name = "channel2";
description = "Channel 2";
prefix = "ch2";
type = SLV;
size = 4;
access_bus = READ_WRITE;
access_dev = READ_WRITE;
load = LOAD_EXT;
};
field {
name = "channel3";
description = "Channel 3";
prefix = "ch3";
type = SLV;
size = 4;
access_bus = READ_WRITE;
access_dev = READ_WRITE;
load = LOAD_EXT;
};
field {
name = "channel4";
description = "Channel 4: Can be used in clock mode";
prefix = "ch4";
type = SLV;
size = 4;
access_bus = READ_WRITE;
access_dev = READ_WRITE;
load = LOAD_EXT;
};
};
-- Single-cycle strobe signal to latch the second/cycles values of the programamble output
reg {
name = "Time-programmable output strobe signal";
description = "It is used to latch second/cycles values generation just 1 clock cycle output ";
prefix = "latch";
field {
name = "Sincle-cycle strobe";
description = "It generates a one-clock cycle pulse for programmable time latching";
prefix = "time_ch0";
type = MONOSTABLE;
};
field {
name = "Sincle-cycle strobe";
description = "It generates a one-clock cycle pulse for programmable time latching";
prefix = "time_ch1";
type = MONOSTABLE;
};
field {
name = "Sincle-cycle strobe";
description = "It generates a one-clock cycle pulse for programmable time latching";
prefix = "time_ch2";
type = MONOSTABLE;
};
field {
name = "Sincle-cycle strobe";
description = "It generates a one-clock cycle pulse for programmable time latching";
prefix = "time_ch3";
type = MONOSTABLE;
};
field {
name = "Sincle-cycle strobe";
description = "It generates a one-clock cycle pulse for programmable time latching";
prefix = "time_ch4";
type = MONOSTABLE;
};
};
-- seconds trigger ready value. Readable-writable the bus, writable from the device.
reg {
name = "FMC-DIO time trigger is ready to accept a new trigger generation request";
description = "ready state, waiting new trigger commands for dio output.";
prefix = "trig";
field {
name = "trig_rdy field";
description = "TBD";
prefix = "rdy";
type = SLV;
size = 5;
access_bus = READ_ONLY;
access_dev = WRITE_ONLY;
};
};
-- DIO CHANNEL 0 trigger ready interrupt
irq {
name = "Channel 0 trigger ready interrupt";
description = "Interrupt active when time-programmable output channels accept new time trigger command.";
prefix = "trigger_ready_0";
trigger = LEVEL_1;
};
-- DIO CHANNEL 1 trigger ready interrupt
irq {
name = "Channel 1 trigger ready interrupt";
description = "Interrupt active when time-programmable output channels accept new time trigger command.";
prefix = "trigger_ready_1";
trigger = LEVEL_1;
};
-- DIO CHANNEL 2 trigger ready interrupt
irq {
name = "Channel 2 trigger ready interrupt";
description = "Interrupt active when time-programmable output channels accept new time trigger command.";
prefix = "trigger_ready_2";
trigger = LEVEL_1;
};
-- DIO CHANNEL 3 trigger ready interrupt
irq {
name = "Channel 3 trigger ready interrupt";
description = "Interrupt active when time-programmable output channels accept new time trigger command.";
prefix = "trigger_ready_3";
trigger = LEVEL_1;
};
-- DIO CHANNEL 4 trigger ready interrupt
irq {
name = "Channel 4 trigger ready interrupt";
description = "Interrupt active when time-programmable output channels accept new time trigger command.";
prefix = "trigger_ready_4";
trigger = LEVEL_1;
};
-- DIO CHANNEL 0: Programmable/immediate output pulse length . Readable-writable the bus, readble from the device.
reg {
name = "fmc-dio channel 0 Programmable/immediate output pulse length";
description = "Number of clk_ref clock ticks that output will be active";
prefix = "prog0_pulse";
field {
name = "number of ticks field for channel 0";
description = "ticks number";
prefix = "length";
type = SLV;
size = 28;
access_bus = READ_WRITE;
access_dev = READ_ONLY;
};
};
-- DIO CHANNEL 1: Programmable/immediate output pulse length . Readable-writable the bus, readble from the device.
reg {
name = "fmc-dio channel 1 Programmable/immediate output pulse length";
description = "Number of clk_ref clock ticks that output will be active";
prefix = "prog1_pulse";
field {
name = "number of ticks field for channel 1";
description = "ticks number";
prefix = "length";
type = SLV;
size = 28;
access_bus = READ_WRITE;
access_dev = READ_ONLY;
};
};
-- DIO CHANNEL 2: Programmable/immediate output pulse length . Readable-writable the bus, readble from the device.
reg {
name = "fmc-dio channel 2 Programmable/immediate output pulse length";
description = "Number of clk_ref clock ticks that output will be active";
prefix = "prog2_pulse";
field {
name = "number of ticks field for channel 2";
description = "ticks number";
prefix = "length";
type = SLV;
size = 28;
access_bus = READ_WRITE;
access_dev = READ_ONLY;
};
};
-- DIO CHANNEL 3: Programmable/immediate output pulse length . Readable-writable the bus, readble from the device.
reg {
name = "fmc-dio channel 3 Programmable/immediate output pulse length";
description = "Number of clk_ref clock ticks that output will be active";
prefix = "prog3_pulse";
field {
name = "number of ticks field for channel 3";
description = "ticks number";
prefix = "length";
type = SLV;
size = 28;
access_bus = READ_WRITE;
access_dev = READ_ONLY;
};
};
-- DIO CHANNEL 4: Programmable/immediate output pulse length . Readable-writable the bus, readble from the device.
reg {
name = "fmc-dio channel 4 Programmable/immediate output pulse length";
description = "Number of clk_ref clock ticks that output will be active";
prefix = "prog4_pulse";
field {
name = "number of ticks field for channel 4";
description = "ticks number";
prefix = "length";
type = SLV;
size = 28;
access_bus = READ_WRITE;
access_dev = READ_ONLY;
};
};
-----------------------------------------
-- IMMEDIATE OUTPUT REGISTERS
-----------------------------------------
-- Pulse generator.
reg {
name = "Pulse generate immediately";
description = "It is used to generate a pulse immediately";
prefix = "pulse";
field {
name = "pulse_gen_now_0";
description = "It generates a pulse";
prefix = "imm_0";
type = MONOSTABLE;
clock = "clk_asyn_i";
};
field {
name = "pulse_gen_now_1";
description = "It generates a pulse";
prefix = "imm_1";
type = MONOSTABLE;
clock = "clk_asyn_i";
};
field {
name = "pulse_gen_now_2";
description = "It generates a pulse";
prefix = "imm_2";
type = MONOSTABLE;
clock = "clk_asyn_i";
};
field {
name = "pulse_gen_now_3";
description = "It generates a pulse";
prefix = "imm_3";
type = MONOSTABLE;
clock = "clk_asyn_i";
};
field {
name = "pulse_gen_now_4";
description = "It generates a pulse";
prefix = "imm_4";
type = MONOSTABLE;
clock = "clk_asyn_i";
};
};
};
libspec.a
libspec.so
spec-cl
specmem
spec-fwloader
spec-vuart
wr-dio-cmd
wr-dio-pps
wr-dio-agent
wr-dio-ruler
stamp-frame
Makefile.specific
\ No newline at end of file
# If it exists includes Makefile.specific. In this Makefile, you should put
# specific Makefile code that you want to run before this. For example,
# build a particular environment.
-include Makefile.specific
# include parent_common.mk for buildsystem's defines
REPO_PARENT=../..
-include $(REPO_PARENT)/parent_common.mk
GIT_VERSION ?= $(shell git describe --dirty --long --tags)
CFLAGS += -ggdb -Wall -fPIC -I../kernel $(EXTRACFLAGS)
CFLAGS += -DGIT_VERSION=\"$(GIT_VERSION)\"
INSTALL = install
DESTDIR =
PREFIX = /usr/local
BINDIR = $(PREFIX)/sbin
LIBDIR = $(PREFIX)/lib
PROGS += wr-dio-cmd wr-dio-pps wr-dio-agent wr-dio-ruler
PROGS += stamp-frame
all: $(LIB) $(PROGS) $(LIBSHARED)
$(PROGS):
install:
$(INSTALL) -d $(DESTDIR)$(BINDIR)
$(INSTALL) -m 0755 -t $(DESTDIR)$(BINDIR) $(PROGS)
clean:
rm -f *.o $(PROGS) *~
# add the other unused targets, so the rule in ../Makefile works
modules modules_install:
/*
* Userspace API for hardware time stamping of network packets
*
* Copyright (C) 2008,2009 Intel Corporation
* Author: Patrick Ohly <patrick.ohly@intel.com>
*
*/
#ifndef _NET_TIMESTAMPING_H
#define _NET_TIMESTAMPING_H
#include <linux/socket.h> /* for SO_TIMESTAMPING */
/* SO_TIMESTAMPING gets an integer bit field comprised of these values */
enum {
SOF_TIMESTAMPING_TX_HARDWARE = (1<<0),
SOF_TIMESTAMPING_TX_SOFTWARE = (1<<1),
SOF_TIMESTAMPING_RX_HARDWARE = (1<<2),
SOF_TIMESTAMPING_RX_SOFTWARE = (1<<3),
SOF_TIMESTAMPING_SOFTWARE = (1<<4),
SOF_TIMESTAMPING_SYS_HARDWARE = (1<<5),
SOF_TIMESTAMPING_RAW_HARDWARE = (1<<6),
SOF_TIMESTAMPING_MASK =
(SOF_TIMESTAMPING_RAW_HARDWARE - 1) |
SOF_TIMESTAMPING_RAW_HARDWARE
};
/**
* struct hwtstamp_config - %SIOCSHWTSTAMP parameter
*
* @flags: no flags defined right now, must be zero
* @tx_type: one of HWTSTAMP_TX_*
* @rx_type: one of one of HWTSTAMP_FILTER_*
*
* %SIOCSHWTSTAMP expects a &struct ifreq with a ifr_data pointer to
* this structure. dev_ifsioc() in the kernel takes care of the
* translation between 32 bit userspace and 64 bit kernel. The
* structure is intentionally chosen so that it has the same layout on
* 32 and 64 bit systems, don't break this!
*/
struct hwtstamp_config {
int flags;
int tx_type;
int rx_filter;
};
/* possible values for hwtstamp_config->tx_type */
enum hwtstamp_tx_types {
/*
* No outgoing packet will need hardware time stamping;
* should a packet arrive which asks for it, no hardware
* time stamping will be done.
*/
HWTSTAMP_TX_OFF,
/*
* Enables hardware time stamping for outgoing packets;
* the sender of the packet decides which are to be
* time stamped by setting %SOF_TIMESTAMPING_TX_SOFTWARE
* before sending the packet.
*/
HWTSTAMP_TX_ON,
/*
* Enables time stamping for outgoing packets just as
* HWTSTAMP_TX_ON does, but also enables time stamp insertion
* directly into Sync packets. In this case, transmitted Sync
* packets will not received a time stamp via the socket error
* queue.
*/
HWTSTAMP_TX_ONESTEP_SYNC,
};
/* possible values for hwtstamp_config->rx_filter */
enum hwtstamp_rx_filters {
/* time stamp no incoming packet at all */
HWTSTAMP_FILTER_NONE,
/* time stamp any incoming packet */
HWTSTAMP_FILTER_ALL,
/* return value: time stamp all packets requested plus some others */
HWTSTAMP_FILTER_SOME,
/* PTP v1, UDP, any kind of event packet */
HWTSTAMP_FILTER_PTP_V1_L4_EVENT,
/* PTP v1, UDP, Sync packet */
HWTSTAMP_FILTER_PTP_V1_L4_SYNC,
/* PTP v1, UDP, Delay_req packet */
HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ,
/* PTP v2, UDP, any kind of event packet */
HWTSTAMP_FILTER_PTP_V2_L4_EVENT,
/* PTP v2, UDP, Sync packet */
HWTSTAMP_FILTER_PTP_V2_L4_SYNC,
/* PTP v2, UDP, Delay_req packet */
HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ,
/* 802.AS1, Ethernet, any kind of event packet */
HWTSTAMP_FILTER_PTP_V2_L2_EVENT,
/* 802.AS1, Ethernet, Sync packet */
HWTSTAMP_FILTER_PTP_V2_L2_SYNC,
/* 802.AS1, Ethernet, Delay_req packet */
HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ,
/* PTP v2/802.AS1, any layer, any kind of event packet */
HWTSTAMP_FILTER_PTP_V2_EVENT,
/* PTP v2/802.AS1, any layer, Sync packet */
HWTSTAMP_FILTER_PTP_V2_SYNC,
/* PTP v2/802.AS1, any layer, Delay_req packet */
HWTSTAMP_FILTER_PTP_V2_DELAY_REQ,
};
#endif /* _NET_TIMESTAMPING_H */
/*
* Copyright (C) 2010,2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released to the public domain as sample code to be customized.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netpacket/packet.h>
#include <net/if.h>
#include <net/ethernet.h>
#include <arpa/inet.h>
#include "net_tstamp.h" /* Actually, <linux/net_tstamp.h> */
#ifndef SO_TIMESTAMPNS
# define SO_TIMESTAMPNS 35
# define SCM_TIMESTAMPNS SO_TIMESTAMPNS
#endif
#ifndef SO_TIMESTAMPING
# define SO_TIMESTAMPING 37
# define SCM_TIMESTAMPING SO_TIMESTAMPING
#endif
#ifndef SIOCSHWTSTAMP
# define SIOCSHWTSTAMP 0x89b0
#endif
#ifndef ETH_P_1588
# define ETH_P_1588 0x88F7
#endif
static char git_version[] = "version: " GIT_VERSION;
/* This structure is used to collect stamping information */
struct ts_data {
struct timespec ns;
struct timespec hw[3]; /* software, hw-sys, hw-raw */
int error;
};
/* We can print such stamp info. Returns -1 with errno set on error */
int print_stamp(FILE *out, char *prefix, struct ts_data *tstamp, FILE *err)
{
int i;
static char *names[] = {"sw ", "hw-sys", "hw-raw"};
if (tstamp->error) {
if (err)
fprintf(err, "%s: %s\n", prefix, strerror(errno));
errno = tstamp->error;
return -1;
}
fprintf(out, "%s ns: %10li.%09li\n", prefix, tstamp->ns.tv_sec,
tstamp->ns.tv_nsec);
for (i = 0; i < 3; i++)
fprintf(out, "%s %s: %10li.%09li\n", prefix, names[i],
tstamp->hw[i].tv_sec,
tstamp->hw[i].tv_nsec);
fprintf(out, "\n");
return 0;
}
/*
* This function opens a socket and configures it for stamping.
* It is a library function, in a way, and was used as such
* when part of the "testing" programs of wr-switch-sw
*/
int make_stamping_socket(FILE *errchan, char *argv0, char *ifname,
int tx_type, int rx_filter, int bits,
unsigned char *macaddr)
{
struct ifreq ifr;
struct sockaddr_ll addr;
struct hwtstamp_config hwconfig;
int sock, iindex, enable = 1;
sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (sock < 0 && errchan)
fprintf(errchan, "%s: socket(): %s\n", argv0,
strerror(errno));
if (sock < 0)
return sock;
memset (&ifr, 0, sizeof(ifr));
strcpy(ifr.ifr_name, ifname);
/* hw interface information */
if (ioctl(sock, SIOCGIFINDEX, &ifr) < 0) {
if (errchan)
fprintf(errchan, "%s: SIOCGIFINDEX(%s): %s\n", argv0,
ifname, strerror(errno));
close(sock);
return -1;
}
iindex = ifr.ifr_ifindex;
if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0) {
if (errchan)
fprintf(errchan, "%s: SIOCGIFHWADDR(%s): %s\n", argv0,
ifname, strerror(errno));
close(sock);
return -1;
}
memcpy(macaddr, ifr.ifr_hwaddr.sa_data, 6);
/* Also, enable stamping for the hw interface */
memset(&hwconfig, 0, sizeof(hwconfig));
hwconfig.tx_type = tx_type;
hwconfig.rx_filter = rx_filter;
ifr.ifr_data = (void *)&hwconfig;
if (ioctl(sock, SIOCSHWTSTAMP, &ifr) < 0) {
if (errchan)
fprintf(errchan, "%s: SIOCSHWSTAMP(%s): %s\n", argv0,
ifname, strerror(errno));
close(sock);
return -1;
}
/* bind and setsockopt */
memset(&addr, 0, sizeof(addr));
addr.sll_family = AF_PACKET;
addr.sll_protocol = htons(ETH_P_ALL);
addr.sll_ifindex = iindex;
addr.sll_pkttype = PACKET_OUTGOING;
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
if (errchan)
fprintf(errchan, "%s: SIOCSHWSTAMP(%s): %s\n", argv0,
ifname, strerror(errno));
close(sock);
return -1;
}
if (setsockopt(sock, SOL_SOCKET, SO_TIMESTAMP,
&enable, sizeof(enable)) < 0) {
if (errchan)
fprintf(errchan, "%s: setsockopt(TIMESTAMP): %s\n",
argv0, strerror(errno));
close(sock);
return -1;
}
if (setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING,
&bits, sizeof(bits)) < 0) {
if (errchan)
fprintf(errchan, "%s: setsockopt(TIMESTAMPING): %s\n",
argv0, strerror(errno));
close(sock);
return -1;
}
return sock;
}
/*
* Another "library" function, actually only used by the following two
*/
static void __collect_data(struct msghdr *msgp, struct ts_data *tstamp)
{
struct cmsghdr *cm;
struct timespec *tsptr;
if (!tstamp)
return;
memset(tstamp, 0, sizeof(*tstamp));
/* Extract data from the cmsg */
for (cm = CMSG_FIRSTHDR(msgp); cm; cm = CMSG_NXTHDR(msgp, cm)) {
tsptr = (struct timespec *)CMSG_DATA(cm);
if (0) {
printf("level %i, type %i, len %zi\n", cm->cmsg_level,
cm->cmsg_type, cm->cmsg_len);
}
if (cm->cmsg_level != SOL_SOCKET)
continue;
if (cm->cmsg_type == SO_TIMESTAMPNS)
tstamp->ns = *tsptr;
if (cm->cmsg_type == SO_TIMESTAMPING)
memcpy(tstamp->hw, tsptr, sizeof(tstamp->hw));
}
}
/*
* These functions are like send/recv but handle stamping too.
*/
ssize_t send_and_stamp(int sock, void *buf, size_t len, int flags,
struct ts_data *tstamp)
{
struct msghdr msg; /* this line and more from timestamping.c */
struct iovec entry;
struct sockaddr_ll from_addr;
struct {
struct cmsghdr cm;
char control[512];
} control;
char data[3*1024];
int i, j, ret;
ret = send(sock, buf, len, flags);
if (ret < 0)
return ret;
/* Then, get back from the error queue */
memset(&msg, 0, sizeof(msg));
msg.msg_iov = &entry;
msg.msg_iovlen = 1;
entry.iov_base = data;
entry.iov_len = sizeof(data);
msg.msg_name = (caddr_t)&from_addr;
msg.msg_namelen = sizeof(from_addr);
msg.msg_control = &control;
msg.msg_controllen = sizeof(control);
j = 100; /* number of trials */
while ( (i = recvmsg(sock, &msg, MSG_ERRQUEUE)) < 0 && j--)
usleep(10000); /* retry for 1 second */
if (i < 0) {
if (tstamp) {
memset(tstamp, 0, sizeof(*tstamp));
tstamp->error = ETIMEDOUT;
}
return ret;
}
if (getenv("STAMP_VERBOSE")) {
int b;
printf("send %i =", i);
for (b = 0; b < i && b < 20; b++)
printf(" %02x", data[b] & 0xff);
putchar('\n');
}
/* FIX<E: Check that the actual data is what we sent */
__collect_data(&msg, tstamp);
return ret;
}
ssize_t recv_and_stamp(int sock, void *buf, size_t len, int flags,
struct ts_data *tstamp)
{
int ret;
struct msghdr msg;
struct iovec entry;
struct sockaddr_ll from_addr;
struct {
struct cmsghdr cm;
char control[512];
} control;
if (0) {
/* we can't really call recv, do it with cmsg alone */
ret = recv(sock, buf, len, flags);
} else {
memset(&msg, 0, sizeof(msg));
msg.msg_iov = &entry;
msg.msg_iovlen = 1;
entry.iov_base = buf;
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);
ret = recvmsg(sock, &msg, 0);
if (ret < 0)
return ret;
if (getenv("STAMP_VERBOSE")) {
int b;
printf("recv %i =", ret);
for (b = 0; b < ret && b < 20; b++)
printf(" %02x", ((char *)buf)[b] & 0xff);
putchar('\n');
}
__collect_data(&msg, tstamp);
}
return ret;
}
/* Add and subtract timespec */
void ts_add(struct timespec *t1, struct timespec *t2)
{
t1->tv_sec += t2->tv_sec;
t1->tv_nsec += t2->tv_nsec;
if (t1->tv_nsec > 1000 * 1000 * 1000) {
t1->tv_nsec -= 1000 * 1000 * 1000;
t1->tv_sec++;
}
}
void ts_sub(struct timespec *t1, struct timespec *t2)
{
t1->tv_sec -= t2->tv_sec;
t1->tv_nsec -= t2->tv_nsec;
if (t1->tv_nsec < 0) {
t1->tv_nsec += 1000 * 1000 * 1000;
t1->tv_sec--;
}
}
/*
* Ok, the library-like part is over, we are at main now
*/
#define STAMP_PROTO 0xf001
/* This is the frame we are exchanging back and forth */
struct frame {
struct ether_header h;
uint16_t phase; /* 0 = transmit, 1 = tx follow up */
struct timespec ts[4];
unsigned char filler[64]; /* to reach minimum size for sure */
};
void report_times(struct timespec *ts)
{
struct timespec rtt, tmp, fw, bw;
int i;
for (i = 0; i < 4; i++)
printf("timestamp T%i: %9li.%09li\n", i+1,
ts[i].tv_sec, ts[i].tv_nsec);
/* calculate round trip time, forward, backward */
rtt = ts[3];
ts_sub(&rtt, &ts[0]);
tmp = ts[2];
ts_sub(&tmp, &ts[1]);
ts_sub(&rtt, &tmp);
fw = ts[1];
ts_sub(&fw, &ts[0]);
bw = ts[3];
ts_sub(&bw, &ts[2]);
printf("round trip time: %9li.%09li\n", rtt.tv_sec, rtt.tv_nsec);
printf("forward time: %9li.%09li\n", fw.tv_sec, fw.tv_nsec);
printf("backward time: %9li.%09li\n", bw.tv_sec, bw.tv_nsec);
}
/* send a frame, and then again after filling the tx time at offset given */
void send_one_with_followup(int sock, struct frame *f, unsigned char *mac,
int offset)
{
struct ts_data stamp;
/* Fill ether header */
memset(&f->h.ether_dhost, 0xff, ETH_ALEN); /* broadcast */
memcpy(&f->h.ether_shost, mac, ETH_ALEN);
f->h.ether_type = htons(STAMP_PROTO);
f->phase = 0;
if (send_and_stamp(sock, f, sizeof(*f), 0, &stamp) < 0)
fprintf(stderr, "send_and_stamp: %s\n", strerror(errno));
f->phase = 1;
f->ts[offset] = stamp.hw[2]; /* hw raw */
if (send_and_stamp(sock, f, sizeof(*f), 0, NULL) < 0)
fprintf(stderr, "send_and_stamp: %s\n", strerror(errno));
if (getenv("STAMP_PRINT_ALL"))
print_stamp(stdout, "send", &stamp, stderr);
}
/* receive a frame, timestamping it, and receive the followup too; save ts */
void recv_one_with_followup(int sock, struct frame *f, unsigned char *mac,
int offset)
{
struct ts_data stamp;
int ret;
while (1) { /* repeat until a good frame is received */
ret = recv_and_stamp(sock, f, sizeof(*f), MSG_TRUNC, &stamp);
if (ret < 0) {
fprintf(stderr, "recv_and_stamp: %s\n",
strerror(errno));
continue;
}
if (ret != sizeof(*f))
continue;
if (ntohs(f->h.ether_type) != STAMP_PROTO)
continue;
if (f->phase != 0)
continue;
break;
}
/* receive another one, lazily don't wait */
if (recv_and_stamp(sock, f, sizeof(*f), MSG_TRUNC, NULL) < 0)
fprintf(stderr, "recv_and_stamp: %s\n",
strerror(errno));
f->ts[offset] = stamp.hw[2];
if (getenv("STAMP_PRINT_ALL"))
print_stamp(stdout, "recv", &stamp, stderr);
}
static void print_version(char *pname)
{
printf("%s %s\n", pname, git_version);
}
int main(int argc, char **argv)
{
static struct frame f;
int sock;
unsigned char macaddr[6];
int listenmode = 0;
int howto = SOF_TIMESTAMPING_MASK; /* everything */
if ((argc == 2) && (!strcmp(argv[1], "-V"))) {
print_version(argv[0]);
exit(0);
}
/* From ./net_tstamp.h, these are the "howto" values
*
* SOF_TIMESTAMPING_TX_HARDWARE = 1,
* SOF_TIMESTAMPING_TX_SOFTWARE = 2
* SOF_TIMESTAMPING_RX_HARDWARE = 4,
* SOF_TIMESTAMPING_RX_SOFTWARE = 8,
* SOF_TIMESTAMPING_SOFTWARE = 16,
* SOF_TIMESTAMPING_SYS_HARDWARE = 32,
* SOF_TIMESTAMPING_RAW_HARDWARE = 64,
*/
if (argc == 3 && !strcmp(argv[2], "listen")) {
listenmode = 1;
argc--;
}
if (argc != 2) {
fprintf(stderr, "%s: Use \"%s [-V] <ifname> [listen]\n", argv[0],
argv[0]);
exit(1);
}
printf("%s: Using interface %s, with all timestamp options active\n",
argv[0], argv[1]);
/* Create a socket to use for stamping, use the library code above */
sock = make_stamping_socket(stderr, argv[0], argv[1],
HWTSTAMP_TX_ON, HWTSTAMP_FILTER_ALL,
howto, macaddr);
if (sock < 0) /* message already printed */
exit(1);
if (listenmode) {
/* forever reply */
while (1) {
recv_one_with_followup(sock, &f, macaddr, 1);
send_one_with_followup(sock, &f, macaddr, 2);
}
}
/* send mode: send first, then receive, then print */
send_one_with_followup(sock, &f, macaddr, 0);
recv_one_with_followup(sock, &f, macaddr, 3);
report_times(f.ts);
exit(0);
}
/*
* Copyright (C) 2012 CERN (www.cern.ch)
* Copyright (C) 2019 Seven Solutions (sevensols.com)
* Author: Alessandro Rubini <rubini@gnudd.com>
* Author: Miguel Jimenez Lopez <miguel.jimenez@sevensols.com>
*
* Released to the public domain as sample code to be customized.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
/* Typical use: "wr-dio-agent wr1 /dev/fmc-dio1" */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/if_ether.h>
#include <net/if.h>
#include <netpacket/packet.h>
#include "fmc-dio.h"
static char git_version[] = "version: " GIT_VERSION;
#define RULER_PROTO 0x5752 /* WR */
/*
* Lazily, use global variables, so the code has less parameter passing.
* Everything in this file is using "agent_" as a prefix, to ease the
* reader -- and remember that <TAB> aligns at 8 spaces, not 4.
*/
char *agent_prgname;
int agent_sock;
char *agent_ifname;
int agent_fd_dio;
char *agent_dio_name;
struct ifreq agent_ifr;
/* Boring network stuff extracted from main function */
static int agent_open_wr_sock(char *ifname, char *dio_name)
{
struct sockaddr_ll addr;
int sock, fd_dio, ifindex;
sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (sock < 0) {
fprintf(stderr, "%s: socket(): %s\n",
agent_prgname, strerror(errno));
return -1;
}
fd_dio = open(dio_name, O_RDWR);
if(fd_dio < 0) {
fprintf(stderr, "%s: open(): %s\n",
agent_prgname, strerror(errno));
close(sock);
return -1;
}
memset(&agent_ifr, 0, sizeof(agent_ifr));
strncpy(agent_ifr.ifr_name, ifname, sizeof(agent_ifr.ifr_name));
if (ioctl(fd_dio, PRIV_MEZZANINE_ID, (unsigned long) dio_name) < 0
/* EAGAIN is special: it means we have no ID to check yet */
&& errno != EAGAIN) {
fprintf(stderr, "%s: ioctl(PRIV_MEZZANINE_ID(%s)): %s\n",
agent_prgname, dio_name, strerror(errno));
close(sock);
return -1;
}
/* Retieve the interfaceindex */
if (ioctl(sock, SIOCGIFINDEX, &agent_ifr) < 0) {
fprintf(stderr, "%s: SIOCGIFINDEX(%s): %s\n", agent_prgname,
ifname, strerror(errno));
close(sock);
return -1;
}
ifindex = agent_ifr.ifr_ifindex;
/* Bind to the interface, so to be able to receive */
memset(&addr, 0, sizeof(addr));
addr.sll_family = AF_PACKET;
addr.sll_protocol = htons(RULER_PROTO);
addr.sll_ifindex = ifindex;
addr.sll_pkttype = PACKET_BROADCAST; /* that's what ruler sends */
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
fprintf(stderr, "%s: bind(%s): %s\n", agent_prgname,
ifname, strerror(errno));
close(sock);
return -1;
}
/* save in globals for later */
agent_sock = sock;
agent_ifname = ifname;
agent_fd_dio = fd_dio;
agent_dio_name = dio_name;
return 0;
}
static void print_version(char *pname)
{
printf("%s %s\n", pname, git_version);
}
/* And a simple main with the loop inside */
int main(int argc, char **argv)
{
int len;
/* We are receiving stuff in this frame */
static struct frame {
struct ether_header h;
unsigned char pad[2];
struct wr_dio_cmd cmd;
} f;
if ((argc == 2) && (!strcmp(argv[1], "-V"))) {
print_version(argv[0]);
exit(0);
}
if (argc != 3) {
fprintf(stderr, "%s: Use \"%s [-V] <wr-if> <dio-dev>\"\n",
argv[0], argv[0]);
exit(1);
}
agent_prgname = argv[0];
/* All functions print error messages by themselves, so just exit */
if (agent_open_wr_sock(argv[1],argv[2]) < 0)
exit(1);
while (1) {
len = recv(agent_sock, &f, sizeof(f), MSG_TRUNC);
if (len != sizeof(f)) {
fprintf(stderr, "%s: recevied unexpected frame length"
" (%i instead of %zu)\n", agent_prgname, len,
sizeof(f));
continue;
}
if (ntohs(f.h.ether_type) != RULER_PROTO) {
fprintf(stderr, "%s: received unexpected eth type"
" (%04x instead of %04x)\n", agent_prgname,
ntohs(f.h.ether_type), RULER_PROTO);
continue;
}
if (0)
printf("command %i, ch %i, t %li.%09li\n",
f.cmd.command, f.cmd.channel, f.cmd.t[0].tv_sec,
f.cmd.t[0].tv_nsec);
/* Then simply pass it to the hardware */
if (ioctl(agent_fd_dio, PRIV_MEZZANINE_CMD, (unsigned long)&f.cmd) < 0) {
fprintf(stderr, "%s: ioctl(PRIV_MEZZANINE_CMD(%s)): "
"%s\n", agent_prgname, agent_dio_name,
strerror(errno));
return -1;
}
}
exit(0);
}
/*
* Copyright (C) 2012 CERN (www.cern.ch)
* Copyright (C) 2019 Seven Solutions (sevensols.com)
* Author: Alessandro Rubini <rubini@gnudd.com>
* Author: Miguel Jimenez Lopez <miguel.jimenez@sevensols.com>
*
* Released to the public domain as sample code to be customized.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "fmc-dio.h"
static char git_version[] = "version: " GIT_VERSION;
char *prgname;
char c;
int fd;
char *devname;
// FIXME: Remove it if not used
void *last_cmd;
struct wr_dio_cmd _cmd;
struct wr_dio_cmd *cmd = &_cmd;
static int parse_ts(char *s, struct timespec *ts)
{
int i, n;
unsigned long nano;
char c;
/*
* Hairy: if we scan "%ld%lf", the 0.009999 will become 9998 micro.
* Thus, scan as integer and string, so we can count leading zeros
*/
nano = 0;
ts->tv_sec = 0;
ts->tv_nsec = 0;
if ( (i = sscanf(s, "%ld.%ld%c", &ts->tv_sec, &nano, &c)) == 1)
return 0; /* seconds only */
if (i == 3)
return -1; /* trailing crap */
if (i == 0)
if (sscanf(s, ".%ld%c", &nano, &c) != 1)
return -1; /* leading or trailing crap */
s = strchr(s, '.') + 1;
n = strlen(s);
if (n > 9)
return -1; /* too many decimals */
while (n < 9) {
nano *= 10;
n++;
}
ts->tv_nsec = nano;
return 0;
}
static int scan_pulse(int argc, char **argv)
{
char c;
if (argc != 4 && argc != 6) {
fprintf(stderr, "%s: %s: wrong number of arguments\n",
prgname, argv[0]);
fprintf(stderr, " Use: %s <channel> <duration> <when> "
"[<period> <count>]\n", argv[0]);
return -1;
}
if (sscanf(argv[1], "%hi%c", &cmd->channel, &c) != 1
|| cmd->channel > 4) {
fprintf(stderr, "%s: %s: not a channel number \"%s\"\n",
prgname, argv[0], argv[1]);
return -1;
}
/* Duration is first time argument but position 1 for ioctl */
if (parse_ts(argv[2], cmd->t + 1) < 0) {
fprintf(stderr, "%s: %s: invalid time \"%s\"\n",
prgname, argv[0], argv[2]);
return -1;
}
if (cmd->t[1].tv_sec) {
fprintf(stderr, "%s: %s: duration must be < 1s (got \"%s\")\n",
prgname, argv[0], argv[2]);
return -1;
}
/* Next argument is the "when", position 0 in ioctl timestamp array */
if (!strcmp(argv[3], "now")) {
cmd->flags |= WR_DIO_F_NOW;
} else {
char *s2 = argv[3];
if (s2[0] == '+') {
cmd->flags |= WR_DIO_F_REL;
s2++;
}
if (parse_ts(s2, cmd->t) < 0) {
fprintf(stderr, "%s: %s: invalid time \"%s\"\n",
prgname, argv[0], argv[3]);
return -1;
}
}
/* If argc is 6, we have period and count */
if (argc == 6) {
cmd->flags |= WR_DIO_F_LOOP;
if (parse_ts(argv[4], cmd->t + 2) < 0) {
fprintf(stderr, "%s: %s: invalid time \"%s\"\n",
prgname, argv[0], argv[4]);
return -1;
}
if (sscanf(argv[5], "%i%c", &cmd->value, &c) != 1) {
fprintf(stderr, "%s: %s: invalid count \"%s\"\n",
prgname, argv[0], argv[5]);
return -1;
}
}
if (ioctl(fd, PRIV_MEZZANINE_CMD, (unsigned long) cmd) < 0) {
fprintf(stderr, "%s: ioctl(PRIV_MEZZANINE_CMD(%s)): %s\n",
prgname, devname, strerror(errno));
return -1;
}
return 0;
}
static int scan_stamp(int argc, char **argv, int ismask)
{
int i, ch;
char c;
cmd->flags = 0;
if (argc == 3 && !strcmp(argv[2], "wait")) {
cmd->flags = WR_DIO_F_WAIT;
argc = 2;
}
if (argc == 1) {
ismask = 1;
ch = 0x1f;
} else if (argc == 2) {
if (sscanf(argv[1], "%i%c", &ch, &c) != 1) {
fprintf(stderr, "%s: %s: not a channel \"%s\"\n",
prgname, argv[0], argv[1]);
exit(1);
}
if (ch < 0 || ch > 31 || (!ismask && ch > 4)) {
fprintf(stderr, "%s: %s: out of range channel \"%s\"\n",
prgname, argv[0], argv[1]);
exit(1);
}
} else {
fprintf(stderr, "%s: %s: wrong number of arguments\n",
prgname, argv[0]);
if (ismask)
fprintf(stderr, " Use: %s [<channel-mask>]\n",
argv[0]);
else
fprintf(stderr, " Use: %s [<channel>] [wait]\n",
argv[0]);
return -1;
}
if (ismask)
cmd->flags = WR_DIO_F_MASK;
while (1) {
cmd->channel = ch;
errno = 0;
if (ioctl(fd, PRIV_MEZZANINE_CMD, (unsigned long) cmd) < 0 ) {
if (errno == EAGAIN)
break;
fprintf(stderr, "%s: ioctl(PRIV_MEZZANINE_CMD(%s)): "
"%s\n", prgname, devname, strerror(errno));
return -1;
}
for (i = 0; i < cmd->nstamp; i++)
printf("ch %i, %9li.%09li\n", cmd->channel,
(long)cmd->t[i].tv_sec, cmd->t[i].tv_nsec);
}
return 0;
}
static int one_mode(int c, int index)
{
if (c == '-')
return 0;
cmd->channel |= 1 << index;
//Add error message for channel 0
if(index==0 && strchr("dD01",c))
{
fprintf(stderr, "Error: Only p/P modes are available as ouput mode for channel 0\n");
return -1;
}
switch(c) {
case 'D':
cmd->value |= WR_DIO_INOUT_TERM << index;
case 'd':
cmd->value |= WR_DIO_INOUT_DIO << index;
cmd->value |= WR_DIO_INOUT_OUTPUT << index;
break;
case 'C':
cmd->value |= WR_DIO_INOUT_TERM << index;
case 'c':
cmd->value |= WR_DIO_INOUT_DIO << index;
cmd->value |= WR_DIO_INOUT_VALUE << index;
if(index!=4)
fprintf(stdout, "Warning: Clock mode is only available for last channel (ch4)\n,"
"(on other channel it corresponds to input mode without interruptions)\n");
break;
case 'P':
cmd->value |= WR_DIO_INOUT_TERM << index;
case 'p':
cmd->value |= WR_DIO_INOUT_DIO << index;
cmd->value |= WR_DIO_INOUT_VALUE << index;
cmd->value |= WR_DIO_INOUT_OUTPUT << index;
break;
case 'I':
cmd->value |= WR_DIO_INOUT_TERM << index;
case 'i':
break;
case '1':
cmd->value |= WR_DIO_INOUT_VALUE << index;
case '0':
cmd->value |= WR_DIO_INOUT_OUTPUT << index;
break;
default:
fprintf(stderr, "%s: mode: invalid mode '%c'\n",
prgname, c);
return -1;
}
return 0;
}
static int scan_inout(int argc, char **argv)
{
int i, ch;
char c;
cmd->flags = WR_DIO_F_MASK;
cmd->channel = 0;
cmd->value = 0;
if (argc == 2) {
if (strlen(argv[1]) != 5) {
fprintf(stderr, "%s: %s: wrong argument \"%s\"\n",
prgname, argv[0], argv[1]);
exit(1);
}
for (i = 0; i < 5; i++)
if (one_mode(argv[1][i], i) < 0)
return -1;
} else {
if (argc < 3 || argc > 11 || ((argc & 1) == 0)) {
fprintf(stderr, "%s: %s: wrong number of arguments\n",
prgname, argv[0]);
return -1;
}
while (argc >= 3) {
if (sscanf(argv[1], "%i%c", &ch, &c) != 1
|| ch < 0 || ch > 4) {
fprintf(stderr, "%s: mode: invalid channel "
"\"%s\"\n", prgname, argv[1]);
return -1;
}
if (strlen(argv[2]) != 1) {
fprintf(stderr, "%s: mode: invalid mode "
"\"%s\"\n", prgname, argv[2]);
return -1;
}
if (one_mode(argv[2][0], ch) < 0)
return -1;
argv += 2;
argc -= 2;
}
}
if (ioctl(fd, PRIV_MEZZANINE_CMD, cmd) < 0) {
fprintf(stderr, "%s: ioctl(PRIV_MEZZANINE_CMD(%s)): %s\n",
prgname, devname, strerror(errno));
return -1;
}
return 0;
}
static void print_version(char *pname)
{
printf("%s %s\n", pname, git_version);
}
int main(int argc, char **argv)
{
prgname = argv[0];
argv++, argc--;
if ((argc == 2) && (!strcmp(argv[1], "-V"))) {
print_version(argv[0]);
exit(0);
}
if (argc < 2) {
fprintf(stderr, "%s: use \"%s [-V] <dev> <cmd> [...]\"\n",
prgname, prgname);
exit(1);
}
devname = argv[0];
argv++, argc--;
fd = open(devname,O_RDWR);
if (fd < 0) {
fprintf(stderr, "%s: open(): %s\n",
prgname, strerror(errno));
exit(1);
}
if (ioctl(fd, PRIV_MEZZANINE_ID, (unsigned long) devname) < 0
/* EAGAIN is special: it means we have no ID to check yet */
&& errno != EAGAIN) {
fprintf(stderr, "%s: ioctl(PRIV_MEZZANINE_ID(%s)): %s\n",
prgname, devname, strerror(errno));
}
/*
* Parse the command line:
*
* pulse <ch> .<len> <seconds>.<fraction>
* pulse <ch> .<len> now
* pulse <ch> .<len> +<seconds>.<fraction>
*
* stamp [<channel>]
* stampm [<mask>]
*
* mode <01234>
* mode <ch> <mode> [...]
*/
if (!strcmp(argv[0], "pulse")) {
cmd->command = WR_DIO_CMD_PULSE;
if (scan_pulse(argc, argv) < 0)
exit(1);
} else if (!strcmp(argv[0], "stamp")) {
cmd->command = WR_DIO_CMD_STAMP;
if (scan_stamp(argc, argv, 0 /* no mask */) < 0)
exit(1);
} else if (!strcmp(argv[0], "stampm")) {
cmd->command = WR_DIO_CMD_STAMP;
if (scan_stamp(argc, argv, 1 /* mask */) < 0)
exit(1);
} else if (!strcmp(argv[0], "mode")) {
cmd->command = WR_DIO_CMD_INOUT;
if (scan_inout(argc, argv) < 0)
exit(1);
} else {
fprintf(stderr, "%s: unknown command \"%s\"\n", prgname,
argv[0]);
exit(1);
}
// FIXME: Is this useful?
last_cmd = (void *)cmd;
exit(0);
}
/*
* Copyright (C) 2012 CERN (www.cern.ch)
* Copyright (C) 2019 Seven Solutions (sevensols.com)
* Author: Alessandro Rubini <rubini@gnudd.com>
* Author: Miguel Jimenez Lopez <miguel.jimenez@sevensols.com>
*
* Released to the public domain as sample code to be customized.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "fmc-dio.h"
/**
* This takes two arguments: interface name and channel number
*
* This simple tools just show an example of how to program with wr-nic/dio.
* If you want to measure WR timing we suggest to use the hardwire PPS on channel 0
* that gives a more accurate precision.
**/
static char git_version[] = "version: " GIT_VERSION;
static void print_version(char *pname)
{
printf("%s %s\n", pname, git_version);
}
int main(int argc, char **argv)
{
struct wr_dio_cmd _cmd;
struct wr_dio_cmd *cmd = &_cmd;
char *prgname = argv[0];
char *devname = "fmc-dio0";
int fd, ch, charg = 1;
char c;
if ((argc == 2) && (!strcmp(argv[1], "-V"))) {
print_version(argv[0]);
exit(0);
}
if (argc < 2 || argc > 3) {
fprintf(stderr, "%s: Use \"%s [<devname>] <channel>\"\n",
prgname, prgname);
exit(1);
}
if (argc == 3) {
devname = argv[1];
charg++;
}
if (sscanf(argv[charg], "%i%c", &ch, &c) != 1) {
fprintf(stderr, "%s: Not a channel number \"%s\"\n",
prgname, argv[charg]);
exit(1);
}
if (ch < 1 || ch > 4) {
fprintf(stderr, "%s: Out of range channel number \"%s\"\n",
prgname, argv[charg]);
exit(1);
}
fprintf(stderr, "%s: Using dev \"%s\" and channel %i for "
"pps output\n", prgname,devname, ch);
/* The following is standard stuff to access wr-dio */
fd = open(devname, O_RDWR);
if (fd < 0) {
fprintf(stderr, "%s: open(): %s\n",
prgname, strerror(errno));
exit(1);
}
if (ioctl(fd, PRIV_MEZZANINE_ID, (unsigned long) devname) < 0
/* EAGAIN is special: it means we have no ID to check yet */
&& errno != EAGAIN) {
fprintf(stderr, "%s: ioctl(PRIV_MEZZANINE_ID(%s)): %s\n",
prgname, devname, strerror(errno));
}
/* Fill the command structure */
memset(cmd, 0, sizeof(*cmd));
cmd->command = WR_DIO_CMD_PULSE;
cmd->channel = ch;
cmd->flags = WR_DIO_F_REL | WR_DIO_F_LOOP;
/* Number of loops: -1 <=> Inf */
cmd->value = -1;
/* 2s delay to have time to send and process this command */
cmd->t[0].tv_sec = 2;
/* 1ms pulse width */
cmd->t[1].tv_nsec = 1000 * 1000;
/* Loop period */
cmd->t[2].tv_sec = 1;
/* Give it to the driver and we are done */
if (ioctl(fd, PRIV_MEZZANINE_CMD, (unsigned long) cmd) < 0) {
fprintf(stderr, "%s: ioctl(PRIV_MEZZANINE_CMD(%s)): %s\n",
prgname, devname, strerror(errno));
exit(1);
}
exit(0);
}
/*
* Copyright (C) 2012 CERN (www.cern.ch)
* Copyright (C) 2019 Seven Solutions (sevensols.com)
* Author: Alessandro Rubini <rubini@gnudd.com>
* Author: Miguel Jimenez Lopez <miguel.jimenez@sevensols.com>
*
* Released to the public domain as sample code to be customized.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
/* Typical use: "wr-dio-ruler wr1 /dev/fmc-dio1 IN0 L3+0.001 R3+0.001 L4+0.002" */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/if_ether.h>
#include <net/if.h>
#include <netpacket/packet.h>
#include "fmc-dio.h"
static char git_version[] = "version: " GIT_VERSION;
#define RULER_PROTO 0x5752 /* WR */
/*
* Lazily, use global variables, so the code has less parameter passing.
* Everything in this file is using "ruler_" as a prefix, to ease the
* reader -- and remember that <TAB> aligns at 8 spaces, not 4.
*/
char *ruler_prgname;
int ruler_sock;
char *ruler_ifname;
int ruler_fd_dio;
char *ruler_dio_name;
struct wr_dio_cmd ruler_cmd;
struct ifreq ruler_ifr;
unsigned char ruler_macaddr[ETH_ALEN];
struct ruler_action {
int isremote;
int channel;
struct timespec delay;
};
/* Boring parsing separated to a separate function (same code as elsewhere) */
static int parse_ts(char *s, struct timespec *ts)
{
int i, n;
unsigned long nano;
char c;
/*
* Hairy: if we scan "%ld%lf", the 0.009999 will become 9998 micro.
* Thus, scan as integer and string, so we can count leading zeros
*/
nano = 0;
ts->tv_sec = 0;
ts->tv_nsec = 0;
if ( (i = sscanf(s, "%ld.%ld%c", &ts->tv_sec, &nano, &c)) == 1)
return 0; /* seconds only */
if (i == 3)
return -1; /* trailing crap */
if (i == 0)
if (sscanf(s, ".%ld%c", &nano, &c) != 1)
return -1; /* leading or trailing crap */
s = strchr(s, '.') + 1;
n = strlen(s);
if (n > 9)
return -1; /* too many decimals */
while (n < 9) {
nano *= 10;
n++;
}
ts->tv_nsec = nano;
return 0;
}
/* Boring network stuff extracted from main function */
static int ruler_open_wr_sock(char *ifname, char *dio_name)
{
struct sockaddr_ll addr;
int sock,fd_dio, ifindex;
sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (sock < 0) {
fprintf(stderr, "%s: socket(): %s\n",
ruler_prgname, strerror(errno));
return -1;
}
fd_dio = open(dio_name, O_RDWR);
if(fd_dio < 0) {
fprintf(stderr, "%s: open(): %s\n",
ruler_prgname, strerror(errno));
close(sock);
return -1;
}
memset(&ruler_ifr, 0, sizeof(ruler_ifr));
strncpy(ruler_ifr.ifr_name, ifname, sizeof(ruler_ifr.ifr_name));
if (ioctl(fd_dio, PRIV_MEZZANINE_ID, (unsigned long)dio_name) < 0
/* EAGAIN is special: it means we have no ID to check yet */
&& errno != EAGAIN) {
fprintf(stderr, "%s: ioctl(PRIV_MEZZANINE_ID(%s)): %s\n",
ruler_prgname, dio_name, strerror(errno));
close(sock);
return -1;
}
/* Retrieve the local MAC address to send correct Ethernet frames */
if (ioctl(sock, SIOCGIFHWADDR, &ruler_ifr) < 0) {
fprintf(stderr, "%s: SIOCGIFHWADDR(%s): %s\n", ruler_prgname,
ifname, strerror(errno));
close(sock);
return -1;
}
memcpy(ruler_macaddr, ruler_ifr.ifr_hwaddr.sa_data,
sizeof(ruler_macaddr));
/* Retieve the interfaceindex */
if (ioctl(sock, SIOCGIFINDEX, &ruler_ifr) < 0) {
fprintf(stderr, "%s: SIOCGIFINDEX(%s): %s\n", ruler_prgname,
ifname, strerror(errno));
close(sock);
return -1;
}
ifindex = ruler_ifr.ifr_ifindex;
/* Bind to the interface, so to be able to send */
memset(&addr, 0, sizeof(addr));
addr.sll_family = AF_PACKET;
addr.sll_protocol = htons(RULER_PROTO);
addr.sll_ifindex = ifindex;
addr.sll_pkttype = PACKET_OUTGOING;
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
fprintf(stderr, "%s: bind(%s): %s\n", ruler_prgname,
ifname, strerror(errno));
close(sock);
return -1;
}
/* save in globals for later */
ruler_sock = sock;
ruler_ifname = ifname;
ruler_fd_dio = fd_dio;
ruler_dio_name = dio_name;
return 0;
}
/* The channel being monitored will be configured as input */
static int ruler_config_in(char *chname)
{
int ch;
char c;
if (sscanf(chname, "IN%i%c", &ch, &c) != 1) {
fprintf(stderr, "%s: Argument \"%s\" must be \"IN<ch>\"\n",
ruler_prgname, chname);
return -1;
}
if (ch < 0 || ch > 4) {
fprintf(stderr, "%s: Out of range channel number in \"%s\"\n",
ruler_prgname, chname);
return -1;
}
return ch;
}
/* Actions are allocated in an array and returned for later use */
static struct ruler_action *ruler_build_actions(int nact, char **strings)
{
struct ruler_action *act;
char *s;
int i, ch;
char lr, c;
act = calloc(nact, sizeof(*act));
if (!act) {
fprintf(stderr, "%s: %s\n", ruler_prgname, strerror(errno));
return NULL;
}
for (i = 0; i < nact; i++) {
if (sscanf(strings[i], "%c%i+%c\n", &lr, &ch, &c) != 3) {
fprintf(stderr, "%s: Wrong argument \"%s\"\n",
ruler_prgname, strings[i]);
free(act);
return NULL;
}
if (lr != 'L' && lr != 'R') {
fprintf(stderr, "%s: Wrong argument \"%s\"\n",
ruler_prgname, strings[i]);
free(act);
return NULL;
}
if (ch < 0 || ch > 4) {
fprintf(stderr, "%s: Out of range channel in \"%s\"\n",
ruler_prgname, strings[i]);
free(act);
return NULL;
}
s = strchr(strings[i], '+') + 1;
if (parse_ts(s, &act[i].delay) < 0) {
fprintf(stderr, "%s: Wrong time \"%s\" in \"%s\"\n",
ruler_prgname, s, strings[i]);
free(act);
return NULL;
}
if (lr == 'L')
act[i].isremote = 0;
else
act[i].isremote = 1;
act[i].channel = ch;
}
for (i = 0; i < nact; i++) {
fprintf(stderr, "%s: configured for %s channel %i, "
"delay %li.%09li\n", ruler_prgname,
act[i].isremote ? "remote" : " local",
act[i].channel,
act[i].delay.tv_sec, act[i].delay.tv_nsec);
}
return act;
}
/* The main loop will wait for an event... */
static int ruler_wait_event(int inch, struct timespec *ts)
{
ruler_cmd.command = WR_DIO_CMD_STAMP;
ruler_cmd.flags = WR_DIO_F_WAIT;
ruler_cmd.channel = inch;
if (ioctl(ruler_fd_dio, PRIV_MEZZANINE_CMD, (unsigned long)&ruler_cmd) < 0 ) {
fprintf(stderr, "%s: A ioctl(PRIV_MEZZANINE_CMD(%s)): "
"%s\n", ruler_prgname, ruler_dio_name, strerror(errno));
return -1;
}
/* Assume it's only one stamp */
*ts = ruler_cmd.t[0];
return 0;
}
/* ...and run all actions when the event happens */
static int ruler_run_actions(int nact, struct timespec *ts,
struct ruler_action *actions)
{
int i;
/* We are building stuff in this frame, to possibly send it */
static struct frame {
struct ether_header h;
unsigned char pad[2];
struct wr_dio_cmd cmd;
} f;
/* Most parameters are unchanged over actions */
memset(&f.h.ether_dhost, 0xff, ETH_ALEN); /* broadcast */
memcpy(&f.h.ether_shost, ruler_macaddr, ETH_ALEN);
f.h.ether_type = ntohs(RULER_PROTO);
f.cmd.t[1].tv_nsec = 1000 * 1000; /* 1ms*/
f.cmd.command = WR_DIO_CMD_PULSE;
for (i = 0; i < nact; i++) {
f.cmd.channel = actions[i].channel;
f.cmd.t[0] = *ts;
/* add the requested delay */
f.cmd.t[0].tv_sec += actions[i].delay.tv_sec;
f.cmd.t[0].tv_nsec += actions[i].delay.tv_nsec;
if (f.cmd.t[0].tv_nsec > 1000 * 1000 * 1000) {
f.cmd.t[0].tv_nsec -= 1000 * 1000 * 1000;
f.cmd.t[0].tv_sec++;
}
if (actions[i].isremote) {
if (send(ruler_sock, &f, sizeof(f), 0) < 0) {
fprintf(stderr, "%s: send(): %s\n",
ruler_prgname, strerror(errno));
return -1;
}
continue;
}
/* local */
if (ioctl(ruler_fd_dio, PRIV_MEZZANINE_CMD, (unsigned long) &f.cmd) < 0) {
fprintf(stderr, "%s: ioctl(PRIV_MEZZANINE_CMD(%s)): "
"%s\n", ruler_prgname, ruler_dio_name,
strerror(errno));
return -1;
}
}
return 0;
}
static void print_version(char *pname)
{
printf("%s %s\n", pname, git_version);
}
/* Finally, a main function to wrap it all */
int main(int argc, char **argv)
{
struct ruler_action *actions;
struct timespec ts;
int inch;
if ((argc == 2) && (!strcmp(argv[1], "-V"))) {
print_version(argv[0]);
exit(0);
}
if (argc < 5) {
fprintf(stderr, "%s: Use \"%s [-V] <wr-if> <dio-dev> IN<ch> "
"{L,R}<ch>+<delay-as-decimal> [...]\n",
argv[0], argv[0]);
exit(1);
}
ruler_prgname = argv[0];
/* All functions print error messages by themselves, so just exit */
if (ruler_open_wr_sock(argv[1],argv[2]) < 0)
exit(1);
inch = ruler_config_in(argv[3]);
if (inch < 0)
exit(1);
actions = ruler_build_actions(argc - 4, argv + 4);
if (!actions)
exit(1);
while(1) {
if (ruler_wait_event(inch, &ts) < 0)
exit(1);
if (ruler_run_actions(argc - 4, &ts, actions) < 0)
exit(1);
}
exit(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