Commit 1394f372 authored by Dimitris Lampridis's avatar Dimitris Lampridis Committed by Dimitris Lampridis

sw: wip cleanup and comments

parent 1f2cd1f6
......@@ -44,3 +44,16 @@ else
GIT_VERSION=$(GIT_VERSION) EXCLUDE_FILES=$(EXCLUDE_FILES) BRIEF=$(BRIEF) \
OUTPUT=$(DOXY_OUT) doxygen ./doxygen-wrtd-config
endif
# List of Doxygen folders to consider
DOXINPUT := ../software/lib
DOXINPUT += ../software/include
DOXINPUT += ../software/firmware/common
DOXEXCL := ""
# List of actual Doxygen source files
DOXSRC = $(shell find $(DOXINPUT) -type f -name '*.[chS]')
.doxystamp: $(DOXSRC)
GIT_VERSION=$(GIT_VERSION) DOXINPUT="$(DOXINPUT)" DOXEXCL="$(DOXEXCL)" doxygen ./doxygen-wrtd-config
@touch .doxystamp
Dictionary
==========
The main purpose of this dictionary is to explain the
White-Rabbit Trigger-Distribution terminology.
channel
dead time
delay
input
output
pulse
trigger
trigger condition
\ No newline at end of file
Introduction
============
`Hello` *there* [link](https://ohwr.org)
- item 1
- item 2
[dictionary](dictionary.md)
-include Makefile.specific
DIRS := tdc fd adc
all clean cleanall modules install modules_install: $(DIRS)
clean: TARGET = clean
......
#
# Automatically generated file; DO NOT EDIT.
# fmc-svec-carrier fw-01 demo configuration
#
#
......
/*
* Copyright (C) 2018 CERN (www.cern.ch)
* Author: Dimitris Lampridis <dimitris.lampridis@cern.ch>
/**
* @file wrtd-adcin.c
*
* ADCIN: trigger coming from the ADC mezzanine.
*
* Copyright (c) 2018-2019 CERN (home.cern)
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
/* ADCIN: trigger coming from the ADC mezzanine. */
#include "mockturtle-rt.h"
#define ADCIN_NUM_CHANNELS 5
......@@ -17,39 +18,39 @@ struct wrtd_adcin_dev {
};
static inline void adcin_writel(const struct wrtd_adcin_dev *dev,
uint32_t value, uint32_t reg)
uint32_t value, uint32_t reg)
{
dp_writel(value, dev->io_addr + reg);
dp_writel(value, dev->io_addr + reg);
}
static inline uint32_t adcin_readl(const struct wrtd_adcin_dev *dev, uint32_t reg)
{
return dp_readl(dev->io_addr + reg);
return dp_readl(dev->io_addr + reg);
}
static inline int adcin_wr_link_up(struct wrtd_adcin_dev *adcin)
{
return adcin_readl(adcin, ALT_TRIGOUT_STATUS) & ALT_TRIGOUT_WR_LINK;
return adcin_readl(adcin, ALT_TRIGOUT_STATUS) & ALT_TRIGOUT_WR_LINK;
}
static inline int adcin_wr_time_locked(struct wrtd_adcin_dev *adcin)
{
return 1;
return 1;
}
static void adcin_wr_enable_lock(struct wrtd_adcin_dev *dev, int enable)
{
return;
return;
}
static inline int adcin_wr_time_ready(struct wrtd_adcin_dev *adcin)
{
return adcin_readl(adcin, ALT_TRIGOUT_STATUS) & ALT_TRIGOUT_WR_VALID;
return adcin_readl(adcin, ALT_TRIGOUT_STATUS) & ALT_TRIGOUT_WR_VALID;
}
static inline int adcin_wr_sync_timeout(void)
{
return 0;
return 0;
}
/**
......@@ -68,7 +69,7 @@ static void adcin_input(struct wrtd_adcin_dev *adcin)
/* Poll the FIFO and read the timestamp */
if(!(status & ALT_TRIGOUT_TS_PRESENT))
return;
return;
mask = adcin_readl(adcin, ALT_TRIGOUT_TS_MASK_SEC + 0);
ev.ts.seconds = adcin_readl(adcin, ALT_TRIGOUT_TS_MASK_SEC + 4);
......@@ -77,13 +78,13 @@ static void adcin_input(struct wrtd_adcin_dev *adcin)
for (i = 0; i < ADCIN_NUM_CHANNELS; i++) {
/* The last channel is the ext trigger with a different mask */
if ( i == ADCIN_NUM_CHANNELS - 1 ) {
if (!(mask & (ALT_TRIGOUT_EXT_MASK >> 32)))
continue;
}
else if (!(mask & ((ALT_TRIGOUT_CH1_MASK >> 32) << i)))
continue;
/* The last channel is the ext trigger with a different mask */
if ( i == ADCIN_NUM_CHANNELS - 1 ) {
if (!(mask & (ALT_TRIGOUT_EXT_MASK >> 32)))
continue;
}
else if (!(mask & ((ALT_TRIGOUT_CH1_MASK >> 32) << i)))
continue;
memset(ev.id, 0, WRTD_ID_LEN);
ev.id[0] = 'L';
......@@ -104,7 +105,7 @@ static void adcin_input(struct wrtd_adcin_dev *adcin)
static int adcin_init(struct wrtd_adcin_dev *adcin)
{
pr_debug("adcin initialization complete\n\r");
pr_debug("adcin initialization complete\n\r");
return 0;
return 0;
}
/*
* Copyright (C) 2013-2018 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@cern.ch>
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
/**
* @file wrtd-adcin.c
*
* ADCout: trigger ADC sampling. Output from the WRTD pov.
*
* Copyright (c) 2018-2019 CERN (home.cern)
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
/* ADCout: trigger ADC sampling. Output from the WRTD pov. */
#include <errno.h>
#include <string.h>
......@@ -30,44 +30,44 @@ struct wrtd_adcout_dev {
static inline int adcout_wr_present(struct wrtd_adcout_dev *fd)
{
return 1;
return 1;
}
static inline int adcout_wr_link_up(struct wrtd_adcout_dev *fd)
{
return 1;
return 1;
}
static inline int adcout_wr_time_locked(struct wrtd_adcout_dev *fd)
{
return 1;
return 1;
}
static inline int adcout_wr_time_ready(struct wrtd_adcout_dev *fd)
{
return 1;
return 1;
}
static inline int adcout_wr_sync_timeout(void)
{
return 0;
return 0;
}
static void adcout_wr_enable_lock(struct wrtd_adcout_dev *fd, int enable)
{
return;
return;
}
static inline void adcout_writel(struct wrtd_adcout_dev *dev, uint32_t value,
uint32_t reg)
uint32_t reg)
{
dp_writel(value, dev->io_addr + reg);
dp_writel(value, dev->io_addr + reg);
}
static inline uint32_t adcout_readl (struct wrtd_adcout_dev *dev, uint32_t reg)
{
return dp_readl(dev->io_addr + reg);
return dp_readl(dev->io_addr + reg);
}
......@@ -76,24 +76,24 @@ static inline uint32_t adcout_readl (struct wrtd_adcout_dev *dev, uint32_t reg)
* Drop the given enqueued trigger
*/
static void adcout_drop_trigger(struct wrtd_adcout_dev *dev,
struct wrtd_event *ev, unsigned reason,
struct wrtd_tstamp *now)
struct wrtd_event *ev, unsigned reason,
struct wrtd_tstamp *now)
{
struct adcout_out_queue *q = &dev->queue;
dev->idle = 1;
struct adcout_out_queue *q = &dev->queue;
dev->idle = 1;
if (adcout_out_queue_empty(q)) {
/* Should never happen. */
return;
}
if (adcout_out_queue_empty(q)) {
/* Should never happen. */
return;
}
/* Drop the pulse */
adcout_out_queue_pop(q);
/* Drop the pulse */
adcout_out_queue_pop(q);
/* Disarm the ADC output */
adcout_writel(dev, 0, ALT_TRIGIN_CTRL);
/* Disarm the ADC output */
adcout_writel(dev, 0, ALT_TRIGIN_CTRL);
wrtd_log(WRTD_LOG_MSG_EV_DISCARDED, reason, NULL, ev, now);
wrtd_log(WRTD_LOG_MSG_EV_DISCARDED, reason, NULL, ev, now);
}
......@@ -103,91 +103,91 @@ static void adcout_drop_trigger(struct wrtd_adcout_dev *dev,
*/
static void adcout_output (struct wrtd_adcout_dev *dev)
{
struct adcout_out_queue *q = &dev->queue;
struct wrtd_event *ev = adcout_out_queue_front(q);
uint32_t ctrl = adcout_readl(dev, ALT_TRIGIN_CTRL);
struct wrtd_tstamp *ts;
struct adcout_out_queue *q = &dev->queue;
struct wrtd_event *ev = adcout_out_queue_front(q);
uint32_t ctrl = adcout_readl(dev, ALT_TRIGIN_CTRL);
struct wrtd_tstamp *ts;
/* Check if the output has triggered */
if (!dev->idle) {
/* Check if the output has triggered */
if (!dev->idle) {
#if 0
if (!wr_is_timing_ok()) {
/* Timing has been lost. */
drop_trigger(out, ev, q, WRTD_LOG_DISCARD_NO_SYNC);
return;
}
if (!wr_is_timing_ok()) {
/* Timing has been lost. */
drop_trigger(out, ev, q, WRTD_LOG_DISCARD_NO_SYNC);
return;
}
#endif
if (ctrl & ALT_TRIGIN_CTRL_ENABLE) {
/* Armed but still waiting for trigger */
struct wrtd_tstamp now;
ts_now(&now);
if (adcout_out_queue_check_timeout (q, &now)) {
/* Will never trigger. Missed. */
adcout_drop_trigger(dev, ev, WRTD_LOG_DISCARD_TIMEOUT, &now);
}
} else {
/* Has been triggered. */
wrtd_log(WRTD_LOG_MSG_EV_CONSUMED,
if (ctrl & ALT_TRIGIN_CTRL_ENABLE) {
/* Armed but still waiting for trigger */
struct wrtd_tstamp now;
ts_now(&now);
if (adcout_out_queue_check_timeout (q, &now)) {
/* Will never trigger. Missed. */
adcout_drop_trigger(dev, ev, WRTD_LOG_DISCARD_TIMEOUT, &now);
}
} else {
/* Has been triggered. */
wrtd_log(WRTD_LOG_MSG_EV_CONSUMED,
WRTD_LOG_CONSUMED_DONE,
NULL, ev, NULL);
adcout_out_queue_pop(q);
dev->idle = 1;
}
return;
}
adcout_out_queue_pop(q);
dev->idle = 1;
}
return;
}
/* Output is idle: check if there's something in the queue to execute */
if (adcout_out_queue_empty(q))
return;
/* Output is idle: check if there's something in the queue to execute */
if (adcout_out_queue_empty(q))
return;
ev = adcout_out_queue_front(q);
ts = &ev->ts;
ev = adcout_out_queue_front(q);
ts = &ev->ts;
if (!wr_is_timing_ok()) {
adcout_drop_trigger(dev, ev, WRTD_LOG_DISCARD_NO_SYNC, NULL);
return;
}
if (!wr_is_timing_ok()) {
adcout_drop_trigger(dev, ev, WRTD_LOG_DISCARD_NO_SYNC, NULL);
return;
}
/* Program the output start time */
adcout_writel(dev, 0, ALT_TRIGIN_SECONDS + 0);
adcout_writel(dev, ts->seconds, ALT_TRIGIN_SECONDS + 4);
adcout_writel(dev, ts->ns / 8, ALT_TRIGIN_CYCLES);
adcout_writel(dev, ALT_TRIGIN_CTRL_ENABLE, ALT_TRIGIN_CTRL);
/* Program the output start time */
adcout_writel(dev, 0, ALT_TRIGIN_SECONDS + 0);
adcout_writel(dev, ts->seconds, ALT_TRIGIN_SECONDS + 4);
adcout_writel(dev, ts->ns / 8, ALT_TRIGIN_CYCLES);
adcout_writel(dev, ALT_TRIGIN_CTRL_ENABLE, ALT_TRIGIN_CTRL);
wrtd_log(WRTD_LOG_MSG_EV_CONSUMED, WRTD_LOG_CONSUMED_START,
wrtd_log(WRTD_LOG_MSG_EV_CONSUMED, WRTD_LOG_CONSUMED_START,
NULL, ev, NULL);
ts_add2_ns (ts, 8000);
ts_add2_ns (ts, 8000);
/*
* Store the last programmed timestamp (+ some margin) and mark
* the output as busy
*/
q->last_programmed_sec = ts->seconds;
q->last_programmed_ns = ts->ns;
dev->idle = 0;
/*
* Store the last programmed timestamp (+ some margin) and mark
* the output as busy
*/
q->last_programmed_sec = ts->seconds;
q->last_programmed_ns = ts->ns;
dev->idle = 0;
}
static int adcout_local_output(struct wrtd_adcout_dev *dev,
struct wrtd_event *ev, unsigned ch)
{
struct wrtd_event *pq_ev;
struct wrtd_event *pq_ev;
pq_ev = adcout_out_queue_push(&dev->queue);
if (!pq_ev) {
pq_ev = adcout_out_queue_push(&dev->queue);
if (!pq_ev) {
return -EOVERFLOW;
}
}
*pq_ev = *ev;
*pq_ev = *ev;
return 0;
}
static void adcout_init(struct wrtd_adcout_dev *dev)
{
adcout_out_queue_init(&dev->queue);
dev->idle = 1;
adcout_out_queue_init(&dev->queue);
dev->idle = 1;
pr_debug("adcout initialization complete\n\r");
pr_debug("adcout initialization complete\n\r");
}
/*
* Copyright (C) 2013-2018 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@cern.ch>
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
/**
* @file wrtd-rt-adc.c
*
* Copyright (c) 2018-2019 CERN (home.cern)
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
#include "mockturtle-rt.h"
#include <mockturtle-framework.h>
#include "wrtd-common.h"
......@@ -44,35 +43,35 @@ static struct wrtd_adcout_dev adcout0 =
static inline int wr_link_up(void)
{
if (!adcin_wr_link_up(&adcin0))
return 0;
if (!adcin_wr_link_up(&adcin0))
return 0;
if (!adcout_wr_link_up(&adcout0))
return 0;
return 1;
return 0;
return 1;
}
static inline int wr_time_locked(void)
{
if (!adcin_wr_time_locked(&adcin0))
return 0;
if (!adcin_wr_time_locked(&adcin0))
return 0;
if (!adcout_wr_time_locked(&adcout0))
return 0;
return 1;
return 0;
return 1;
}
static inline int wr_time_ready(void)
{
if (!adcin_wr_time_ready(&adcin0))
return 0;
if (!adcin_wr_time_ready(&adcin0))
return 0;
if (!adcout_wr_time_ready(&adcout0))
return 0;
return 1;
return 0;
return 1;
}
static inline void wr_enable_lock(int enable)
{
adcin_wr_enable_lock(&adcin0, enable);
adcout_wr_enable_lock(&adcout0, enable);
adcin_wr_enable_lock(&adcin0, enable);
adcout_wr_enable_lock(&adcout0, enable);
}
static inline int wr_sync_timeout(void)
......@@ -80,14 +79,14 @@ static inline int wr_sync_timeout(void)
#ifdef SIMULATION
return 0;
#else
int res, val;
res = 0;
int res, val;
res = 0;
val = adcin_wr_sync_timeout();
res = val > res ? val : res;
val = adcin_wr_sync_timeout();
res = val > res ? val : res;
val = adcout_wr_sync_timeout();
res = val > res ? val : res;
val = adcout_wr_sync_timeout();
res = val > res ? val : res;
return res;
#endif
}
......@@ -99,18 +98,18 @@ static int wrtd_local_output(struct wrtd_event *ev, unsigned ch)
static int wrtd_user_init(void)
{
adcin_init(&adcin0);
adcin_init(&adcin0);
adcout_init(&adcout0);
wr_enable_lock(0);
wr_enable_lock(0);
pr_debug("rt-adc firmware initialized.\n\r");
pr_debug("rt-adc firmware initialized.\n\r");
return 0;
return 0;
}
static void wrtd_io(void)
{
adcin_input(&adcin0);
adcin_input(&adcin0);
adcout_output(&adcout0);
}
/*
* acam_gpx.h
/**
* @file acam_gpx.h
*
* Copyright (c) 2012-2013 CERN (http://www.cern.ch)
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; version 2 of the License.
* Copyright (c) 2012-2019 CERN (home.cern)
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
#ifndef __ACAM_GPX_H
......@@ -102,4 +101,3 @@
#define AR12_StartNU (1<<26)
#endif
/* Generic output queue.
User needs to define:
OUT_QUEUE_PREFIX: the prefix (ends with an '_')
OUT_QUEUE_SIZE: max length of the queue
OUT_QUEUE_MAXTIME: maximum time in advance.
*/
/**
* @file out_queue.h
*
* Generic output queue. Users need to define:
* - `OUT_QUEUE_PREFIX` : the prefix (must end with an '_')
* - `OUT_QUEUE_SIZE` : max length of the queue
* - `OUT_QUEUE_MAXTIME`: maximum time in advance.
*
* Copyright (c) 2018-2019 CERN (home.cern)
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
#ifndef OUT_QUEUE_PREFIX
#error "OUT_QUEUE_PREFIX not defined"
......@@ -15,12 +21,12 @@
#define OUT_QUEUE_STRUCT OUT_QUEUE_NAME(out_queue)
struct OUT_QUEUE_STRUCT {
struct wrtd_event events[OUT_QUEUE_SIZE];
int head, tail, count;
struct wrtd_event events[OUT_QUEUE_SIZE];
int head, tail, count;
/* Last timestamp value written to output config. */
uint32_t last_programmed_sec;
uint32_t last_programmed_ns;
/* Last timestamp value written to output config. */
uint32_t last_programmed_sec;
uint32_t last_programmed_ns;
};
/**
......@@ -28,12 +34,12 @@ struct OUT_QUEUE_STRUCT {
*/
static void OUT_QUEUE_NAME(out_queue_init)(struct OUT_QUEUE_STRUCT *p)
{
p->head = 0;
p->tail = 0;
p->count = 0;
p->head = 0;
p->tail = 0;
p->count = 0;
p->last_programmed_sec = 0;
p->last_programmed_ns = 0;
p->last_programmed_sec = 0;
p->last_programmed_ns = 0;
}
......@@ -43,19 +49,19 @@ static void OUT_QUEUE_NAME(out_queue_init)(struct OUT_QUEUE_STRUCT *p)
*/
static struct wrtd_event *OUT_QUEUE_NAME(out_queue_push)(struct OUT_QUEUE_STRUCT *p)
{
struct wrtd_event *ev;
struct wrtd_event *ev;
if (p->count == OUT_QUEUE_SIZE)
return NULL;
if (p->count == OUT_QUEUE_SIZE)
return NULL;
ev = &p->events[p->head];
p->count++;
p->head++;
ev = &p->events[p->head];
p->count++;
p->head++;
if (p->head == OUT_QUEUE_SIZE)
p->head = 0;
if (p->head == OUT_QUEUE_SIZE)
p->head = 0;
return ev;
return ev;
}
......@@ -64,7 +70,7 @@ static struct wrtd_event *OUT_QUEUE_NAME(out_queue_push)(struct OUT_QUEUE_STRUCT
*/
static inline int OUT_QUEUE_NAME(out_queue_empty)(struct OUT_QUEUE_STRUCT *p)
{
return (p->count == 0);
return (p->count == 0);
}
......@@ -73,9 +79,9 @@ static inline int OUT_QUEUE_NAME(out_queue_empty)(struct OUT_QUEUE_STRUCT *p)
*/
static struct wrtd_event *OUT_QUEUE_NAME(out_queue_front)(struct OUT_QUEUE_STRUCT *p)
{
if (!p->count)
return NULL;
return &p->events[p->tail];
if (!p->count)
return NULL;
return &p->events[p->tail];
}
......@@ -84,11 +90,11 @@ static struct wrtd_event *OUT_QUEUE_NAME(out_queue_front)(struct OUT_QUEUE_STRUC
*/
static void OUT_QUEUE_NAME(out_queue_pop)(struct OUT_QUEUE_STRUCT *p)
{
p->tail++;
p->tail++;
if(p->tail == OUT_QUEUE_SIZE)
p->tail = 0;
p->count--;
if(p->tail == OUT_QUEUE_SIZE)
p->tail = 0;
p->count--;
}
/**
......@@ -96,23 +102,23 @@ static void OUT_QUEUE_NAME(out_queue_pop)(struct OUT_QUEUE_STRUCT *p)
* of timeout
*/
static int OUT_QUEUE_NAME(out_queue_check_timeout) (struct OUT_QUEUE_STRUCT *q,
struct wrtd_tstamp *now)
struct wrtd_tstamp *now)
{
int delta;
int delta;
if(q->last_programmed_sec > now->seconds + OUT_QUEUE_MAXTIME) {
pr_error("Enqueued event very far in the future. Dropping.");
return 0;
}
if(q->last_programmed_sec > now->seconds + OUT_QUEUE_MAXTIME) {
pr_error("Enqueued event very far in the future. Dropping.");
return 0;
}
/* Current time exceeds FD setpoint? */
/* Current time exceeds FD setpoint? */
delta = now->seconds - q->last_programmed_sec;
if (delta != 0)
return delta > 0;
delta = now->seconds - q->last_programmed_sec;
if (delta != 0)
return delta > 0;
delta = now->ns - q->last_programmed_ns;
return (delta > 0);
delta = now->ns - q->last_programmed_ns;
return (delta > 0);
}
#undef OUT_QUEUE_STRUCT
......
/* This is a basic linker configuration for WRTD.
/**
* @file trtl-memory.ld
*
* This is a basic linker configuration for WRTD.
*
* It expects that every MT CPU has at least 32KB
* of memory, as well as 2KB of shared memory for
* all CPUS.
*
* Copyright (c) 2018-2019 CERN (home.cern)
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
MEMORY
......
/* Code to insert inside the firmware:
#define NBR_CPUS xxx
#define CPU_IDX xxx
#define NBR_RULES xxx
#define NBR_DEVICES xxx
#define NBR_ALARMS xxx
#define DEVICES_NBR_CHS { xxx, 0, 0, 0}
#define DEVICES_CHS_DIR { WRTD_CH_DIR_IN, 0, 0, 0}
#define APP_ID WRTD_APP_xxx
#define APP_VER RT_VERSION(xxx, 0)
#define APP_NAME xxx
#define WRTD_NET_RX x // 1 = enabled, 0 = disabled
#define WRTD_NET_TX x
#define WRTD_LOCAL_RX x
#define WRTD_LOCAL_TX x
#include "wrtd-rt-common.h"
*/
/**
* @file wrtd-rt-common.h
*
* This is the common part that should be included
* by every WRTD firmware application.
*
* In order for it to work, the firmware application
* must declare the following definitions and includes:
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* #define NBR_CPUS xxx
* #define CPU_IDX xxx
* #define NBR_RULES xxx
* #define NBR_DEVICES xxx
* #define NBR_ALARMS xxx
* #define DEVICES_NBR_CHS { xxx, 0, 0, 0}
* #define DEVICES_CHS_DIR { WRTD_CH_DIR_IN, 0, 0, 0}
* #define APP_ID WRTD_APP_xxx
* #define APP_VER RT_VERSION(xxx, 0)
* #define APP_NAME xxx
* #define WRTD_NET_RX x // 1 = enabled, 0 = disabled
* #define WRTD_NET_TX x
* #define WRTD_LOCAL_RX x
* #define WRTD_LOCAL_TX x
* #include "wrtd-rt-common.h"
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Copyright (c) 2018-2019 CERN (home.cern)
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
/* Remote message queue for network. */
#define WRTD_RMQ 0
......@@ -49,7 +61,7 @@ static struct wrtd_root root =
.ver_major = WRTD_VERSION_MAJOR,
.ver_minor = WRTD_VERSION_MINOR,
.fw_id = APP_ID,
.fw_id = APP_ID,
.nbr_devices = NBR_DEVICES,
.nbr_alarms = NBR_ALARMS,
......@@ -73,7 +85,7 @@ static struct wrtd_root root =
static inline int wr_is_timing_ok(void)
{
return (root.wr_state == WR_LINK_SYNCED);
return (root.wr_state == WR_LINK_SYNCED);
}
/**
......@@ -81,41 +93,41 @@ static inline int wr_is_timing_ok(void)
*/
static void wr_update_link(void)
{
switch (root.wr_state) {
case WR_LINK_OFFLINE:
if (wr_link_up())
root.wr_state = WR_LINK_ONLINE;
break;
case WR_LINK_ONLINE:
if (wr_time_ready()) {
root.wr_state = WR_LINK_SYNCING;
wr_enable_lock(1);
}
break;
case WR_LINK_SYNCING:
if (wr_time_locked()) {
pr_debug("sync detected, waiting for plumbing to catch up...\n\r");
switch (root.wr_state) {
case WR_LINK_OFFLINE:
if (wr_link_up())
root.wr_state = WR_LINK_ONLINE;
break;
case WR_LINK_ONLINE:
if (wr_time_ready()) {
root.wr_state = WR_LINK_SYNCING;
wr_enable_lock(1);
}
break;
case WR_LINK_SYNCING:
if (wr_time_locked()) {
pr_debug("sync detected, waiting for plumbing to catch up...\n\r");
root.wr_state = WR_LINK_WAIT;
tai_start = lr_readl(MT_CPU_LR_REG_TAI_SEC);
}
break;
case WR_LINK_WAIT:
if (wr_sync_timeout() == 0
}
break;
case WR_LINK_WAIT:
if (wr_sync_timeout() == 0
|| (lr_readl(MT_CPU_LR_REG_TAI_SEC)
>= (tai_start + wr_sync_timeout()))) {
pr_debug("WR synced\n\r");
root.wr_state = WR_LINK_SYNCED;
}
break;
case WR_LINK_SYNCED:
break;
}
if (root.wr_state != WR_LINK_OFFLINE && !wr_link_up()) {
pr_error("WR sync lost\n\r");
root.wr_state = WR_LINK_OFFLINE;
wr_enable_lock(0);
}
pr_debug("WR synced\n\r");
root.wr_state = WR_LINK_SYNCED;
}
break;
case WR_LINK_SYNCED:
break;
}
if (root.wr_state != WR_LINK_OFFLINE && !wr_link_up()) {
pr_error("WR sync lost\n\r");
root.wr_state = WR_LINK_OFFLINE;
wr_enable_lock(0);
}
}
#if NBR_CPUS > 1
......@@ -168,8 +180,8 @@ static void wrtd_recv_loopback(void)
volatile struct loopback_queue *q = &lqueues[CPU_IDX];
volatile struct wrtd_loopback_event *e;
if (q->count == 0)
return; /* No entry */
if (q->count == 0)
return; /* No entry */
e = &q->buf[q->read_idx];
......@@ -181,14 +193,14 @@ static void wrtd_recv_loopback(void)
wrtd_local_output((struct wrtd_event *)&e->ev, e->ch);
q->read_idx = (q->read_idx + 1) & (LOOPBACK_QUEUE_SIZE - 1);
smem_atomic_add(&q->count, -1);
smem_atomic_add(&q->count, -1);
}
#endif
#endif // NBR_CPUS
static inline void ts_add2_ns(struct wrtd_tstamp *ts, uint32_t ns)
{
ts->ns += ns;
if (ts->ns >= 1000000000) {
if (ts->ns >= 1000000000) {
ts->ns -= 1000000000;
ts->seconds++;
}
......@@ -200,7 +212,7 @@ static inline void ts_add3_ns(struct wrtd_tstamp *dest,
dest->seconds = src->seconds;
dest->ns = src->ns + ns;
dest->frac = src->frac;
if (dest->ns >= 1000000000) {
if (dest->ns >= 1000000000) {
dest->ns -= 1000000000;
dest->seconds++;
}
......@@ -225,10 +237,10 @@ static inline int ts_cmp(const struct wrtd_tstamp *l, const struct wrtd_tstamp *
static void ts_now(struct wrtd_tstamp *now)
{
/*
* Read the current WR time, order is important: first seconds,
* then cycles (cycles get latched on reading secs register).
*/
/*
* Read the current WR time, order is important: first seconds,
* then cycles (cycles get latched on reading secs register).
*/
now->seconds = lr_readl(MT_CPU_LR_REG_TAI_SEC);
now->ns = lr_readl(MT_CPU_LR_REG_TAI_CYCLES) * 8;
now->frac = 0;
......@@ -241,7 +253,6 @@ static void wrtd_log(uint32_t type, uint32_t reason,
{
struct wrtd_log_entry *log;
struct trtl_fw_msg msg;
struct wrtd_tstamp now;
int ret;
/* First, handle statistics */
......@@ -270,32 +281,30 @@ static void wrtd_log(uint32_t type, uint32_t reason,
return;
}
ret = mq_claim(TRTL_HMQ, WRTD_HMQ);
if (ret == -EBUSY) {
ret = mq_claim(TRTL_HMQ, WRTD_HMQ);
if (ret == -EBUSY) {
root.log_nbr_busy++;
return;
return;
}
if ( ts == NULL )
ts_now(&now);
else
now = *ts;
mq_map_out_message(TRTL_HMQ, WRTD_HMQ, &msg);
msg.header->flags = 0;
msg.header->msg_id = WRTD_ACTION_LOG;
msg.header->len = sizeof(struct wrtd_log_entry) / 4;
msg.header->flags = 0;
msg.header->msg_id = WRTD_ACTION_LOG;
msg.header->len = sizeof(struct wrtd_log_entry) / 4;
log = (struct wrtd_log_entry *)msg.payload;
log->type = type;
log = (struct wrtd_log_entry *)msg.payload;
log->type = type;
log->reason = reason;
if (ev != NULL)
log->event = *ev;
else
memset(&log->event, 0, sizeof(struct wrtd_event));
log->ts = now;
if (ts != NULL)
log->ts = *ts;
else
memset(&log->ts, 0, sizeof(struct wrtd_tstamp));
mq_send(TRTL_HMQ, WRTD_HMQ);
......@@ -305,17 +314,17 @@ static void wrtd_log(uint32_t type, uint32_t reason,
#if WRTD_NET_TX > 0
static void wrtd_init_tx(void)
{
static const struct trtl_ep_eth_address addr = {
.type = TRTL_EP_FRAME_UDP,
.dst_mac = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
.dst_ip = 0xFFFFFFFF, /* broadcast on 255.255.255.255 */
.src_ip = 0xC0A85A11,
.dst_port = WRTD_UDP_PORT,
.src_port = WRTD_UDP_PORT,
.ethertype = 0x800, /* IPv4 */
.filter = 0 /* only the TX path of the endpoint is used */
};
rmq_bind_out(WRTD_RMQ, TRTL_EP_ETH, &addr);
static const struct trtl_ep_eth_address addr = {
.type = TRTL_EP_FRAME_UDP,
.dst_mac = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
.dst_ip = 0xFFFFFFFF, /* broadcast on 255.255.255.255 */
.src_ip = 0xC0A85A11,
.dst_port = WRTD_UDP_PORT,
.src_port = WRTD_UDP_PORT,
.ethertype = 0x800, /* IPv4 */
.filter = 0 /* only the TX path of the endpoint is used */
};
rmq_bind_out(WRTD_RMQ, TRTL_EP_ETH, &addr);
}
static int wrtd_send_network(struct wrtd_event *ev)
......@@ -357,7 +366,7 @@ static int wrtd_send_network(struct wrtd_event *ev)
return 0;
}
#endif
#endif // WRTD_NET_TX
static void wrtd_route(struct wrtd_rule *rule, const struct wrtd_event *ev)
{
......@@ -365,12 +374,8 @@ static void wrtd_route(struct wrtd_rule *rule, const struct wrtd_event *ev)
struct wrtd_tstamp now;
uint32_t lat_ns;
/* Event was received. */
rule->stat.rx_events++;
rule->stat.rx_last = ev->ts;
/* Check hold-off.
Note: this also exclude events that came before the last
Note: this also excludes events that came before the last
one. */
if (rule->conf.hold_off_ns) {
if (ts_cmp(&ev->ts, &rule->stat.hold_off) < 0) {
......@@ -385,14 +390,18 @@ static void wrtd_route(struct wrtd_rule *rule, const struct wrtd_event *ev)
&ev->ts, rule->conf.hold_off_ns);
}
/* Event was received. */
rule->stat.rx_events++;
rule->stat.rx_last = ev->ts;
ts_now(&now);
/* Compute latency. */
do {
lat_ns = now.ns - ev->ts.ns;
if(lat_ns >> 31) {
/* Result is negatif, possible only if differ by
1 sec. */
/* Result is negative, possible only if
timestamps differ by 1 sec. */
if (ev->ts.seconds + 1 == now.seconds)
lat_ns += 1000000000;
else
......@@ -521,24 +530,24 @@ static void wrtd_route_in(struct wrtd_event *ev)
#if WRTD_NET_RX > 0
static void wrtd_init_rx(void)
{
static const struct trtl_ep_eth_address addr = {
.type = TRTL_EP_FRAME_UDP,
.dst_mac = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
.dst_port = WRTD_UDP_PORT,
.ethertype = 0x800, /* IPv4 */
.filter = (TRTL_EP_FILTER_UDP
static const struct trtl_ep_eth_address addr = {
.type = TRTL_EP_FRAME_UDP,
.dst_mac = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
.dst_port = WRTD_UDP_PORT,
.ethertype = 0x800, /* IPv4 */
.filter = (TRTL_EP_FILTER_UDP
| TRTL_EP_FILTER_DST_PORT
| TRTL_EP_FILTER_ENABLE)
};
rmq_bind_in(WRTD_RMQ, TRTL_EP_ETH, &addr);
};
rmq_bind_in(WRTD_RMQ, TRTL_EP_ETH, &addr);
}
static void wrtd_recv_network(void)
{
volatile struct wrtd_message *msg;
volatile struct wrtd_message *msg;
struct wrtd_event ev;
if (!mq_poll_in(TRTL_RMQ, 1 << WRTD_RMQ)) {
if (!mq_poll_in(TRTL_RMQ, 1 << WRTD_RMQ)) {
/* No packet. */
return;
}
......@@ -559,7 +568,7 @@ static void wrtd_recv_network(void)
wrtd_route_in(&ev);
}
#endif
#endif //WRTD_NET_RX
#if NBR_ALARMS > 0
static void wrtd_alarms(void)
......@@ -601,7 +610,7 @@ static void wrtd_alarms(void)
ts_add2_ns(&al->event.ts, al->period_ns);
}
}
#endif
#endif // NBR_ALARMS
static int wrtd_action_readw(struct trtl_fw_msg *msg_i,
struct trtl_fw_msg *msg_o)
......@@ -612,9 +621,9 @@ static int wrtd_action_readw(struct trtl_fw_msg *msg_i,
uint32_t len;
unsigned int i;
/* Verify that the size is correct */
if (msg_i->header->len != sizeof(struct wrtd_io_msg) / 4)
return -EINVAL;
/* Verify that the size is correct */
if (msg_i->header->len != sizeof(struct wrtd_io_msg) / 4)
return -EINVAL;
imsg = (volatile struct wrtd_io_msg *)msg_i->payload;
odata = (volatile uint32_t *)msg_o->payload;
......@@ -631,7 +640,6 @@ static int wrtd_action_readw(struct trtl_fw_msg *msg_i,
return 0;
}
static int wrtd_action_writew(struct trtl_fw_msg *msg_i,
struct trtl_fw_msg *msg_o)
{
......@@ -640,9 +648,9 @@ static int wrtd_action_writew(struct trtl_fw_msg *msg_i,
uint32_t len;
unsigned int i;
/* Verify that the size is correct */
if (msg_i->header->len * 4 < sizeof(uint32_t))
return -EINVAL;
/* Verify that the size is correct */
if (msg_i->header->len * 4 < sizeof(uint32_t))
return -EINVAL;
idata = (volatile uint32_t *)msg_i->payload;
addr = (volatile uint32_t *)idata[0];
......@@ -658,15 +666,14 @@ static int wrtd_action_writew(struct trtl_fw_msg *msg_i,
return 0;
}
static int wrtd_action_get_config(struct trtl_fw_msg *msg_i,
struct trtl_fw_msg *msg_o)
{
struct wrtd_config_msg *cfg;
/* Verify that the size is correct */
if (msg_i->header->len * 4 != 0)
return -EINVAL;
/* Verify that the size is correct */
if (msg_i->header->len * 4 != 0)
return -EINVAL;
cfg = (struct wrtd_config_msg *)msg_o->payload;
cfg->root_addr = (uint32_t) &root;
......@@ -678,7 +685,6 @@ static int wrtd_action_get_config(struct trtl_fw_msg *msg_i,
return 0;
}
static trtl_fw_action_t *wrtd_actions[] = {
[WRTD_ACTION_GET_CONFIG] = wrtd_action_get_config,
[WRTD_ACTION_READW] = wrtd_action_readw,
......@@ -687,7 +693,7 @@ static trtl_fw_action_t *wrtd_actions[] = {
static int wrtd_main(void)
{
while (1) {
while (1) {
if (!root.freeze_flag) {
wrtd_io();
#if WRTD_NET_RX > 0
......@@ -701,11 +707,11 @@ static int wrtd_main(void)
wrtd_alarms();
#endif
}
trtl_fw_mq_action_dispatch(TRTL_HMQ, WRTD_HMQ);
wr_update_link();
}
trtl_fw_mq_action_dispatch(TRTL_HMQ, WRTD_HMQ);
wr_update_link();
}
return 0;
return 0;
}
static int wrtd_sys_init(void)
......@@ -722,17 +728,17 @@ static int wrtd_sys_init(void)
}
struct trtl_fw_application app = {
.name = APP_NAME,
.version = {
.name = APP_NAME,
.version = {
.rt_id = APP_ID,
.rt_version = APP_VER,
.git_version = GIT_VERSION
},
.rt_version = APP_VER,
.git_version = GIT_VERSION
},
.actions = wrtd_actions,
.n_actions = ARRAY_SIZE(wrtd_actions),
.actions = wrtd_actions,
.n_actions = ARRAY_SIZE(wrtd_actions),
.init = wrtd_sys_init,
.main = wrtd_main,
.exit = NULL,
.init = wrtd_sys_init,
.main = wrtd_main,
.exit = NULL,
};
#
# Automatically generated file; DO NOT EDIT.
# fmc-svec-carrier fw-01 demo configuration
#
#
......
/*
/**
* @file fd_acam.c
*
* Accessing the ACAM chip and configuring it.
*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* Author: Alessandro Rubini <rubini@gnudd.com>
* Author: Dimitris Lampridis <dimitris.lampridis@cern.ch>
*
* Copyright (c) 2012-2019 CERN (home.cern)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2 as published by the Free Software Foundation or, at your
* option, any later version.
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
#include "mockturtle-rt.h"
......@@ -23,312 +24,314 @@
* We know the bin is small, but the Tref is several nanos so we need 64 bits
* (although our current values fit in 32 bits after the division)
*/
#define ACAM_FP_BIN ((int)(ACAM_DESIRED_BIN * (1 << 16)))
#define ACAM_FP_TREF (((1000LL * 1000 * 1000) << 16) / ACAM_CLOCK_FREQ_KHZ)
#define ACAM_FP_BIN ((int)(ACAM_DESIRED_BIN * (1 << 16)))
#define ACAM_FP_TREF (((1000LL * 1000 * 1000) << 16) / ACAM_CLOCK_FREQ_KHZ)
/* Default values of control registers for the ACAM TDC working in G-Mode
(eeprom values are obsolete) */
#define ACAM_GMODE_START_OFFSET 10000
#define ACAM_GMODE_ASOR 17000
#define ACAM_GMODE_ATMCR (26 | (1500 << 8))
#define ACAM_GMODE_ADSFR 84977
#define ACAM_GMODE_START_OFFSET 10000
#define ACAM_GMODE_ASOR 17000
#define ACAM_GMODE_ATMCR (26 | (1500 << 8))
#define ACAM_GMODE_ADSFR 84977
static int acam_calc_pll(uint64_t tref, int bin, int *hsdiv_out,
int *refdiv_out)
int *refdiv_out)
{
uint64_t tmpll;
int x, refdiv, hsdiv;
/*
* Tbin(I-mode) = (Tref << refdiv) / (216 * hsdiv)
*
* so, calling X the value "hsdiv >> refdiv" we have
*
* X = Tref / (216 * Tbin)
*
* Then, we can choose refdiv == 7 to have the best bits,
* and then shift out the zeros to get smaller values.
*
*/
if (0) {
x = (tref << 16) / 216 / bin;
//printf("x = %lf\n", (double)x / (1<<16));
} else {
/* We can't divide 64 bits in kernel space */
tmpll = div_u64(tref << 16, 216);
x = div_u64(tmpll, bin);
}
/* Now, shift out the max bits (usually 7) and drop decimal part */
refdiv = ACAM_MAX_REFDIV;
hsdiv = (x << refdiv) >> 16;
/* Check the first decimal bit and approximate */
if ((x << refdiv) & (1 << 15))
hsdiv++;
/* until we have zeroes as LSB, shift out to decrease pll quotient */
while (refdiv > 0 && !(hsdiv & 1)) {
refdiv--;
hsdiv >>= 1;
}
*hsdiv_out = hsdiv;
*refdiv_out = refdiv;
/* Finally, calculate what we really have */
if (0) {
bin = (tref << refdiv) / 216 / hsdiv;
} else {
tmpll = div_u64(tref << refdiv, 216);
bin = div_u64(tmpll, hsdiv);
}
return (bin + 1); /* We always return the bin size in the I mode. Other modes should scale it appropriately. */
uint64_t tmpll;
int x, refdiv, hsdiv;
/*
* Tbin(I-mode) = (Tref << refdiv) / (216 * hsdiv)
*
* so, calling X the value "hsdiv >> refdiv" we have
*
* X = Tref / (216 * Tbin)
*
* Then, we can choose refdiv == 7 to have the best bits,
* and then shift out the zeros to get smaller values.
*
*/
if (0) {
x = (tref << 16) / 216 / bin;
//printf("x = %lf\n", (double)x / (1<<16));
} else {
/* We can't divide 64 bits in kernel space */
tmpll = div_u64(tref << 16, 216);
x = div_u64(tmpll, bin);
}
/* Now, shift out the max bits (usually 7) and drop decimal part */
refdiv = ACAM_MAX_REFDIV;
hsdiv = (x << refdiv) >> 16;
/* Check the first decimal bit and approximate */
if ((x << refdiv) & (1 << 15))
hsdiv++;
/* until we have zeroes as LSB, shift out to decrease pll quotient */
while (refdiv > 0 && !(hsdiv & 1)) {
refdiv--;
hsdiv >>= 1;
}
*hsdiv_out = hsdiv;
*refdiv_out = refdiv;
/* Finally, calculate what we really have */
if (0) {
bin = (tref << refdiv) / 216 / hsdiv;
} else {
tmpll = div_u64(tref << refdiv, 216);
bin = div_u64(tmpll, hsdiv);
}
/* We always return the bin size in the I mode.
Other modes should scale it appropriately. */
return (bin + 1);
}
static void acam_set_address(struct wrtd_fd_dev *fd, int addr)
{
if (fd->fd_acam_addr == addr)
return;
if (fd->fd_acam_addr == addr)
return;
if (fd->fd_acam_addr == -1) {
/* first time */
if (fd->fd_acam_addr == -1) {
/* first time */
fd_gpio_dir(fd, 0xf00, FD_GPIO_OUT);
}
fd_gpio_val(fd, 0xf00, addr << 8);
fd->fd_acam_addr = addr;
}
fd_gpio_val(fd, 0xf00, addr << 8);
fd->fd_acam_addr = addr;
}
/* Warning: acam_readl and acam_writel only work if GCR.BYPASS is set */
uint32_t acam_readl(struct wrtd_fd_dev *fd, int reg)
{
acam_set_address(fd, reg);
fd_writel(fd, FD_TDCSR_READ, FD_REG_TDCSR);
return fd_readl(fd, FD_REG_TDR) & ACAM_MASK;
acam_set_address(fd, reg);
fd_writel(fd, FD_TDCSR_READ, FD_REG_TDCSR);
return fd_readl(fd, FD_REG_TDR) & ACAM_MASK;
}
void acam_writel(struct wrtd_fd_dev *fd, int val, int reg)
{
acam_set_address(fd, reg);
fd_writel(fd, val, FD_REG_TDR);
fd_writel(fd, FD_TDCSR_WRITE, FD_REG_TDCSR);
acam_set_address(fd, reg);
fd_writel(fd, val, FD_REG_TDR);
fd_writel(fd, FD_TDCSR_WRITE, FD_REG_TDCSR);
}
static void acam_set_bypass(struct wrtd_fd_dev *fd, int on)
{
/* warning: this clears the "input enable" bit: call at init only */
fd_writel(fd, on ? FD_GCR_BYPASS : 0, FD_REG_GCR);
/* warning: this clears the "input enable" bit: call at init only */
fd_writel(fd, on ? FD_GCR_BYPASS : 0, FD_REG_GCR);
}
static inline int acam_is_pll_locked(struct wrtd_fd_dev *fd)
{
return !(acam_readl(fd, 12) & AR12_NotLocked);
return !(acam_readl(fd, 12) & AR12_NotLocked);
}
/* Two test functions to verify the bus is working -- Tom */
static int acam_test_addr_bit(struct wrtd_fd_dev *fd,
int base, int bit, int data)
{
int addr1 = base;
int addr2 = base + (1<<bit);
int reg;
reg = acam_readl(fd, addr1) & ~data;
acam_writel(fd, reg, addr1); /* zero the data mask */
reg = acam_readl(fd, addr2) | data;
acam_writel(fd, reg, addr2); /* set the data mask */
if ((acam_readl(fd, addr1) & data) != 0)
goto out;
if ((acam_readl(fd, addr2) & data) != data)
goto out;
/* the other way around */
reg = acam_readl(fd, addr2) & ~data;
acam_writel(fd, reg, addr2); /* zero the data mask */
reg = acam_readl(fd, addr1) | data;
acam_writel(fd, reg, addr1); /* set the data mask */
if ((acam_readl(fd, addr2) & data) != 0)
goto out;
if ((acam_readl(fd, addr1) & data) != data)
goto out;
return 0;
int addr1 = base;
int addr2 = base + (1<<bit);
int reg;
reg = acam_readl(fd, addr1) & ~data;
acam_writel(fd, reg, addr1); /* zero the data mask */
reg = acam_readl(fd, addr2) | data;
acam_writel(fd, reg, addr2); /* set the data mask */
if ((acam_readl(fd, addr1) & data) != 0)
goto out;
if ((acam_readl(fd, addr2) & data) != data)
goto out;
/* the other way around */
reg = acam_readl(fd, addr2) & ~data;
acam_writel(fd, reg, addr2); /* zero the data mask */
reg = acam_readl(fd, addr1) | data;
acam_writel(fd, reg, addr1); /* set the data mask */
if ((acam_readl(fd, addr2) & data) != 0)
goto out;
if ((acam_readl(fd, addr1) & data) != data)
goto out;
return 0;
out:
pr_error("ACAM address bit %d failure\n", bit);
return -EIO;
pr_error("ACAM address bit %d failure\n", bit);
return -EIO;
}
static int acam_test_bus(struct wrtd_fd_dev *fd)
{
int err = 0, i, v;
/* Use register 5 to checke the data bits */
for(i = 0; i & ACAM_MASK; i <<= 1) {
acam_writel(fd, i, 5);
acam_readl(fd, 0);
v = acam_readl(fd, 5);
if (v != i)
goto out;
acam_writel(fd, ~i & ACAM_MASK, 5);
acam_readl(fd, 0);
v = acam_readl(fd, 5);
if (v != (~i & ACAM_MASK))
goto out;
}
err += acam_test_addr_bit(fd, 0, 0, 0x000001);
err += acam_test_addr_bit(fd, 1, 1, 0x000008);
err += acam_test_addr_bit(fd, 0, 2, 0x000001);
err += acam_test_addr_bit(fd, 3, 3, 0x010000);
if (err)
return -EIO;
return 0;
int err = 0, i, v;
/* Use register 5 to checke the data bits */
for(i = 0; i & ACAM_MASK; i <<= 1) {
acam_writel(fd, i, 5);
acam_readl(fd, 0);
v = acam_readl(fd, 5);
if (v != i)
goto out;
acam_writel(fd, ~i & ACAM_MASK, 5);
acam_readl(fd, 0);
v = acam_readl(fd, 5);
if (v != (~i & ACAM_MASK))
goto out;
}
err += acam_test_addr_bit(fd, 0, 0, 0x000001);
err += acam_test_addr_bit(fd, 1, 1, 0x000008);
err += acam_test_addr_bit(fd, 0, 2, 0x000001);
err += acam_test_addr_bit(fd, 3, 3, 0x010000);
if (err)
return -EIO;
return 0;
out:
pp_printf("Error: ACAM data bit 0x%06x failure\n", i);
return -EIO;
pp_printf("Error: ACAM data bit 0x%06x failure\n", i);
return -EIO;
}
/* We need to write come static configuration in the registers */
struct acam_init_data {
int addr;
int val;
int addr;
int val;
};
/* Commented values are not constant, they are added at runtime (see later) */
static struct acam_init_data acam_init_regs_gmode[] = {
{0, AR0_ROsc | AR0_RiseEn0 | AR0_RiseEn1 | AR0_HQSel},
{1, AR1_Adj(0, 0) | AR1_Adj(1, 0) | AR1_Adj(2, 5) |
AR1_Adj(3, 0) | AR1_Adj(4, 5) | AR1_Adj(5, 0) | AR1_Adj(6, 5)},
{2, AR2_GMode | AR2_Adj(7, 0) | AR2_Adj(8, 5) |
AR2_DelRise1(0) | AR2_DelFall1(0) | AR2_DelRise2(0) | AR2_DelFall2(0)},
{3, AR3_DelTx(1,3) | AR3_DelTx(2,3) | AR3_DelTx(3,3) | AR3_DelTx(4,3) |
AR3_DelTx(5,3) | AR3_DelTx(6,3) | AR3_DelTx(7,3) | AR3_DelTx(8,3) |
AR3_RaSpeed(0,3) | AR3_RaSpeed(1,3) | AR3_RaSpeed(2,3)},
{4, AR4_EFlagHiZN | AR4_RaSpeed(3,3) | AR4_RaSpeed(4,3) |
AR4_RaSpeed(5,3) | AR4_RaSpeed(6,3) | AR4_RaSpeed(7,3) | AR4_RaSpeed(8,3)},
{5, AR5_StartRetrig
| 0 /* AR5_StartOff1(hw->calib.acam_start_offset) */
| AR5_MasterAluTrig},
{6, AR6_Fill(200) | AR6_PowerOnECL},
{7, /* AR7_HSDiv(hsdiv) | AR7_RefClkDiv(refdiv) */ 0
| AR7_ResAdj | AR7_NegPhase},
{11, 0x7ff0000},
{12, 0x0000000},
{14, 0},
/* finally, reset */
{4, AR4_EFlagHiZN | AR4_MasterReset | AR4_StartTimer(0)},
{0, AR0_ROsc | AR0_RiseEn0 | AR0_RiseEn1 | AR0_HQSel},
{1, AR1_Adj(0, 0) | AR1_Adj(1, 0) | AR1_Adj(2, 5) |
AR1_Adj(3, 0) | AR1_Adj(4, 5) | AR1_Adj(5, 0) | AR1_Adj(6, 5)},
{2, AR2_GMode | AR2_Adj(7, 0) | AR2_Adj(8, 5) |
AR2_DelRise1(0) | AR2_DelFall1(0) | AR2_DelRise2(0) | AR2_DelFall2(0)},
{3, AR3_DelTx(1,3) | AR3_DelTx(2,3) | AR3_DelTx(3,3) | AR3_DelTx(4,3) |
AR3_DelTx(5,3) | AR3_DelTx(6,3) | AR3_DelTx(7,3) | AR3_DelTx(8,3) |
AR3_RaSpeed(0,3) | AR3_RaSpeed(1,3) | AR3_RaSpeed(2,3)},
{4, AR4_EFlagHiZN | AR4_RaSpeed(3,3) | AR4_RaSpeed(4,3) |
AR4_RaSpeed(5,3) | AR4_RaSpeed(6,3) | AR4_RaSpeed(7,3) | AR4_RaSpeed(8,3)},
{5, AR5_StartRetrig
| 0 /* AR5_StartOff1(hw->calib.acam_start_offset) */
| AR5_MasterAluTrig},
{6, AR6_Fill(200) | AR6_PowerOnECL},
{7, /* AR7_HSDiv(hsdiv) | AR7_RefClkDiv(refdiv) */ 0
| AR7_ResAdj | AR7_NegPhase},
{11, 0x7ff0000},
{12, 0x0000000},
{14, 0},
/* finally, reset */
{4, AR4_EFlagHiZN | AR4_MasterReset | AR4_StartTimer(0)},
};
static struct acam_init_data acam_init_regs_imode[] = {
{0, AR0_TRiseEn(0) | AR0_HQSel | AR0_ROsc},
{2, AR2_IMode},
{5, AR5_StartOff1(3000) | AR5_MasterAluTrig},
{6, 0},
{7, /* AR7_HSDiv(hsdiv) | AR7_RefClkDiv(refdiv) */ 0
| AR7_ResAdj | AR7_NegPhase},
{11, 0x7ff0000},
{12, 0x0000000},
{14, 0},
/* finally, reset */
{4, AR4_EFlagHiZN | AR4_MasterReset | AR4_StartTimer(0)},
{0, AR0_TRiseEn(0) | AR0_HQSel | AR0_ROsc},
{2, AR2_IMode},
{5, AR5_StartOff1(3000) | AR5_MasterAluTrig},
{6, 0},
{7, /* AR7_HSDiv(hsdiv) | AR7_RefClkDiv(refdiv) */ 0
| AR7_ResAdj | AR7_NegPhase},
{11, 0x7ff0000},
{12, 0x0000000},
{14, 0},
/* finally, reset */
{4, AR4_EFlagHiZN | AR4_MasterReset | AR4_StartTimer(0)},
};
static int acam_configure(struct wrtd_fd_dev *fd, enum fd_acam_modes mode,
const struct acam_init_data *regs, int n_regs )
{
int i, hsdiv, refdiv, reg7val;
const struct acam_init_data *p;
uint32_t regval;
int locked = 0;
int i, hsdiv, refdiv, reg7val;
const struct acam_init_data *p;
uint32_t regval;
int locked = 0;
fd->fd_bin_size = acam_calc_pll(ACAM_FP_TREF, ACAM_FP_BIN,
fd->fd_bin_size = acam_calc_pll(ACAM_FP_TREF, ACAM_FP_BIN,
&hsdiv, &refdiv);
reg7val = AR7_HSDiv(hsdiv) | AR7_RefClkDiv(refdiv);
reg7val = AR7_HSDiv(hsdiv) | AR7_RefClkDiv(refdiv);
pr_debug("ACAM config: mode %d bin 0x%x, hsdiv %i, refdiv %i\n",
pr_debug("ACAM config: mode %d bin 0x%x, hsdiv %i, refdiv %i\n",
mode, fd->fd_bin_size, hsdiv, refdiv);
/* Disable TDC inputs prior to configuring */
fd_writel(fd, FD_TDCSR_STOP_DIS | FD_TDCSR_START_DIS, FD_REG_TDCSR);
/* Disable the ACAM PLL for a while to make sure it is reset */
acam_writel(fd, 0, 0);
acam_writel(fd, 7, 0);
mdelay(100);
for (p = regs, i = 0; i < n_regs; p++, i++) {
regval = p->val;
if (p->addr == 7)
regval |= reg7val;
if (p->addr == 5 && mode == ACAM_GMODE )
regval |= AR5_StartOff1(ACAM_GMODE_START_OFFSET);
if (p->addr == 6 && mode == ACAM_GMODE )
regval |= AR6_StartOff2(ACAM_GMODE_START_OFFSET);
acam_writel(fd, regval, p->addr);
}
for (i = 0; i < 20; i++)
{
if (acam_is_pll_locked(fd))
{
locked = 1;
break;
}
mdelay(100);
}
if(!locked)
{
pp_printf("Error: ACAM PLL doesn't lock.\n");
return -1;
}
/* after config, set the FIFO address for further reads */
acam_set_address(fd, 8);
return 0;
/* Disable TDC inputs prior to configuring */
fd_writel(fd, FD_TDCSR_STOP_DIS | FD_TDCSR_START_DIS, FD_REG_TDCSR);
/* Disable the ACAM PLL for a while to make sure it is reset */
acam_writel(fd, 0, 0);
acam_writel(fd, 7, 0);
mdelay(100);
for (p = regs, i = 0; i < n_regs; p++, i++) {
regval = p->val;
if (p->addr == 7)
regval |= reg7val;
if (p->addr == 5 && mode == ACAM_GMODE )
regval |= AR5_StartOff1(ACAM_GMODE_START_OFFSET);
if (p->addr == 6 && mode == ACAM_GMODE )
regval |= AR6_StartOff2(ACAM_GMODE_START_OFFSET);
acam_writel(fd, regval, p->addr);
}
for (i = 0; i < 20; i++)
{
if (acam_is_pll_locked(fd))
{
locked = 1;
break;
}
mdelay(100);
}
if(!locked)
{
pp_printf("Error: ACAM PLL doesn't lock.\n");
return -1;
}
/* after config, set the FIFO address for further reads */
acam_set_address(fd, 8);
return 0;
}
int fd_acam_init(struct wrtd_fd_dev *fd)
{
int ret;
fd->fd_acam_addr = -1; /* First time must be activated */
int ret;
fd->fd_acam_addr = -1; /* First time must be activated */
acam_set_bypass(fd, 1); /* Driven by host, not core */
acam_set_bypass(fd, 1); /* Driven by host, not core */
if ( (ret = acam_test_bus(fd)) )
return ret;
if ( (ret = acam_test_bus(fd)) )
return ret;
if ( (ret = acam_configure(fd, ACAM_IMODE, acam_init_regs_imode, ARRAY_SIZE(acam_init_regs_imode))) )
return ret;
if ( (ret = acam_configure(fd, ACAM_IMODE, acam_init_regs_imode,
ARRAY_SIZE(acam_init_regs_imode))) )
return ret;
if ( (ret = fd_calibrate_outputs(fd)) )
return ret;
if ( (ret = fd_calibrate_outputs(fd)) )
return ret;
if ( (ret = acam_configure(fd, ACAM_GMODE, acam_init_regs_gmode, ARRAY_SIZE(acam_init_regs_gmode))) )
return ret;
if ( (ret = acam_configure(fd, ACAM_GMODE, acam_init_regs_gmode,
ARRAY_SIZE(acam_init_regs_gmode))) )
return ret;
acam_set_bypass(fd, 0); /* Driven by core, not host */
acam_set_bypass(fd, 0); /* Driven by core, not host */
/* Clear and disable the timestamp readout buffer */
fd_writel(fd, FD_TSBCR_PURGE | FD_TSBCR_RST_SEQ, FD_REG_TSBCR);
/* Clear and disable the timestamp readout buffer */
fd_writel(fd, FD_TSBCR_PURGE | FD_TSBCR_RST_SEQ, FD_REG_TSBCR);
/*
* Program the ACAM-specific TS registers w pre-defined calib values:
* - bin -> internal timebase scalefactor (ADSFR),
* - Start offset (must be consistent with value in ACAM reg 4)
* - timestamp merging control register (ATMCR)
* GMode fix: we no longer use the values from the EEPROM (they are fixed anyway)
*/
/*
* Program the ACAM-specific TS registers w pre-defined calib values:
* - bin -> internal timebase scalefactor (ADSFR),
* - Start offset (must be consistent with value in ACAM reg 4)
* - timestamp merging control register (ATMCR)
* GMode fix: we no longer use the values from the EEPROM (they are fixed anyway)
*/
fd_writel(fd, ACAM_GMODE_ADSFR, FD_REG_ADSFR);
fd_writel(fd, ACAM_GMODE_ASOR, FD_REG_ASOR);
fd_writel(fd, ACAM_GMODE_ATMCR, FD_REG_ATMCR);
fd_writel(fd, ACAM_GMODE_ADSFR, FD_REG_ADSFR);
fd_writel(fd, ACAM_GMODE_ASOR, FD_REG_ASOR);
fd_writel(fd, ACAM_GMODE_ATMCR, FD_REG_ATMCR);
return 0;
return 0;
}
/*
/**
* @file fd_calibrate.c
*
* Calibrate the output path.
*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* Author: Alessandro Rubini <rubini@gnudd.com>
* Author: Dimitris Lampridis <dimitris.lampridis@cern.ch>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2 as published by the Free Software Foundation or, at your
* option, any later version.
* Copyright (c) 2012-2019 CERN (home.cern)
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
#include "mockturtle-rt.h"
......@@ -21,10 +22,9 @@
/* This is the same as in ./acam.c: use only at init time */
static void acam_set_bypass(struct wrtd_fd_dev *fd, int on)
{
fd_writel(fd, on ? FD_GCR_BYPASS : 0, FD_REG_GCR);
fd_writel(fd, on ? FD_GCR_BYPASS : 0, FD_REG_GCR);
}
/*
* Measures the the FPGA-generated TDC start and the output of one of
* the fine delay chips (channel) at a pre-defined number of taps
......@@ -36,80 +36,80 @@ static void acam_set_bypass(struct wrtd_fd_dev *fd, int on)
/* Note: channel is the "internal" one: 0..3 */
static uint64_t output_delay_ps(struct wrtd_fd_dev *fd, int ch, int fine, int n)
{
int i;
uint64_t acc = 0;
int i;
uint64_t acc = 0;
/* Disable the output for the channel being calibrated */
fd_gpio_clr(fd, FD_GPIO_OUTPUT_EN(FD_CH_EXT(ch)));
/* Disable the output for the channel being calibrated */
fd_gpio_clr(fd, FD_GPIO_OUTPUT_EN(FD_CH_EXT(ch)));
/* Enable the stop input in ACAM for the channel being calibrated */
acam_writel(fd, AR0_TRiseEn(0) | AR0_TRiseEn(FD_CH_EXT(ch))
| AR0_HQSel | AR0_ROsc, 0);
/* Enable the stop input in ACAM for the channel being calibrated */
acam_writel(fd, AR0_TRiseEn(0) | AR0_TRiseEn(FD_CH_EXT(ch))
| AR0_HQSel | AR0_ROsc, 0);
/* Program the output delay line setpoint */
fd_drv_ch_writel(fd, ch, fine, FD_REG_FRR);
fd_drv_ch_writel(fd, ch, FD_DCR_ENABLE | FD_DCR_MODE | FD_DCR_UPDATE,
/* Program the output delay line setpoint */
fd_drv_ch_writel(fd, ch, fine, FD_REG_FRR);
fd_drv_ch_writel(fd, ch, FD_DCR_ENABLE | FD_DCR_MODE | FD_DCR_UPDATE,
FD_REG_DCR);
fd_drv_ch_writel(fd, ch, FD_DCR_FORCE_DLY | FD_DCR_ENABLE, FD_REG_DCR);
/*
* Set the calibration pulse mask to genrate calibration
* pulses only on one channel at a time. This minimizes the
* crosstalk in the output buffer which can severely decrease
* the accuracy of calibration measurements
*/
fd_writel(fd, FD_CALR_PSEL_W(1 << ch), FD_REG_CALR);
udelay(1);
/* Do n_avgs single measurements and average */
for (i = 0; i < n; i++) {
uint32_t fr;
/* Re-arm the ACAM (it's working in a single-shot mode) */
fd_writel(fd, FD_TDCSR_ALUTRIG, FD_REG_TDCSR);
udelay(1);
/* Produce a calib pulse on the TDC start and the output ch */
fd_writel(fd, FD_CALR_CAL_PULSE |
FD_CALR_PSEL_W(1 << ch), FD_REG_CALR);
udelay(1);
/* read the tag, convert to picoseconds (fixed point: 16.16) */
fr = acam_readl(fd, 8 /* fifo */) & 0x1ffff;
//pp_printf("i %d fr %x\n\r", i, fr);
acc += fr * fd->fd_bin_size;
}
fd_drv_ch_writel(fd, ch, 0, FD_REG_DCR);
/* Calculate avg, min max */
acc = div_u64((acc + n / 2), n );
//pp_printf("ch %d avg %08x%08x\n\r", ch, (uint32_t)(acc>>32), (uint32_t)acc);
return acc;
fd_drv_ch_writel(fd, ch, FD_DCR_FORCE_DLY | FD_DCR_ENABLE, FD_REG_DCR);
/*
* Set the calibration pulse mask to genrate calibration
* pulses only on one channel at a time. This minimizes the
* crosstalk in the output buffer which can severely decrease
* the accuracy of calibration measurements
*/
fd_writel(fd, FD_CALR_PSEL_W(1 << ch), FD_REG_CALR);
udelay(1);
/* Do n_avgs single measurements and average */
for (i = 0; i < n; i++) {
uint32_t fr;
/* Re-arm the ACAM (it's working in a single-shot mode) */
fd_writel(fd, FD_TDCSR_ALUTRIG, FD_REG_TDCSR);
udelay(1);
/* Produce a calib pulse on the TDC start and the output ch */
fd_writel(fd, FD_CALR_CAL_PULSE |
FD_CALR_PSEL_W(1 << ch), FD_REG_CALR);
udelay(1);
/* read the tag, convert to picoseconds (fixed point: 16.16) */
fr = acam_readl(fd, 8 /* fifo */) & 0x1ffff;
//pp_printf("i %d fr %x\n\r", i, fr);
acc += fr * fd->fd_bin_size;
}
fd_drv_ch_writel(fd, ch, 0, FD_REG_DCR);
/* Calculate avg, min max */
acc = div_u64((acc + n / 2), n );
//pp_printf("ch %d avg %08x%08x\n\r", ch, (uint32_t)(acc>>32), (uint32_t)acc);
return acc;
}
static int fd_find_8ns_tap(struct wrtd_fd_dev *fd, int ch)
{
int l = 0, mid, r = FD_NUM_TAPS - 1;
uint64_t bias, dly;
/*
* Measure the delay at zero setting, so it can be further
* subtracted to get only the delay part introduced by the
* delay line (ingoring the TDC, FPGA and routing delays).
* Use a binary search of the delay value.
*/
bias = output_delay_ps(fd, ch, 0, FD_CAL_STEPS);
while( r - l > 1) {
mid = ( l + r) / 2;
dly = output_delay_ps(fd, ch, mid, FD_CAL_STEPS) - bias;
if(dly < 8000 << 16)
l = mid;
else
r = mid;
}
return l;
int l = 0, mid, r = FD_NUM_TAPS - 1;
uint64_t bias, dly;
/*
* Measure the delay at zero setting, so it can be further
* subtracted to get only the delay part introduced by the
* delay line (ingoring the TDC, FPGA and routing delays).
* Use a binary search of the delay value.
*/
bias = output_delay_ps(fd, ch, 0, FD_CAL_STEPS);
while( r - l > 1) {
mid = ( l + r) / 2;
dly = output_delay_ps(fd, ch, mid, FD_CAL_STEPS) - bias;
if(dly < 8000 << 16)
l = mid;
else
r = mid;
}
return l;
}
......@@ -121,17 +121,17 @@ static int fd_find_8ns_tap(struct wrtd_fd_dev *fd, int ch)
*/
int fd_calibrate_outputs(struct wrtd_fd_dev *fd)
{
int ch;
int measured;
int ch;
int measured;
acam_set_bypass(fd, 1); /* not useful */
fd_writel(fd, FD_TDCSR_START_EN | FD_TDCSR_STOP_EN, FD_REG_TDCSR);
acam_set_bypass(fd, 1); /* not useful */
fd_writel(fd, FD_TDCSR_START_EN | FD_TDCSR_STOP_EN, FD_REG_TDCSR);
for (ch = 0; ch < FD_NUM_CHANNELS; ch++) {
for (ch = 0; ch < FD_NUM_CHANNELS; ch++) {
measured = fd_find_8ns_tap(fd, ch);
fd_drv_ch_writel(fd, ch, measured, FD_REG_FRR);
fd_drv_ch_writel(fd, ch, measured, FD_REG_FRR);
pr_debug("Channel %d: 8ns @ %i taps.\n\r", ch+1, measured );
}
return 0;
pr_debug("Channel %d: 8ns @ %i taps.\n\r", ch+1, measured );
}
return 0;
}
/*
/**
* @file fd_gpio.c
*
* SPI access to fine-delay internals
*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* Author: Alessandro Rubini <rubini@gnudd.com>
* Author: Dimitris Lampridis <dimitris.lampridis@cern.ch>
*
* Copyright (c) 2012-2019 CERN (home.cern)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2 as published by the Free Software Foundation or, at your
* option, any later version.
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
#include "mockturtle-rt.h"
......@@ -22,58 +23,58 @@ int gpio_writel(struct wrtd_fd_dev *fd, int val, int reg)
int rval = fd_spi_xfer(fd, FD_CS_GPIO, 24,
0x4e0000 | (reg << 8) | val, NULL);
return rval;
return rval;
}
static int gpio_readl(struct wrtd_fd_dev *fd, int reg)
{
uint32_t ret;
int err;
uint32_t ret;
int err;
err = fd_spi_xfer(fd, FD_CS_GPIO, 24,
0x4f0000 | (reg << 8), &ret);
err = fd_spi_xfer(fd, FD_CS_GPIO, 24,
0x4f0000 | (reg << 8), &ret);
if (err < 0)
return err;
return ret & 0xff;
if (err < 0)
return err;
return ret & 0xff;
}
static int gpio_writel_with_retry(struct wrtd_fd_dev *fd, int val, int reg)
{
int retries = SPI_RETRIES, rv;
while(retries--)
{
gpio_writel(fd, val, reg);
rv = gpio_readl(fd, reg);
if(rv >= 0 && (rv == val))
{
if(SPI_RETRIES-1-retries > 0)
pr_debug("gpio_writel_with_retry: succeded after %d retries\n",
SPI_RETRIES - 1 - retries);
return 0;
}
}
return -EIO;
int retries = SPI_RETRIES, rv;
while(retries--)
{
gpio_writel(fd, val, reg);
rv = gpio_readl(fd, reg);
if(rv >= 0 && (rv == val))
{
if(SPI_RETRIES-1-retries > 0)
pr_debug("gpio_writel_with_retry: succeded after %d retries\n",
SPI_RETRIES - 1 - retries);
return 0;
}
}
return -EIO;
}
void fd_gpio_dir(struct wrtd_fd_dev *fd, int mask, int dir)
{
fd->fd_mcp_iodir &= ~mask;
if (dir == FD_GPIO_IN)
fd->fd_mcp_iodir |= mask;
fd->fd_mcp_iodir &= ~mask;
if (dir == FD_GPIO_IN)
fd->fd_mcp_iodir |= mask;
gpio_writel_with_retry(fd, (fd->fd_mcp_iodir & 0xff), FD_MCP_IODIR);
gpio_writel_with_retry(fd, (fd->fd_mcp_iodir >> 8), FD_MCP_IODIR+1);
gpio_writel_with_retry(fd, (fd->fd_mcp_iodir & 0xff), FD_MCP_IODIR);
gpio_writel_with_retry(fd, (fd->fd_mcp_iodir >> 8), FD_MCP_IODIR+1);
}
void fd_gpio_val(struct wrtd_fd_dev *fd, int mask, int values)
{
fd->fd_mcp_olat &= ~mask;
fd->fd_mcp_olat |= values;
fd->fd_mcp_olat &= ~mask;
fd->fd_mcp_olat |= values;
gpio_writel_with_retry(fd, (fd->fd_mcp_olat & 0xff), FD_MCP_OLAT);
gpio_writel_with_retry(fd, (fd->fd_mcp_olat >> 8), FD_MCP_OLAT+1);
gpio_writel_with_retry(fd, (fd->fd_mcp_olat & 0xff), FD_MCP_OLAT);
gpio_writel_with_retry(fd, (fd->fd_mcp_olat >> 8), FD_MCP_OLAT+1);
}
void fd_gpio_set_clr(struct wrtd_fd_dev *fd, int mask, int set)
......@@ -83,28 +84,28 @@ void fd_gpio_set_clr(struct wrtd_fd_dev *fd, int mask, int set)
int fd_gpio_init(struct wrtd_fd_dev *fd)
{
int i, val;
fd->fd_mcp_iodir = 0xffff;
fd->fd_mcp_olat = 0;
if (gpio_writel(fd, 0x00, FD_MCP_IOCON) < 0)
goto out;
/* Try to read and write a register to test the SPI connection */
for (val = 0xaa; val >= 0; val -= 0x11) {
if (gpio_writel(fd, val, FD_MCP_IPOL) < 0)
goto out;
i = gpio_readl(fd, FD_MCP_IPOL);
if (i < 0)
goto out;
if (i != val) {
pr_error("GPIO comm error (got 0x%x, expected 0x%x)\n", i, val);
return -EIO;
}
}
/* last time we wrote 0, ok */
return 0;
int i, val;
fd->fd_mcp_iodir = 0xffff;
fd->fd_mcp_olat = 0;
if (gpio_writel(fd, 0x00, FD_MCP_IOCON) < 0)
goto out;
/* Try to read and write a register to test the SPI connection */
for (val = 0xaa; val >= 0; val -= 0x11) {
if (gpio_writel(fd, val, FD_MCP_IPOL) < 0)
goto out;
i = gpio_readl(fd, FD_MCP_IPOL);
if (i < 0)
goto out;
if (i != val) {
pr_error("GPIO comm error (got 0x%x, expected 0x%x)\n", i, val);
return -EIO;
}
}
/* last time we wrote 0, ok */
return 0;
out:
return -EIO;
return -EIO;
}
/*
/**
* @file fd_i2c.c
*
* I2C access (on-board EEPROM)
*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* Author: Alessandro Rubini <rubini@gnudd.com>
* Author: Dimitris Lampridis <dimitris.lampridis@cern.ch>
*
* Copyright (c) 2012-2019 CERN (home.cern)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2 as published by the Free Software Foundation or, at your
* option, any later version.
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
#include "mockturtle-rt.h"
......@@ -18,24 +19,24 @@
static void set_sda(struct wrtd_fd_dev *fd, int val)
{
uint32_t reg;
uint32_t reg;
reg = fd_readl(fd, FD_REG_I2CR) & ~FD_I2CR_SDA_OUT;
if (val)
reg |= FD_I2CR_SDA_OUT;
fd_writel(fd, reg, FD_REG_I2CR);
udelay(3);
reg = fd_readl(fd, FD_REG_I2CR) & ~FD_I2CR_SDA_OUT;
if (val)
reg |= FD_I2CR_SDA_OUT;
fd_writel(fd, reg, FD_REG_I2CR);
udelay(3);
}
static void set_scl(struct wrtd_fd_dev *fd, int val)
{
uint32_t reg;
uint32_t reg;
reg = fd_readl(fd, FD_REG_I2CR) & ~FD_I2CR_SCL_OUT;
if (val)
reg |= FD_I2CR_SCL_OUT;
fd_writel(fd, reg, FD_REG_I2CR);
udelay(3);
reg = fd_readl(fd, FD_REG_I2CR) & ~FD_I2CR_SCL_OUT;
if (val)
reg |= FD_I2CR_SCL_OUT;
fd_writel(fd, reg, FD_REG_I2CR);
udelay(3);
}
static int get_sda(struct wrtd_fd_dev *fd)
......@@ -46,105 +47,105 @@ static int get_sda(struct wrtd_fd_dev *fd)
static void mi2c_start(struct wrtd_fd_dev *fd)
{
set_sda(fd, 0);
set_scl(fd, 0);
set_scl(fd, 0);
}
static void mi2c_stop(struct wrtd_fd_dev *fd)
{
set_sda(fd, 0);
set_scl(fd, 1);
set_sda(fd, 1);
set_sda(fd, 0);
set_scl(fd, 1);
set_sda(fd, 1);
}
int mi2c_put_byte(struct wrtd_fd_dev *fd, int data)
{
int i;
int ack;
int i;
int ack;
for (i = 0; i < 8; i++, data<<=1) {
set_sda(fd, data & 0x80);
set_scl(fd, 1);
set_scl(fd, 0);
}
for (i = 0; i < 8; i++, data<<=1) {
set_sda(fd, data & 0x80);
set_scl(fd, 1);
set_scl(fd, 0);
}
set_sda(fd, 1);
set_scl(fd, 1);
set_sda(fd, 1);
set_scl(fd, 1);
ack = get_sda(fd);
ack = get_sda(fd);
set_scl(fd, 0);
set_sda(fd, 0);
set_scl(fd, 0);
set_sda(fd, 0);
return ack ? -EIO : 0; /* ack low == success */
return ack ? -EIO : 0; /* ack low == success */
}
int mi2c_get_byte(struct wrtd_fd_dev *fd, unsigned char *data, int sendack)
{
int i;
int indata = 0;
/* assert: scl is low */
set_scl(fd, 0);
set_sda(fd, 1);
for (i = 0; i < 8; i++) {
set_scl(fd, 1);
indata <<= 1;
if (get_sda(fd))
indata |= 0x01;
set_scl(fd, 0);
}
set_sda(fd, (sendack ? 0 : 1));
set_scl(fd, 1);
set_scl(fd, 0);
set_sda(fd, 0);
*data= indata;
return 0;
int i;
int indata = 0;
/* assert: scl is low */
set_scl(fd, 0);
set_sda(fd, 1);
for (i = 0; i < 8; i++) {
set_scl(fd, 1);
indata <<= 1;
if (get_sda(fd))
indata |= 0x01;
set_scl(fd, 0);
}
set_sda(fd, (sendack ? 0 : 1));
set_scl(fd, 1);
set_scl(fd, 0);
set_sda(fd, 0);
*data= indata;
return 0;
}
void mi2c_init(struct wrtd_fd_dev *fd)
{
set_scl(fd, 1);
set_sda(fd, 1);
set_scl(fd, 1);
set_sda(fd, 1);
}
void mi2c_scan(struct wrtd_fd_dev *fd)
{
int i;
for(i = 0; i < 256; i += 2) {
mi2c_start(fd);
if(!mi2c_put_byte(fd, i))
pr_debug("Found i2c device at 0x%x\n",
i >> 1);
mi2c_stop(fd);
}
int i;
for(i = 0; i < 256; i += 2) {
mi2c_start(fd);
if(!mi2c_put_byte(fd, i))
pr_debug("Found i2c device at 0x%x\n",
i >> 1);
mi2c_stop(fd);
}
}
/* FIXME: this is very inefficient: read several bytes in a row instead */
int fd_eeprom_read(struct wrtd_fd_dev *fd, int i2c_addr, uint32_t offset,
void *buf, size_t size)
{
int i;
uint8_t *buf8 = buf;
unsigned char c;
for(i = 0; i < size; i++) {
mi2c_start(fd);
if(mi2c_put_byte(fd, i2c_addr << 1) < 0) {
mi2c_stop(fd);
return -EIO;
}
mi2c_put_byte(fd, (offset >> 8) & 0xff);
mi2c_put_byte(fd, offset & 0xff);
offset++;
mi2c_stop(fd);
mi2c_start(fd);
mi2c_put_byte(fd, (i2c_addr << 1) | 1);
mi2c_get_byte(fd, &c, 0);
*buf8++ = c;
mi2c_stop(fd);
}
return size;
int i;
uint8_t *buf8 = buf;
unsigned char c;
for(i = 0; i < size; i++) {
mi2c_start(fd);
if(mi2c_put_byte(fd, i2c_addr << 1) < 0) {
mi2c_stop(fd);
return -EIO;
}
mi2c_put_byte(fd, (offset >> 8) & 0xff);
mi2c_put_byte(fd, offset & 0xff);
offset++;
mi2c_stop(fd);
mi2c_start(fd);
mi2c_put_byte(fd, (i2c_addr << 1) | 1);
mi2c_get_byte(fd, &c, 0);
*buf8++ = c;
mi2c_stop(fd);
}
return size;
}
/*
* Copyright (C) 2018 CERN (www.cern.ch)
/**
* @file fd_init.c
*
* Author: Federico Vaga <federico.vaga@cern.ch>
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* Author: Dimitris Lampridis <dimitris.lampridis@cern.ch>
*
* Copyright (c) 2018-2019 CERN (home.cern)
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
#include <errno.h>
#include <string.h>
......@@ -36,102 +39,102 @@
*/
static void fd_do_reset(struct wrtd_fd_dev *fd, int hw_reset)
{
if (hw_reset) {
/* clear RSTS_RST_FMC bit, set RSTS_RST_CORE bit*/
fd_writel(fd, FD_RSTR_LOCK_W(0xdead) | FD_RSTR_RST_CORE_MASK,
if (hw_reset) {
/* clear RSTS_RST_FMC bit, set RSTS_RST_CORE bit*/
fd_writel(fd, FD_RSTR_LOCK_W(0xdead) | FD_RSTR_RST_CORE_MASK,
FD_REG_RSTR);
UDELAY(10000);
fd_writel(fd, FD_RSTR_LOCK_W(0xdead) | FD_RSTR_RST_CORE_MASK
UDELAY(10000);
fd_writel(fd, FD_RSTR_LOCK_W(0xdead) | FD_RSTR_RST_CORE_MASK
| FD_RSTR_RST_FMC_MASK, FD_REG_RSTR);
/* TPS3307 supervisor needs time to de-assert master reset */
MDELAY(600);
return;
}
/* clear RSTS_RST_CORE bit, set RSTS_RST_FMC bit */
fd_writel(fd, FD_RSTR_LOCK_W(0xdead) | FD_RSTR_RST_FMC_MASK,
FD_REG_RSTR);
UDELAY(1000);
fd_writel(fd, FD_RSTR_LOCK_W(0xdead) | FD_RSTR_RST_FMC_MASK
| FD_RSTR_RST_CORE_MASK, FD_REG_RSTR);
UDELAY(1000);
/* TPS3307 supervisor needs time to de-assert master reset */
MDELAY(600);
return;
}
/* clear RSTS_RST_CORE bit, set RSTS_RST_FMC bit */
fd_writel(fd, FD_RSTR_LOCK_W(0xdead) | FD_RSTR_RST_FMC_MASK,
FD_REG_RSTR);
UDELAY(1000);
fd_writel(fd, FD_RSTR_LOCK_W(0xdead) | FD_RSTR_RST_FMC_MASK
| FD_RSTR_RST_CORE_MASK, FD_REG_RSTR);
UDELAY(1000);
}
/* Some init procedures to be intermixed with subsystems */
int fd_gpio_defaults(struct wrtd_fd_dev *fd)
{
fd_gpio_dir(fd, FD_GPIO_TRIG_INTERNAL, FD_GPIO_OUT);
fd_gpio_set(fd, FD_GPIO_TRIG_INTERNAL);
fd_gpio_dir(fd, FD_GPIO_TRIG_INTERNAL, FD_GPIO_OUT);
fd_gpio_set(fd, FD_GPIO_TRIG_INTERNAL);
fd_gpio_set(fd, FD_GPIO_OUTPUT_MASK);
fd_gpio_dir(fd, FD_GPIO_OUTPUT_MASK, FD_GPIO_OUT);
fd_gpio_set(fd, FD_GPIO_OUTPUT_MASK);
fd_gpio_dir(fd, FD_GPIO_OUTPUT_MASK, FD_GPIO_OUT);
fd_gpio_dir(fd, FD_GPIO_TERM_EN, FD_GPIO_OUT);
fd_gpio_clr(fd, FD_GPIO_TERM_EN);
return 0;
fd_gpio_dir(fd, FD_GPIO_TERM_EN, FD_GPIO_OUT);
fd_gpio_clr(fd, FD_GPIO_TERM_EN);
return 0;
}
int fd_reset_again(struct wrtd_fd_dev *fd)
{
/* Reset the FD core once we have proper reference/TDC clocks */
fd_do_reset(fd, 0 /* not hw */);
/* Reset the FD core once we have proper reference/TDC clocks */
fd_do_reset(fd, 0 /* not hw */);
MDELAY(10);
MDELAY(10);
if (! (fd_readl(fd, FD_REG_GCR) & FD_GCR_DDR_LOCKED) )
{
pr_error("timeout waiting for GCR lock bit\n");
return -EIO;
}
if (! (fd_readl(fd, FD_REG_GCR) & FD_GCR_DDR_LOCKED) )
{
pr_error("timeout waiting for GCR lock bit\n");
return -EIO;
}
fd_do_reset(fd, 0 /* not hw */);
return 0;
fd_do_reset(fd, 0 /* not hw */);
return 0;
}
/* FIXME missing all calibration */
int fd_init(struct wrtd_fd_dev *fd)
{
int err, ch;
int err, ch;
pr_debug("Initializing the Fine Delay board...\n");
pr_debug("Initializing the Fine Delay board...\n");
fd_do_reset(fd, 1);
fd_do_reset(fd, 1);
err = fd_gpio_init(fd);
if (err)
return err;
err = fd_gpio_init(fd);
if (err)
return err;
err = fd_pll_init(fd);
if (err)
return err;
err = fd_pll_init(fd);
if (err)
return err;
fd_gpio_defaults(fd);
fd_gpio_defaults(fd);
err = fd_reset_again(fd);
if (err)
return err;
err = fd_reset_again(fd);
if (err)
return err;
err = fd_acam_init(fd);
if (err)
return err;
err = fd_acam_init(fd);
if (err)
return err;
for (ch = 1; ch <= FD_NUM_CHANNELS; ch++)
fd_gpio_set(fd, FD_GPIO_OUTPUT_EN(ch));
for (ch = 1; ch <= FD_NUM_CHANNELS; ch++)
fd_gpio_set(fd, FD_GPIO_OUTPUT_EN(ch));
// todo: read offsets from the EEPROM. I2C should be working OK.
// todo: read offsets from the EEPROM. I2C should be working OK.
return 0;
return 0;
}
#ifdef SIMULATION
int fd_sim_init(struct wrtd_fd_dev *fd)
{
int err;
int err;
pr_debug("Initializing the Fine Delay board...\n");
pr_debug("Initializing the Fine Delay board...\n");
fd_do_reset(fd, 1);
fd_do_reset(fd, 1);
return 0;
return 0;
}
#endif
/*
* Copyright (C) 2018 CERN (www.cern.ch)
/**
* @file fd_pll.c
*
* Author: Federico Vaga <federico.vaga@cern.ch>
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* Author: Dimitris Lampridis <dimitris.lampridis@cern.ch>
*
* Copyright (c) 2018-2019 CERN (home.cern)
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
#include <errno.h>
#include <string.h>
......@@ -18,138 +21,138 @@
#include "wrtd-fd.h"
struct ad9516_reg {
unsigned short reg;
unsigned short reg;
unsigned char val;
};
static const struct ad9516_reg __9516_regs[] = {
{0x0000, 0x99}, /* Config SPI */
{0x0001, 0x00},
{0x0002, 0x10},
{0x0003, 0xC3},
{0x0004, 0x00},
/* PLL */
{0x0010, 0x7C}, /* PFD and charge pump */
{0x0011, 0x05}, /* R divider (1) */
{0x0012, 0x00}, /* R divider (2) */
{0x0013, 0x0C}, /* A counter */
{0x0014, 0x12}, /* B counter (1) */
{0x0015, 0x00}, /* B counter (2) */
{0x0016, 0x05}, /* PLL control (1) */
{0x0017, 0xb4}, /* PLL control (2) PLL_STATUS = Lock Detect */
{0x0018, 0x07}, /* PLL control (3) */
{0x0019, 0x00}, /* PLL control (4) */
{0x001A, 0x00}, /* PLL control (5) */
{0x001B, 0xE0}, /* PLL control (6) */
{0x001C, 0x02}, /* PLL control (7) */
{0x001D, 0x00}, /* PLL control (8) */
{0x001E, 0x00}, /* PLL control (9) */
{0x001F, 0x0E}, /* PLL readback */
/* Fine Delay */
{0x00A0, 0x01}, /* OUT6 Delay bypass */
{0x00A1, 0x00}, /* OUT6 Delay full-scale */
{0x00A2, 0x00}, /* OUT6 Delay fraction */
{0x00A3, 0x01}, /* OUT7 Delay bypass */
{0x00A4, 0x00}, /* OUT7 Delay full-scale */
{0x00A5, 0x00}, /* OUT7 Delay fraction */
{0x00A6, 0x01}, /* OUT8 Delay bypass */
{0x00A7, 0x00}, /* OUT8 Delay full-scale */
{0x00A8, 0x00}, /* OUT8 Delay fraction */
{0x00A9, 0x01}, /* OUT9 Delay bypass */
{0x00AA, 0x00}, /* OUT9 Delay full-scale */
{0x00AB, 0x00}, /* OUT9 Delay fraction */
/* LVPECL */
{0x00F0, 0x08}, /* OUT0 */
{0x00F1, 0x08}, /* OUT1 */
{0x00F2, 0x08}, /* OUT2 */
{0x00F3, 0x18}, /* OUT3, inverted */
{0x00F4, 0x00}, /* OUT4 */
{0x00F5, 0x08}, /* OUT5 */
/* LVDS/CMOS */
{0x0140, 0x5A}, /* OUT6 */
{0x0141, 0x5A}, /* OUT7 */
{0x0142, 0x5B}, /* OUT8 */
{0x0143, 0x42}, /* OUT9 */
/* LVPECL Channel divider */
{0x0190, 0x00}, /* Divider 0 (1) */
{0x0191, 0x80}, /* Divider 0 (2) */
{0x0192, 0x00}, /* Divider 0 (3) */
{0x0193, 0x00}, /* Divider 1 (1) */
{0x0194, 0x80}, /* Divider 1 (2) */
{0x0195, 0x00}, /* Divider 1 (3) */
{0x0196, 0xFF}, /* Divider 2 (1) */
{0x0197, 0x00}, /* Divider 2 (2) */
{0x0198, 0x00}, /* Divider 2 (3) */
/* LVDS/CMOS Channel divider */
{0x0199, 0x33}, /* Divider 3 (1) */
{0x019A, 0x00}, /* Divider 3 (2) */
{0x019B, 0x11}, /* Divider 3 (3) */
{0x019C, 0x20}, /* Divider 3 (4) */
{0x019D, 0x00}, /* Divider 3 (5) */
{0x019E, 0x00}, /* Divider 4 (1) */
{0x019F, 0x00}, /* Divider 4 (2) */
{0x01A0, 0x11}, /* Divider 4 (3) */
{0x01A1, 0x20}, /* Divider 4 (4) */
{0x01A2, 0x00}, /* Divider 4 (5) */
{0x01A3, 0x00},
/* VCO Divider and CLK Input */
{0x01E0, 0x04}, /* VCO divider VCODIV = 6 */
{0x01E1, 0x02}, /* Input Clock */
/* System */
{0x0230, 0x00}, /* Power down and sync */
{0x0231, 0x00},
/* Update All registers */
{0x0232, 0x00}, /* Update All registers */
{0x0000, 0x99}, /* Config SPI */
{0x0001, 0x00},
{0x0002, 0x10},
{0x0003, 0xC3},
{0x0004, 0x00},
/* PLL */
{0x0010, 0x7C}, /* PFD and charge pump */
{0x0011, 0x05}, /* R divider (1) */
{0x0012, 0x00}, /* R divider (2) */
{0x0013, 0x0C}, /* A counter */
{0x0014, 0x12}, /* B counter (1) */
{0x0015, 0x00}, /* B counter (2) */
{0x0016, 0x05}, /* PLL control (1) */
{0x0017, 0xb4}, /* PLL control (2) PLL_STATUS = Lock Detect */
{0x0018, 0x07}, /* PLL control (3) */
{0x0019, 0x00}, /* PLL control (4) */
{0x001A, 0x00}, /* PLL control (5) */
{0x001B, 0xE0}, /* PLL control (6) */
{0x001C, 0x02}, /* PLL control (7) */
{0x001D, 0x00}, /* PLL control (8) */
{0x001E, 0x00}, /* PLL control (9) */
{0x001F, 0x0E}, /* PLL readback */
/* Fine Delay */
{0x00A0, 0x01}, /* OUT6 Delay bypass */
{0x00A1, 0x00}, /* OUT6 Delay full-scale */
{0x00A2, 0x00}, /* OUT6 Delay fraction */
{0x00A3, 0x01}, /* OUT7 Delay bypass */
{0x00A4, 0x00}, /* OUT7 Delay full-scale */
{0x00A5, 0x00}, /* OUT7 Delay fraction */
{0x00A6, 0x01}, /* OUT8 Delay bypass */
{0x00A7, 0x00}, /* OUT8 Delay full-scale */
{0x00A8, 0x00}, /* OUT8 Delay fraction */
{0x00A9, 0x01}, /* OUT9 Delay bypass */
{0x00AA, 0x00}, /* OUT9 Delay full-scale */
{0x00AB, 0x00}, /* OUT9 Delay fraction */
/* LVPECL */
{0x00F0, 0x08}, /* OUT0 */
{0x00F1, 0x08}, /* OUT1 */
{0x00F2, 0x08}, /* OUT2 */
{0x00F3, 0x18}, /* OUT3, inverted */
{0x00F4, 0x00}, /* OUT4 */
{0x00F5, 0x08}, /* OUT5 */
/* LVDS/CMOS */
{0x0140, 0x5A}, /* OUT6 */
{0x0141, 0x5A}, /* OUT7 */
{0x0142, 0x5B}, /* OUT8 */
{0x0143, 0x42}, /* OUT9 */
/* LVPECL Channel divider */
{0x0190, 0x00}, /* Divider 0 (1) */
{0x0191, 0x80}, /* Divider 0 (2) */
{0x0192, 0x00}, /* Divider 0 (3) */
{0x0193, 0x00}, /* Divider 1 (1) */
{0x0194, 0x80}, /* Divider 1 (2) */
{0x0195, 0x00}, /* Divider 1 (3) */
{0x0196, 0xFF}, /* Divider 2 (1) */
{0x0197, 0x00}, /* Divider 2 (2) */
{0x0198, 0x00}, /* Divider 2 (3) */
/* LVDS/CMOS Channel divider */
{0x0199, 0x33}, /* Divider 3 (1) */
{0x019A, 0x00}, /* Divider 3 (2) */
{0x019B, 0x11}, /* Divider 3 (3) */
{0x019C, 0x20}, /* Divider 3 (4) */
{0x019D, 0x00}, /* Divider 3 (5) */
{0x019E, 0x00}, /* Divider 4 (1) */
{0x019F, 0x00}, /* Divider 4 (2) */
{0x01A0, 0x11}, /* Divider 4 (3) */
{0x01A1, 0x20}, /* Divider 4 (4) */
{0x01A2, 0x00}, /* Divider 4 (5) */
{0x01A3, 0x00},
/* VCO Divider and CLK Input */
{0x01E0, 0x04}, /* VCO divider VCODIV = 6 */
{0x01E1, 0x02}, /* Input Clock */
/* System */
{0x0230, 0x00}, /* Power down and sync */
{0x0231, 0x00},
/* Update All registers */
{0x0232, 0x00}, /* Update All registers */
};
int fd_spi_xfer(struct wrtd_fd_dev *fd,
int ss, int num_bits, uint32_t in, uint32_t *out)
{
uint32_t scr = 0, r;
unsigned long timeout;
uint32_t scr = 0, r;
unsigned long timeout;
scr = FD_SCR_DATA_W(in)| FD_SCR_CPOL;
if(ss == FD_CS_PLL)
scr |= FD_SCR_SEL_PLL;
else if(ss == FD_CS_GPIO)
scr |= FD_SCR_SEL_GPIO;
scr = FD_SCR_DATA_W(in)| FD_SCR_CPOL;
if(ss == FD_CS_PLL)
scr |= FD_SCR_SEL_PLL;
else if(ss == FD_CS_GPIO)
scr |= FD_SCR_SEL_GPIO;
fd_writel(fd, scr, FD_REG_SCR);
fd_writel(fd, scr | FD_SCR_START, FD_REG_SCR);
fd_writel(fd, scr, FD_REG_SCR);
fd_writel(fd, scr | FD_SCR_START, FD_REG_SCR);
timeout = trtl_get_runtime_milliseconds() + 1000;
while (!(fd_readl(fd, FD_REG_SCR) & FD_SCR_READY))
if (trtl_get_runtime_milliseconds() > timeout)
break;
if (!(fd_readl(fd, FD_REG_SCR) & FD_SCR_READY))
return -EIO;
scr = fd_readl(fd, FD_REG_SCR);
r = FD_SCR_DATA_R(scr);
if(out)
*out=r;
timeout = trtl_get_runtime_milliseconds() + 1000;
while (!(fd_readl(fd, FD_REG_SCR) & FD_SCR_READY))
if (trtl_get_runtime_milliseconds() > timeout)
break;
if (!(fd_readl(fd, FD_REG_SCR) & FD_SCR_READY))
return -EIO;
scr = fd_readl(fd, FD_REG_SCR);
r = FD_SCR_DATA_R(scr);
if(out)
*out=r;
udelay(100); /* FIXME: check */
udelay(100); /* FIXME: check */
return 0;
return 0;
}
static int fd_pll_writel(struct wrtd_fd_dev *fd, int val, int reg)
{
return fd_spi_xfer(fd, FD_CS_PLL, 24, (reg << 8) | val, NULL);
return fd_spi_xfer(fd, FD_CS_PLL, 24, (reg << 8) | val, NULL);
}
static int fd_pll_readl(struct wrtd_fd_dev *fd, int reg)
{
uint32_t ret;
int err;
uint32_t ret;
int err;
err = fd_spi_xfer(fd, FD_CS_PLL, 24, (reg << 8) | (1 << 23), &ret);
if (err < 0)
return err;
err = fd_spi_xfer(fd, FD_CS_PLL, 24, (reg << 8) | (1 << 23), &ret);
if (err < 0)
return err;
return ret & 0xff;
return ret & 0xff;
}
/* Just check the id register of the pll.
......@@ -158,54 +161,54 @@ static int fd_pll_check(struct wrtd_fd_dev *fd)
{
int id;
id = fd_pll_readl(fd, 0x003);
if (id < 0)
id = fd_pll_readl(fd, 0x003);
if (id < 0)
return -EIO;
if (id != 0xc3) {
pr_error("Error in PLL communication\n"
" (got 0x%x, expected 0xc3)\n", id);
return -EIO;
if (id != 0xc3) {
pr_error("Error in PLL communication\n"
" (got 0x%x, expected 0xc3)\n", id);
return -EIO;
}
}
return 0;
}
int fd_pll_init(struct wrtd_fd_dev *fd)
{
int i, ret;
unsigned long timeout;
const struct ad9516_reg *r;
int i, ret;
unsigned long timeout;
const struct ad9516_reg *r;
if (fd_pll_writel(fd, 0x99, 0x000) < 0)
if (fd_pll_writel(fd, 0x99, 0x000) < 0)
return -EIO;
if (fd_pll_writel(fd, 0x01, 0x232) < 0)
if (fd_pll_writel(fd, 0x01, 0x232) < 0)
return -EIO;
if (fd_pll_check(fd) < 0)
return -EIO;
/* Write the magic config */
for (i = 0, r = __9516_regs; i < ARRAY_SIZE(__9516_regs); i++, r++) {
/* Write the magic config */
for (i = 0, r = __9516_regs; i < ARRAY_SIZE(__9516_regs); i++, r++) {
if (fd_pll_writel(fd, r->val, r->reg) < 0) {
pr_error("Error in configuring PLL (step %i)\n", i);
return -EIO;
}
}
pr_error("Error in configuring PLL (step %i)\n", i);
return -EIO;
}
}
/* Enable the new config. */
if (fd_pll_writel(fd, 0x01, 0x232) < 0)
if (fd_pll_writel(fd, 0x01, 0x232) < 0)
return -EIO;
if (fd_pll_check(fd) < 0)
return -EIO;
/* Wait (at most 500ms) for it to lock */
timeout = trtl_get_runtime_milliseconds() + 500;
do {
ret = fd_pll_readl(fd, 0x1f);
if (ret < 0)
return -EIO;
if (ret & 0x1)
break;
} while (trtl_get_runtime_milliseconds() < timeout);
/* Wait (at most 500ms) for it to lock */
timeout = trtl_get_runtime_milliseconds() + 500;
do {
ret = fd_pll_readl(fd, 0x1f);
if (ret < 0)
return -EIO;
if (ret & 0x1)
break;
} while (trtl_get_runtime_milliseconds() < timeout);
if (!(ret & 0x1))
return -EIO;
......@@ -213,19 +216,19 @@ int fd_pll_init(struct wrtd_fd_dev *fd)
if (fd_pll_check(fd) < 0)
return -EIO;
/*
* Synchronize the phase of all clock outputs
* (this is critical for the accuracy!)
*/
if (fd_pll_writel(fd, 0x01, 0x230) < 0)
return -EIO;
if (fd_pll_writel(fd, 0x01, 0x232) < 0)
return -EIO;
if (fd_pll_writel(fd, 0x00, 0x230) < 0)
return -EIO;
if (fd_pll_writel(fd, 0x01, 0x232) < 0)
return -EIO;
return 0;
/*
* Synchronize the phase of all clock outputs
* (this is critical for the accuracy!)
*/
if (fd_pll_writel(fd, 0x01, 0x230) < 0)
return -EIO;
if (fd_pll_writel(fd, 0x01, 0x232) < 0)
return -EIO;
if (fd_pll_writel(fd, 0x00, 0x230) < 0)
return -EIO;
if (fd_pll_writel(fd, 0x01, 0x232) < 0)
return -EIO;
return 0;
}
/**
* @file pll_config.h
*
* Copyright (c) 2018-2019 CERN (home.cern)
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
struct ad9516_reg {
int reg;
int val;
int reg;
int val;
};
const struct ad9516_reg __9516_regs[] = {
{0x0000, 0x99}, /* Config SPI */
{0x0001, 0x00},
{0x0002, 0x10},
{0x0003, 0xC3},
{0x0004, 0x00},
/* PLL */
{0x0010, 0x7C}, /* PFD and charge pump */
{0x0011, 0x05}, /* R divider (1) */
{0x0012, 0x00}, /* R divider (2) */
{0x0013, 0x0C}, /* A counter */
{0x0014, 0x12}, /* B counter (1) */
{0x0015, 0x00}, /* B counter (2) */
{0x0016, 0x05}, /* PLL control (1) */
{0x0017, 0xb4}, /* PLL control (2) PLL_STATUS = Lock Detect */
{0x0018, 0x07}, /* PLL control (3) */
{0x0019, 0x00}, /* PLL control (4) */
{0x001A, 0x00}, /* PLL control (5) */
{0x001B, 0xE0}, /* PLL control (6) */
{0x001C, 0x02}, /* PLL control (7) */
{0x001D, 0x00}, /* PLL control (8) */
{0x001E, 0x00}, /* PLL control (9) */
{0x001F, 0x0E}, /* PLL readback */
/* Fine Delay */
{0x00A0, 0x01}, /* OUT6 Delay bypass */
{0x00A1, 0x00}, /* OUT6 Delay full-scale */
{0x00A2, 0x00}, /* OUT6 Delay fraction */
{0x00A3, 0x01}, /* OUT7 Delay bypass */
{0x00A4, 0x00}, /* OUT7 Delay full-scale */
{0x00A5, 0x00}, /* OUT7 Delay fraction */
{0x00A6, 0x01}, /* OUT8 Delay bypass */
{0x00A7, 0x00}, /* OUT8 Delay full-scale */
{0x00A8, 0x00}, /* OUT8 Delay fraction */
{0x00A9, 0x01}, /* OUT9 Delay bypass */
{0x00AA, 0x00}, /* OUT9 Delay full-scale */
{0x00AB, 0x00}, /* OUT9 Delay fraction */
/* LVPECL */
{0x00F0, 0x08}, /* OUT0 */
{0x00F1, 0x08}, /* OUT1 */
{0x00F2, 0x08}, /* OUT2 */
{0x00F3, 0x18}, /* OUT3, inverted */
{0x00F4, 0x00}, /* OUT4 */
{0x00F5, 0x08}, /* OUT5 */
/* LVDS/CMOS */
{0x0140, 0x5A}, /* OUT6 */
{0x0141, 0x5A}, /* OUT7 */
{0x0142, 0x5B}, /* OUT8 */
{0x0143, 0x42}, /* OUT9 */
/* LVPECL Channel divider */
{0x0190, 0x00}, /* Divider 0 (1) */
{0x0191, 0x80}, /* Divider 0 (2) */
{0x0192, 0x00}, /* Divider 0 (3) */
{0x0193, 0x00}, /* Divider 1 (1) */
{0x0194, 0x80}, /* Divider 1 (2) */
{0x0195, 0x00}, /* Divider 1 (3) */
{0x0196, 0xFF}, /* Divider 2 (1) */
{0x0197, 0x00}, /* Divider 2 (2) */
{0x0198, 0x00}, /* Divider 2 (3) */
/* LVDS/CMOS Channel divider */
{0x0199, 0x33}, /* Divider 3 (1) */
{0x019A, 0x00}, /* Divider 3 (2) */
{0x019B, 0x11}, /* Divider 3 (3) */
{0x019C, 0x20}, /* Divider 3 (4) */
{0x019D, 0x00}, /* Divider 3 (5) */
{0x019E, 0x00}, /* Divider 4 (1) */
{0x019F, 0x00}, /* Divider 4 (2) */
{0x01A0, 0x11}, /* Divider 4 (3) */
{0x01A1, 0x20}, /* Divider 4 (4) */
{0x01A2, 0x00}, /* Divider 4 (5) */
{0x01A3, 0x00},
/* VCO Divider and CLK Input */
{0x01E0, 0x04}, /* VCO divider VCODIV = 6 */
{0x01E1, 0x02}, /* Input Clock */
/* System */
{0x0230, 0x00}, /* Power down and sync */
{0x0231, 0x00},
/* Update All registers */
{0x0232, 0x00}, /* Update All registers */
{0x0000, 0x99}, /* Config SPI */
{0x0001, 0x00},
{0x0002, 0x10},
{0x0003, 0xC3},
{0x0004, 0x00},
/* PLL */
{0x0010, 0x7C}, /* PFD and charge pump */
{0x0011, 0x05}, /* R divider (1) */
{0x0012, 0x00}, /* R divider (2) */
{0x0013, 0x0C}, /* A counter */
{0x0014, 0x12}, /* B counter (1) */
{0x0015, 0x00}, /* B counter (2) */
{0x0016, 0x05}, /* PLL control (1) */
{0x0017, 0xb4}, /* PLL control (2) PLL_STATUS = Lock Detect */
{0x0018, 0x07}, /* PLL control (3) */
{0x0019, 0x00}, /* PLL control (4) */
{0x001A, 0x00}, /* PLL control (5) */
{0x001B, 0xE0}, /* PLL control (6) */
{0x001C, 0x02}, /* PLL control (7) */
{0x001D, 0x00}, /* PLL control (8) */
{0x001E, 0x00}, /* PLL control (9) */
{0x001F, 0x0E}, /* PLL readback */
/* Fine Delay */
{0x00A0, 0x01}, /* OUT6 Delay bypass */
{0x00A1, 0x00}, /* OUT6 Delay full-scale */
{0x00A2, 0x00}, /* OUT6 Delay fraction */
{0x00A3, 0x01}, /* OUT7 Delay bypass */
{0x00A4, 0x00}, /* OUT7 Delay full-scale */
{0x00A5, 0x00}, /* OUT7 Delay fraction */
{0x00A6, 0x01}, /* OUT8 Delay bypass */
{0x00A7, 0x00}, /* OUT8 Delay full-scale */
{0x00A8, 0x00}, /* OUT8 Delay fraction */
{0x00A9, 0x01}, /* OUT9 Delay bypass */
{0x00AA, 0x00}, /* OUT9 Delay full-scale */
{0x00AB, 0x00}, /* OUT9 Delay fraction */
/* LVPECL */
{0x00F0, 0x08}, /* OUT0 */
{0x00F1, 0x08}, /* OUT1 */
{0x00F2, 0x08}, /* OUT2 */
{0x00F3, 0x18}, /* OUT3, inverted */
{0x00F4, 0x00}, /* OUT4 */
{0x00F5, 0x08}, /* OUT5 */
/* LVDS/CMOS */
{0x0140, 0x5A}, /* OUT6 */
{0x0141, 0x5A}, /* OUT7 */
{0x0142, 0x5B}, /* OUT8 */
{0x0143, 0x42}, /* OUT9 */
/* LVPECL Channel divider */
{0x0190, 0x00}, /* Divider 0 (1) */
{0x0191, 0x80}, /* Divider 0 (2) */
{0x0192, 0x00}, /* Divider 0 (3) */
{0x0193, 0x00}, /* Divider 1 (1) */
{0x0194, 0x80}, /* Divider 1 (2) */
{0x0195, 0x00}, /* Divider 1 (3) */
{0x0196, 0xFF}, /* Divider 2 (1) */
{0x0197, 0x00}, /* Divider 2 (2) */
{0x0198, 0x00}, /* Divider 2 (3) */
/* LVDS/CMOS Channel divider */
{0x0199, 0x33}, /* Divider 3 (1) */
{0x019A, 0x00}, /* Divider 3 (2) */
{0x019B, 0x11}, /* Divider 3 (3) */
{0x019C, 0x20}, /* Divider 3 (4) */
{0x019D, 0x00}, /* Divider 3 (5) */
{0x019E, 0x00}, /* Divider 4 (1) */
{0x019F, 0x00}, /* Divider 4 (2) */
{0x01A0, 0x11}, /* Divider 4 (3) */
{0x01A1, 0x20}, /* Divider 4 (4) */
{0x01A2, 0x00}, /* Divider 4 (5) */
{0x01A3, 0x00},
/* VCO Divider and CLK Input */
{0x01E0, 0x04}, /* VCO divider VCODIV = 6 */
{0x01E1, 0x02}, /* Input Clock */
/* System */
{0x0230, 0x00}, /* Power down and sync */
{0x0231, 0x00},
/* Update All registers */
{0x0232, 0x00}, /* Update All registers */
};
/*
* Copyright (C) 2013-2018 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@cern.ch>
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
/**
* @file wrtd-fd.c
*
* Copyright (c) 2018-2019 CERN (home.cern)
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
......@@ -26,17 +26,17 @@ static inline int fd_wr_present(struct wrtd_fd_dev *fd)
static inline int fd_wr_link_up(struct wrtd_fd_dev *fd)
{
return fd_readl(fd, FD_REG_TCR) & FD_TCR_WR_LINK;
return fd_readl(fd, FD_REG_TCR) & FD_TCR_WR_LINK;
}
static inline int fd_wr_time_locked(struct wrtd_fd_dev *fd)
{
return fd_readl(fd, FD_REG_TCR) & FD_TCR_WR_LOCKED;
return fd_readl(fd, FD_REG_TCR) & FD_TCR_WR_LOCKED;
}
static inline int fd_wr_time_ready(struct wrtd_fd_dev *fd)
{
return 1;
return 1;
}
static inline int fd_wr_sync_timeout(void)
......@@ -54,9 +54,9 @@ static void fd_wr_enable_lock(struct wrtd_fd_dev *fd, int enable)
* Writes to FD output registers for output (out)
*/
static inline void fd_ch_writel(struct wrtd_fd_channel *out, uint32_t value,
uint32_t reg)
uint32_t reg)
{
dp_writel(value, reg + out->channel_addr);
dp_writel(value, reg + out->channel_addr);
}
......@@ -65,7 +65,7 @@ static inline void fd_ch_writel(struct wrtd_fd_channel *out, uint32_t value,
*/
static inline uint32_t fd_ch_readl (struct wrtd_fd_channel *out, uint32_t reg)
{
return dp_readl(reg + out->channel_addr);
return dp_readl(reg + out->channel_addr);
}
......@@ -74,9 +74,9 @@ static inline uint32_t fd_ch_readl (struct wrtd_fd_channel *out, uint32_t reg)
*/
static void pulse_queue_init(struct lrt_pulse_queue *p)
{
p->head = 0;
p->tail = 0;
p->count = 0;
p->head = 0;
p->tail = 0;
p->count = 0;
}
......@@ -86,19 +86,19 @@ static void pulse_queue_init(struct lrt_pulse_queue *p)
*/
static struct wrtd_event *pulse_queue_push(struct lrt_pulse_queue *p)
{
struct wrtd_event *ev;
struct wrtd_event *ev;
if (p->count == FD_MAX_QUEUE_PULSES)
return NULL;
if (p->count == FD_MAX_QUEUE_PULSES)
return NULL;
ev = &p->events[p->head];
p->count++;
p->head++;
ev = &p->events[p->head];
p->count++;
p->head++;
if (p->head == FD_MAX_QUEUE_PULSES)
p->head = 0;
if (p->head == FD_MAX_QUEUE_PULSES)
p->head = 0;
return ev;
return ev;
}
......@@ -107,7 +107,7 @@ static struct wrtd_event *pulse_queue_push(struct lrt_pulse_queue *p)
*/
static inline int pulse_queue_empty(struct lrt_pulse_queue *p)
{
return (p->count == 0);
return (p->count == 0);
}
......@@ -116,9 +116,9 @@ static inline int pulse_queue_empty(struct lrt_pulse_queue *p)
*/
static struct wrtd_event *pulse_queue_front(struct lrt_pulse_queue *p)
{
if (!p->count)
return NULL;
return &p->events[p->tail];
if (!p->count)
return NULL;
return &p->events[p->tail];
}
......@@ -127,11 +127,11 @@ static struct wrtd_event *pulse_queue_front(struct lrt_pulse_queue *p)
*/
static void pulse_queue_pop(struct lrt_pulse_queue *p)
{
p->tail++;
p->tail++;
if(p->tail == FD_MAX_QUEUE_PULSES)
p->tail = 0;
p->count--;
if(p->tail == FD_MAX_QUEUE_PULSES)
p->tail = 0;
p->count--;
}
/**
......@@ -142,47 +142,47 @@ static int check_output_timeout (struct wrtd_fd_channel *out)
{
uint32_t now_sec;
uint32_t now_ns;
int delta;
int delta;
/*
* Read the current WR time, order is important: first seconds,
* then cycles (cycles get latched on reading secs register.
*/
now_sec = lr_readl(MT_CPU_LR_REG_TAI_SEC);
now_ns = lr_readl(MT_CPU_LR_REG_TAI_CYCLES) * 8;
/*
* Read the current WR time, order is important: first seconds,
* then cycles (cycles get latched on reading secs register.
*/
now_sec = lr_readl(MT_CPU_LR_REG_TAI_SEC);
now_ns = lr_readl(MT_CPU_LR_REG_TAI_CYCLES) * 8;
if(out->last_programmed_sec > now_sec + OUT_TIMEOUT) {
pr_error("Enqueued event very far in the future. Dropping.");
if(out->last_programmed_sec > now_sec + OUT_TIMEOUT) {
pr_error("Enqueued event very far in the future. Dropping.");
return 0;
}
}
/* Current time exceeds FD setpoint? */
/* Current time exceeds FD setpoint? */
delta = now_sec - out->last_programmed_sec;
if (delta != 0)
return delta > 0;
delta = now_ns - out->last_programmed_ns;
return (delta > 0);
delta = now_ns - out->last_programmed_ns;
return (delta > 0);
}
/**
* Drop the given enqueued trigger
*/
static void drop_trigger(struct wrtd_fd_channel *out,
struct wrtd_event *ev,
struct lrt_pulse_queue *q, unsigned reason)
struct wrtd_event *ev,
struct lrt_pulse_queue *q, unsigned reason)
{
out->idle = 1;
out->idle = 1;
if (pulse_queue_empty(q))
return;
if (pulse_queue_empty(q))
return;
/* Drop the pulse */
pulse_queue_pop(q);
/* Drop the pulse */
pulse_queue_pop(q);
/* Disarm the FD output */
fd_ch_writel(out, FD_DCR_MODE, FD_REG_DCR);
/* Disarm the FD output */
fd_ch_writel(out, FD_DCR_MODE, FD_REG_DCR);
wrtd_log(WRTD_LOG_MSG_EV_DISCARDED, reason, NULL, ev, NULL);
}
......@@ -195,118 +195,118 @@ static void drop_trigger(struct wrtd_fd_channel *out,
static void fd_output (struct wrtd_fd_dev *fd, unsigned channel)
{
struct wrtd_fd_channel *out = &fd->channels[channel];
struct lrt_pulse_queue *q = &out->queue;
struct wrtd_event *ev = pulse_queue_front(q);
uint32_t dcr = fd_ch_readl(out, FD_REG_DCR);
struct wrtd_tstamp *ts;
/* Check if the output has triggered */
if (!out->idle) {
if (!wr_is_timing_ok()) {
struct lrt_pulse_queue *q = &out->queue;
struct wrtd_event *ev = pulse_queue_front(q);
uint32_t dcr = fd_ch_readl(out, FD_REG_DCR);
struct wrtd_tstamp *ts;
/* Check if the output has triggered */
if (!out->idle) {
if (!wr_is_timing_ok()) {
/* Timing has been lost. */
drop_trigger(out, ev, q, WRTD_LOG_DISCARD_NO_SYNC);
drop_trigger(out, ev, q, WRTD_LOG_DISCARD_NO_SYNC);
return;
}
if (!(dcr & FD_DCR_PG_TRIG)) {
/* Armed but still waiting for trigger */
if (check_output_timeout (out)) {
}
if (!(dcr & FD_DCR_PG_TRIG)) {
/* Armed but still waiting for trigger */
if (check_output_timeout (out)) {
/* Will never trigger. Missed. */
drop_trigger(out, ev, q,
drop_trigger(out, ev, q,
WRTD_LOG_DISCARD_TIMEOUT);
}
} else {
} else {
/* Has been triggered. */
wrtd_log(WRTD_LOG_MSG_EV_CONSUMED,
WRTD_LOG_CONSUMED_DONE,
NULL, ev, NULL);
pulse_queue_pop(q);
out->idle = 1;
}
return;
}
pulse_queue_pop(q);
out->idle = 1;
}
return;
}
/* Output is idle: check if there's something in the queue to execute */
if (pulse_queue_empty(q))
return;
/* Output is idle: check if there's something in the queue to execute */
if (pulse_queue_empty(q))
return;
ev = pulse_queue_front(q);
ts = &ev->ts;
ev = pulse_queue_front(q);
ts = &ev->ts;
if (!wr_is_timing_ok()) {
if (!wr_is_timing_ok()) {
drop_trigger(out, ev, q, WRTD_LOG_DISCARD_NO_SYNC);
return;
}
/* Program the output start time */
fd_ch_writel(out, ts->seconds, FD_REG_U_STARTL);
fd_ch_writel(out, ts->ns / 8, FD_REG_C_START);
fd_ch_writel(out, ((ts->ns & 7) << 9) | (ts->frac >> (32 - 9)),
/* Program the output start time */
fd_ch_writel(out, ts->seconds, FD_REG_U_STARTL);
fd_ch_writel(out, ts->ns / 8, FD_REG_C_START);
fd_ch_writel(out, ((ts->ns & 7) << 9) | (ts->frac >> (32 - 9)),
FD_REG_F_START);
/* Adjust pulse width and program the output end time */
/* Adjust pulse width and program the output end time */
ts_add2_ns(ts, out->width_ns);
fd_ch_writel(out, ts->seconds, FD_REG_U_ENDL);
fd_ch_writel(out, ts->ns / 8, FD_REG_C_END);
fd_ch_writel(out, ((ts->ns & 7) << 9) | (ts->frac >> (32 - 9)),
fd_ch_writel(out, ts->seconds, FD_REG_U_ENDL);
fd_ch_writel(out, ts->ns / 8, FD_REG_C_END);
fd_ch_writel(out, ((ts->ns & 7) << 9) | (ts->frac >> (32 - 9)),
FD_REG_F_END);
fd_ch_writel(out, 0, FD_REG_RCR);
fd_ch_writel(out, FD_DCR_MODE, FD_REG_DCR);
fd_ch_writel(out, FD_DCR_MODE | FD_DCR_UPDATE, FD_REG_DCR);
fd_ch_writel(out, FD_DCR_MODE | FD_DCR_PG_ARM | FD_DCR_ENABLE,
FD_REG_DCR);
fd_ch_writel(out, 0, FD_REG_RCR);
fd_ch_writel(out, FD_DCR_MODE, FD_REG_DCR);
fd_ch_writel(out, FD_DCR_MODE | FD_DCR_UPDATE, FD_REG_DCR);
fd_ch_writel(out, FD_DCR_MODE | FD_DCR_PG_ARM | FD_DCR_ENABLE,
FD_REG_DCR);
wrtd_log(WRTD_LOG_MSG_EV_CONSUMED, WRTD_LOG_CONSUMED_START,
NULL, ev, NULL);
ts_add2_ns (ts, 8000);
ts_add2_ns (ts, 8000);
/*
* Store the last programmed timestamp (+ some margin) and mark
* the output as busy
*/
out->last_programmed_sec = ts->seconds;
out->last_programmed_ns = ts->ns;
out->idle = 0;
/*
* Store the last programmed timestamp (+ some margin) and mark
* the output as busy
*/
out->last_programmed_sec = ts->seconds;
out->last_programmed_ns = ts->ns;
out->idle = 0;
}
static int fd_local_output(struct wrtd_fd_dev *fd,
struct wrtd_event *ev, unsigned ch)
{
struct wrtd_fd_channel *out = &fd->channels[ch];
struct wrtd_event *pq_ev;
struct wrtd_fd_channel *out = &fd->channels[ch];
struct wrtd_event *pq_ev;
pq_ev = pulse_queue_push(&out->queue);
if (!pq_ev) {
return -EOVERFLOW;
}
pq_ev = pulse_queue_push(&out->queue);
if (!pq_ev) {
return -EOVERFLOW;
}
*pq_ev = *ev;
*pq_ev = *ev;
return 0;
}
static void fd_outputs(struct wrtd_fd_dev *fd)
{
int i;
int i;
for (i = 0;i < FD_NUM_CHANNELS; i++)
fd_output(fd, i);
for (i = 0;i < FD_NUM_CHANNELS; i++)
fd_output(fd, i);
}
static void wrtd_fd_data_init(struct wrtd_fd_dev *fd)
{
unsigned i;
/* Channels */
for (i = 0; i < FD_NUM_CHANNELS; i++) {
memset(&fd->channels[i], 0, sizeof(struct wrtd_fd_channel));
fd->channels[i].channel_addr = fd->io_addr + 0x100 + i * 0x100;
/* Channels */
for (i = 0; i < FD_NUM_CHANNELS; i++) {
memset(&fd->channels[i], 0, sizeof(struct wrtd_fd_channel));
fd->channels[i].channel_addr = fd->io_addr + 0x100 + i * 0x100;
pulse_queue_init(&fd->channels[i].queue);
fd->channels[i].idle = 1;
fd->channels[i].width_ns = 10000; // 10us
fd->channels[i].last_programmed_sec = 0;
fd->channels[i].last_programmed_ns = 0;
}
fd->channels[i].idle = 1;
fd->channels[i].width_ns = 10000; // 10us
fd->channels[i].last_programmed_sec = 0;
fd->channels[i].last_programmed_ns = 0;
}
}
/**
* @file wrtd-fd.h
*
* Copyright (c) 2018-2019 CERN (home.cern)
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
#ifndef __FINE_DELAY_WRAPPER_H
#define __FINE_DELAY_WRAPPER_H
#include "wrtd-common.h"
/* Channels are called 1..4 in all docs. Internally it's 0..3 */
#define FD_CH_1 0
#define FD_CH_LAST 3
#define FD_CH_1 0
#define FD_CH_LAST 3
#define FD_NUM_CHANNELS 4
#define FD_CH_EXT(i) ((i) + 1)
#define FD_CH_EXT(i) ((i) + 1)
#define FD_NUM_TAPS 1024 /* This is an hardware feature of SY89295U */
#define FD_CAL_STEPS 1024 /* This is a parameter: must be power of 2 */
#define FD_NUM_TAPS 1024 /* This is an hardware feature of SY89295U */
#define FD_CAL_STEPS 1024 /* This is a parameter: must be power of 2 */
#define FD_MAGIC_FPGA 0xf19ede1a /* FD_REG_IDR content */
......@@ -20,17 +28,17 @@
/* ACAM TDC operation modes */
enum fd_acam_modes {
ACAM_RMODE,
ACAM_IMODE,
ACAM_GMODE
ACAM_RMODE,
ACAM_IMODE,
ACAM_GMODE
};
#define FD_GPIO_TERM_EN 0x0001 /* Input terminator enable */
#define FD_GPIO_OUTPUT_EN(x) \
(1 << (6-(x))) /* Output driver enable */
#define FD_GPIO_OUTPUT_MASK 0x003c /* Output driver enable */
#define FD_GPIO_TRIG_INTERNAL 0x0040 /* TDC trig (1=in, 1=fpga) */
#define FD_GPIO_CAL_DISABLE 0x0080 /* 0 enables calibration */
#define FD_GPIO_TERM_EN 0x0001 /* Input terminator enable */
#define FD_GPIO_OUTPUT_EN(x) \
(1 << (6-(x))) /* Output driver enable */
#define FD_GPIO_OUTPUT_MASK 0x003c /* Output driver enable */
#define FD_GPIO_TRIG_INTERNAL 0x0040 /* TDC trig (1=in, 1=fpga) */
#define FD_GPIO_CAL_DISABLE 0x0080 /* 0 enables calibration */
/*
......@@ -42,23 +50,23 @@ enum fd_acam_modes {
#define ACAM_MASK ((1<<29) - 1) /* 28 bits */
/* SPI Bus chip selects */
#define FD_CS_DAC 0 /* DAC for VCXO */
#define FD_CS_PLL 1 /* AD9516 PLL */
#define FD_CS_GPIO 2 /* MCP23S17 GPIO */
#define FD_CS_DAC 0 /* DAC for VCXO */
#define FD_CS_PLL 1 /* AD9516 PLL */
#define FD_CS_GPIO 2 /* MCP23S17 GPIO */
/* MCP23S17 register addresses (only ones which are used by the lib) */
#define FD_MCP_IODIR 0x00
#define FD_MCP_IPOL 0x01
#define FD_MCP_IOCON 0x0a
#define FD_MCP_GPIO 0x12
#define FD_MCP_OLAT 0x14
#define FD_MCP_IODIR 0x00
#define FD_MCP_IPOL 0x01
#define FD_MCP_IOCON 0x0a
#define FD_MCP_GPIO 0x12
#define FD_MCP_OLAT 0x14
#define FD_MAX_QUEUE_PULSES 4
/* Pulse FIFO for a single Fine Delay output */
struct lrt_pulse_queue {
struct wrtd_event events[FD_MAX_QUEUE_PULSES];
int head, tail, count;
struct wrtd_event events[FD_MAX_QUEUE_PULSES];
int head, tail, count;
};
struct wrtd_fd_channel {
......@@ -109,8 +117,8 @@ int fd_sim_init(struct wrtd_fd_dev *fd);
#define fd_gpio_set(fd, pin) fd_gpio_set_clr(fd, (pin), 1)
#define fd_gpio_clr(fd, pin) fd_gpio_set_clr(fd, (pin), 0)
#define FD_GPIO_IN 0
#define FD_GPIO_OUT 1
#define FD_GPIO_IN 0
#define FD_GPIO_OUT 1
static inline void fd_writel(struct wrtd_fd_dev *fd, uint32_t val, uint32_t reg)
{
......@@ -119,24 +127,24 @@ static inline void fd_writel(struct wrtd_fd_dev *fd, uint32_t val, uint32_t reg)
static inline uint32_t fd_readl(struct wrtd_fd_dev *fd, uint32_t reg)
{
return dp_readl(fd->io_addr + reg);
return dp_readl(fd->io_addr + reg);
}
static inline uint32_t fd_drv_ch_readl(struct wrtd_fd_dev *fd, int ch,
unsigned long reg)
{
return fd_readl(fd, 0x100 + ch * 0x100 + reg);
return fd_readl(fd, 0x100 + ch * 0x100 + reg);
}
static inline void fd_drv_ch_writel(struct wrtd_fd_dev *fd, int ch,
uint32_t v, unsigned long reg)
{
fd_writel(fd, v, 0x100 + ch * 0x100 + reg);
fd_writel(fd, v, 0x100 + ch * 0x100 + reg);
}
static inline uint64_t div_u64(uint64_t dividend, uint32_t divisor)
{
return dividend / divisor;
return dividend / divisor;
}
......
/*
* Copyright (C) 2013-2018 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@cern.ch>
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
/**
* @file wrtd-rt-fd.c
*
* Copyright (c) 2018-2019 CERN (home.cern)
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
......@@ -74,14 +74,14 @@ static int wrtd_local_output(struct wrtd_event *ev, unsigned ch)
*/
static int wrtd_user_init(void)
{
if (!fd_wr_present(&fd0)) {
pr_error("WhiteRabbit not found\n\r");
return -EINVAL;
}
if (!fd_wr_present(&fd0)) {
pr_error("WhiteRabbit not found\n\r");
return -EINVAL;
}
wr_enable_lock(0);
wr_enable_lock(0);
/* Channels */
/* Channels */
wrtd_fd_data_init(&fd0);
#ifdef SIMULATION
......@@ -90,9 +90,9 @@ static int wrtd_user_init(void)
fd_init(&fd0);
#endif
pr_debug("rt-output firmware initialized.\n\r");
pr_debug("rt-output firmware initialized.\n\r");
return 0;
return 0;
}
static void wrtd_io(void)
......
#
# Automatically generated file; DO NOT EDIT.
# fmc-svec-carrier fw-01 demo configuration
#
#
......
/*
* tdc_registers.h
/**
* @file tdc_regs.h
*
* Copyright (c) 2012 CERN (http://www.cern.ch)
* Author: Samuel Iglesias Gonsalvez <siglesias@igalia.com>
* Copyright (c) 2012-2019 CERN (home.cern)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; version 2 of the License.
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
#ifndef __TDC_REGISTERS_H
......@@ -20,116 +17,115 @@
#define TDC_REG_ACAM_CONFIG(index) (0x0000 + (index * 4))
/* TDC core registers */
#define TDC_REG_START_UTC 0x0080
#define TDC_REG_INPUT_ENABLE 0x0084
#define TDC_REG_IRQ_THRESHOLD 0x0090
#define TDC_REG_IRQ_TIMEOUT 0x0094
#define TDC_REG_DAC_TUNE 0x0098
#define TDC_REG_CURRENT_UTC 0x00a0
#define TDC_REG_BUFFER_PTR 0x00a8
#define TDC_REG_CTRL 0x00fc
#define TDC_REG_WR_CTRL 0x00b4
#define TDC_REG_WR_STAT 0x00b0
#define TDC_WR_CTRL_ENABLE BIT(0)
#define TDC_WR_STAT_ENABLED BIT(6)
#define TDC_WR_STAT_LINK BIT(2)
#define TDC_WR_STAT_TIME_VALID BIT(8)
#define TDC_WR_STAT_AUX_LOCKED BIT(4)
#define TDC_REG_START_UTC 0x0080
#define TDC_REG_INPUT_ENABLE 0x0084
#define TDC_REG_IRQ_THRESHOLD 0x0090
#define TDC_REG_IRQ_TIMEOUT 0x0094
#define TDC_REG_DAC_TUNE 0x0098
#define TDC_REG_CURRENT_UTC 0x00a0
#define TDC_REG_BUFFER_PTR 0x00a8
#define TDC_REG_CTRL 0x00fc
#define TDC_REG_WR_CTRL 0x00b4
#define TDC_REG_WR_STAT 0x00b0
#define TDC_WR_CTRL_ENABLE BIT(0)
#define TDC_WR_STAT_ENABLED BIT(6)
#define TDC_WR_STAT_LINK BIT(2)
#define TDC_WR_STAT_TIME_VALID BIT(8)
#define TDC_WR_STAT_AUX_LOCKED BIT(4)
/* TDC_REG_CTRL bits */
#define TDC_CTRL_EN_ACQ BIT(0)
#define TDC_CTRL_DIS_ACQ BIT(1)
#define TDC_CTRL_LOAD_ACAM_CFG BIT(2)
#define TDC_CTRL_READ_ACAM_CFG BIT(3)
#define TDC_CTRL_READ_ACAM_STAT BIT(4)
#define TDC_CTRL_READ_ACAM_IFIFO1 BIT(5)
#define TDC_CTRL_READ_ACAM_IFIFO2 BIT(6)
#define TDC_CTRL_READ_ACAM_START01_R BIT(7)
#define TDC_CTRL_RESET_ACAM BIT(8)
#define TDC_CTRL_LOAD_UTC BIT(9)
#define TDC_CTRL_CLEAR_DACAPO_FLAG BIT(10)
#define TDC_CTRL_CONFIG_DAC BIT(11)
#define TDC_CTRL_EN_ACQ BIT(0)
#define TDC_CTRL_DIS_ACQ BIT(1)
#define TDC_CTRL_LOAD_ACAM_CFG BIT(2)
#define TDC_CTRL_READ_ACAM_CFG BIT(3)
#define TDC_CTRL_READ_ACAM_STAT BIT(4)
#define TDC_CTRL_READ_ACAM_IFIFO1 BIT(5)
#define TDC_CTRL_READ_ACAM_IFIFO2 BIT(6)
#define TDC_CTRL_READ_ACAM_START01_R BIT(7)
#define TDC_CTRL_RESET_ACAM BIT(8)
#define TDC_CTRL_LOAD_UTC BIT(9)
#define TDC_CTRL_CLEAR_DACAPO_FLAG BIT(10)
#define TDC_CTRL_CONFIG_DAC BIT(11)
/* TDC_REG_INPUT_ENABLE bits */
#define TDC_INPUT_ENABLE_FLAG BIT(7)
#define TDC_INPUT_ENABLE_CH1 BIT(16)
#define TDC_INPUT_ENABLE_CH2 BIT(17)
#define TDC_INPUT_ENABLE_CH3 BIT(18)
#define TDC_INPUT_ENABLE_CH4 BIT(19)
#define TDC_INPUT_ENABLE_CH5 BIT(20)
#define TDC_INPUT_ENABLE_FLAG BIT(7)
#define TDC_INPUT_ENABLE_CH1 BIT(16)
#define TDC_INPUT_ENABLE_CH2 BIT(17)
#define TDC_INPUT_ENABLE_CH3 BIT(18)
#define TDC_INPUT_ENABLE_CH4 BIT(19)
#define TDC_INPUT_ENABLE_CH5 BIT(20)
#define TDC_INPUT_ENABLE_CH_ALL (TDC_INPUT_ENABLE_CH1 | \
TDC_INPUT_ENABLE_CH2 | \
TDC_INPUT_ENABLE_CH3 | \
TDC_INPUT_ENABLE_CH4 | \
TDC_INPUT_ENABLE_CH5)
TDC_INPUT_ENABLE_CH2 | \
TDC_INPUT_ENABLE_CH3 | \
TDC_INPUT_ENABLE_CH4 | \
TDC_INPUT_ENABLE_CH5)
/* IRQ controler registers */
#define TDC_REG_EIC_IDR 0x0
#define TDC_REG_EIC_IER 0x4
#define TDC_REG_EIC_IMR 0x8
#define TDC_REG_EIC_ISR 0xc
#define TDC_REG_EIC_IDR 0x0
#define TDC_REG_EIC_IER 0x4
#define TDC_REG_EIC_IMR 0x8
#define TDC_REG_EIC_ISR 0xc
/* IRQ status/enable bits */
#define TDC_IRQ_TDC_TSTAMP BIT(0)
#define TDC_IRQ_TDC_TIME BIT(1)
#define TDC_IRQ_TDC_TSTAMP BIT(0)
#define TDC_IRQ_TDC_TIME BIT(1)
#define TDC_EVENT_BUFFER_SIZE 256
#define TDC_EVENT_CHANNEL_MASK 0xF
#define TDC_EVENT_SLOPE_MASK 0xF0
#define TDC_EVENT_FIFO_LF_MASK 0xF00
#define TDC_EVENT_FIFO_EF_MASK 0xF000
#define TDC_EVENT_DACAPO_FLAG BIT(0)
#define TDC_EVENT_BUFFER_SIZE 256
#define TDC_EVENT_CHANNEL_MASK 0xF
#define TDC_EVENT_SLOPE_MASK 0xF0
#define TDC_EVENT_FIFO_LF_MASK 0xF00
#define TDC_EVENT_FIFO_EF_MASK 0xF000
#define TDC_EVENT_DACAPO_FLAG BIT(0)
/* FIFO registers */
#define TDC_FIFO_OFFSET 0x100
#define TDC_FIFO_LAST 0x0
#define TDC_FIFO_LAST_N 4
#define TDC_FIFO_LAST_CSR 0x10
#define TDC_FIFO_LAST_CSR_VALID BIT(0)
#define TDC_FIFO_LAST_CSR_RST_SEQ BIT(1)
#define TDC_FIFO_OUT 0x14
#define TDC_FIFO_OUT_N 4
#define TDC_FIFO_CSR 0x24
#define TDC_FIFO_CSR_EMPTY BIT(17)
#define TDC_FIFO_CSR_FULL BIT(16)
#define TDC_FIFO_OFFSET 0x100
#define TDC_FIFO_LAST 0x0
#define TDC_FIFO_LAST_N 4
#define TDC_FIFO_LAST_CSR 0x10
#define TDC_FIFO_LAST_CSR_VALID BIT(0)
#define TDC_FIFO_LAST_CSR_RST_SEQ BIT(1)
#define TDC_FIFO_OUT 0x14
#define TDC_FIFO_OUT_N 4
#define TDC_FIFO_CSR 0x24
#define TDC_FIFO_CSR_EMPTY BIT(17)
#define TDC_FIFO_CSR_FULL BIT(16)
#define TDC_FIFO_CSR_USEDW
/* Carrier CSRs */
#define TDC_REG_CARRIER_CTL0 0x0 /* a.k.a. Carrier revision/PCB id reg */
#define TDC_REG_CARRIER_STATUS 0x4
#define TDC_REG_CARRIER_CTL1 0x8
#define TDC_REG_CARRIER_RST 0xc
#define TDC_REG_CARRIER_CTL0 0x0 /* a.k.a. Carrier revision/PCB id reg */
#define TDC_REG_CARRIER_STATUS 0x4
#define TDC_REG_CARRIER_CTL1 0x8
#define TDC_REG_CARRIER_RST 0xc
#define TDC_CARRIER_CTL0_PLL_STAT_FMC0 BIT(5)
#define TDC_CARRIER_CTL0_PLL_STAT_FMC1 BIT(6)
#define TDC_CARRIER_CTL0_PLL_STAT_FMC0 BIT(5)
#define TDC_CARRIER_CTL0_PLL_STAT_FMC1 BIT(6)
#define TDC_CARRIER_CTL1_RSTN_FMC0 BIT(3)
#define TDC_CARRIER_CTL1_RSTN_FMC1 BIT(4)
#define TDC_CARRIER_CTL1_RSTN_FMC0 BIT(3)
#define TDC_CARRIER_CTL1_RSTN_FMC1 BIT(4)
/* Gennum DMA registers (not defined in the SPEC driver headers) */
#define TDC_REG_DMA_CTRL 0x0
#define TDC_REG_DMA_STAT 0x4
#define TDC_REG_DMA_C_START 0x8
#define TDC_REG_DMA_H_START_L 0x0c
#define TDC_REG_DMA_H_START_H 0x10
#define TDC_REG_DMA_NEXT_L 0x18
#define TDC_REG_DMA_NEXT_H 0x1c
#define TDC_REG_DMA_LEN 0x14
#define TDC_REG_DMA_ATTRIB 0x20
#define TDC_REG_DMA_CTRL 0x0
#define TDC_REG_DMA_STAT 0x4
#define TDC_REG_DMA_C_START 0x8
#define TDC_REG_DMA_H_START_L 0x0c
#define TDC_REG_DMA_H_START_H 0x10
#define TDC_REG_DMA_NEXT_L 0x18
#define TDC_REG_DMA_NEXT_H 0x1c
#define TDC_REG_DMA_LEN 0x14
#define TDC_REG_DMA_ATTRIB 0x20
/* TDC_REG_DMA_STAT bits */
#define TDC_DMA_STAT_MASK 0x7
#define TDC_DMA_STAT_DONE 0x1
#define TDC_DMA_STAT_ERROR 0x3
#define TDC_DMA_STAT_MASK 0x7
#define TDC_DMA_STAT_DONE 0x1
#define TDC_DMA_STAT_ERROR 0x3
/* TDC core submodule offsets (wrs to the TDC control registers block) */
#define TDC_MEZZ_ONEWIRE_OFFSET (-0x1000)
#define TDC_MEZZ_EIC_OFFSET (0x1000)
#define TDC_MEZZ_I2C_OFFSET (0x2000)
#define TDC_MEZZ_MEM_OFFSET (0x3000)
#define TDC_MEZZ_ONEWIRE_OFFSET -0x1000
#define TDC_MEZZ_EIC_OFFSET 0x1000
#define TDC_MEZZ_I2C_OFFSET 0x2000
#define TDC_MEZZ_MEM_OFFSET 0x3000
#endif /* __TDC_REGISTERS_H */
/*
* Copyright (C) 2013-2018 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@cern.ch>
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
/**
* @file wrtd-rt-tdc.c
*
* Copyright (c) 2018-2019 CERN (home.cern)
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
#include "mockturtle-rt.h"
#include <mockturtle-framework.h>
#include "wrtd-common.h"
......@@ -75,11 +74,11 @@ static int wrtd_user_init(void)
{
tdc_init(&tdc0);
wr_enable_lock(0);
wr_enable_lock(0);
pr_debug("rt-tdc firmware initialized.\n\r");
pr_debug("rt-tdc firmware initialized.\n\r");
return 0;
return 0;
}
static void wrtd_io(void)
......
/*
* Copyright (C) 2018 CERN (www.cern.ch)
* Author: Dimitris Lampridis <dimitris.lampridis@cern.ch>
/**
* @file wrtd-tdc.c
*
* Copyright (c) 2018-2019 CERN (home.cern)
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
......@@ -22,7 +23,6 @@
#define DEFAULT_DEAD_TIME (80000/16)
struct wrtd_tdc_dev {
uint32_t io_addr;
/* FIXME: base channel (to create the event id). */
......@@ -31,202 +31,202 @@ struct wrtd_tdc_dev {
static inline void tdc_writel(const struct wrtd_tdc_dev *dev,
uint32_t value, uint32_t reg)
{
dp_writel(value, dev->io_addr + reg);
dp_writel(value, dev->io_addr + reg);
}
static inline uint32_t tdc_readl(const struct wrtd_tdc_dev *dev, uint32_t reg)
{
return dp_readl(dev->io_addr + reg);
return dp_readl(dev->io_addr + reg);
}
static const struct {
uint32_t reg;
uint32_t value;
uint32_t reg;
uint32_t value;
} acam_config[NB_ACAM_REGS] = {
{
0, AR0_ROsc | AR0_HQSel | AR0_TRiseEn(0) |
AR0_TRiseEn(1) | AR0_TRiseEn(2) |
AR0_TRiseEn(3) | AR0_TRiseEn(4) |
AR0_TRiseEn(5) |
AR0_TFallEn(1) | AR0_TFallEn(2) |
AR0_TFallEn(3) | AR0_TFallEn(4) | AR0_TFallEn(5)}, {
1, 0}, {
2, AR2_IMode | AR2_Disable(6) | AR2_Disable(7) | AR2_Disable(8)}, {
3, 0}, {
4, AR4_StartTimer(15) | AR4_EFlagHiZN}, {
5, AR5_StartOff1(2000)}, {
6, AR6_Fill(0xfc)}, {
7, AR7_RefClkDiv(7) | AR7_HSDiv(234) | AR7_NegPhase | AR7_ResAdj}, {
11, AR11_HFifoErrU(0) | AR11_HFifoErrU(1) |
AR11_HFifoErrU(2) | AR11_HFifoErrU(3) |
AR11_HFifoErrU(4) | AR11_HFifoErrU(5) |
AR11_HFifoErrU(6) | AR11_HFifoErrU(7)}, {
12, AR12_StartNU | AR12_HFifoE}, {
14, 0}
{
0, AR0_ROsc | AR0_HQSel | AR0_TRiseEn(0) |
AR0_TRiseEn(1) | AR0_TRiseEn(2) |
AR0_TRiseEn(3) | AR0_TRiseEn(4) |
AR0_TRiseEn(5) |
AR0_TFallEn(1) | AR0_TFallEn(2) |
AR0_TFallEn(3) | AR0_TFallEn(4) | AR0_TFallEn(5)}, {
1, 0}, {
2, AR2_IMode | AR2_Disable(6) | AR2_Disable(7) | AR2_Disable(8)}, {
3, 0}, {
4, AR4_StartTimer(15) | AR4_EFlagHiZN}, {
5, AR5_StartOff1(2000)}, {
6, AR6_Fill(0xfc)}, {
7, AR7_RefClkDiv(7) | AR7_HSDiv(234) | AR7_NegPhase | AR7_ResAdj}, {
11, AR11_HFifoErrU(0) | AR11_HFifoErrU(1) |
AR11_HFifoErrU(2) | AR11_HFifoErrU(3) |
AR11_HFifoErrU(4) | AR11_HFifoErrU(5) |
AR11_HFifoErrU(6) | AR11_HFifoErrU(7)}, {
12, AR12_StartNU | AR12_HFifoE}, {
14, 0}
};
static inline int acam_is_pll_locked(struct wrtd_tdc_dev *dev)
{
uint32_t status;
uint32_t status;
tdc_writel(dev, TDC_CTRL_READ_ACAM_CFG,
tdc_writel(dev, TDC_CTRL_READ_ACAM_CFG,
BASE_DP_TDC_REGS + TDC_REG_CTRL);
udelay(100);
udelay(100);
status = tdc_readl(dev, BASE_DP_TDC_REGS + TDC_REG_ACAM_READBACK(12));
status = tdc_readl(dev, BASE_DP_TDC_REGS + TDC_REG_ACAM_READBACK(12));
return !(status & AR12_NotLocked);
return !(status & AR12_NotLocked);
}
int tdc_acam_init(struct wrtd_tdc_dev *dev)
{
int i;
int i;
pr_debug("%s: initializing ACAM TDC...\n", __func__);
pr_debug("%s: initializing ACAM TDC...\n", __func__);
tdc_writel(dev, TDC_CTRL_RESET_ACAM, BASE_DP_TDC_REGS + TDC_REG_CTRL);
udelay(100);
tdc_writel(dev, TDC_CTRL_RESET_ACAM, BASE_DP_TDC_REGS + TDC_REG_CTRL);
udelay(100);
for (i = 0; i < NB_ACAM_REGS; i++) {
tdc_writel(dev, acam_config[i].value,
for (i = 0; i < NB_ACAM_REGS; i++) {
tdc_writel(dev, acam_config[i].value,
BASE_DP_TDC_REGS + TDC_REG_ACAM_CONFIG(acam_config[i].reg));
}
}
/* commit ACAM config regs */
tdc_writel(dev, TDC_CTRL_LOAD_ACAM_CFG, BASE_DP_TDC_REGS + TDC_REG_CTRL);
udelay(100);
/* commit ACAM config regs */
tdc_writel(dev, TDC_CTRL_LOAD_ACAM_CFG, BASE_DP_TDC_REGS + TDC_REG_CTRL);
udelay(100);
/* and reset the chip (keeps configuration) */
tdc_writel(dev, TDC_CTRL_RESET_ACAM, BASE_DP_TDC_REGS + TDC_REG_CTRL);
udelay(100);
/* and reset the chip (keeps configuration) */
tdc_writel(dev, TDC_CTRL_RESET_ACAM, BASE_DP_TDC_REGS + TDC_REG_CTRL);
udelay(100);
/* wait for the ACAM's PLL to lock (2 seconds) */
mdelay(2000);
if (acam_is_pll_locked(dev)) {
pr_debug("%s: ACAM initialization OK.\n",__func__);
return 0;
}
/* wait for the ACAM's PLL to lock (2 seconds) */
mdelay(2000);
if (acam_is_pll_locked(dev)) {
pr_debug("%s: ACAM initialization OK.\n",__func__);
return 0;
}
pr_error("%s: ACAM PLL doesn't lock\n", __func__);
return -EIO;
pr_error("%s: ACAM PLL doesn't lock\n", __func__);
return -EIO;
}
static int tdc_time_init(struct wrtd_tdc_dev *dev)
{
/* program the VCXO DAC to the default calibration value */
tdc_writel(dev, TDC_VCXO_DEFAULT_TUNE, BASE_DP_TDC_REGS + TDC_REG_DAC_TUNE);
tdc_writel(dev, TDC_CTRL_CONFIG_DAC, BASE_DP_TDC_REGS + TDC_REG_CTRL);
/* program the VCXO DAC to the default calibration value */
tdc_writel(dev, TDC_VCXO_DEFAULT_TUNE, BASE_DP_TDC_REGS + TDC_REG_DAC_TUNE);
tdc_writel(dev, TDC_CTRL_CONFIG_DAC, BASE_DP_TDC_REGS + TDC_REG_CTRL);
/* set TAI time to zero */
tdc_writel(dev, 0, BASE_DP_TDC_REGS + TDC_REG_START_UTC);
tdc_writel(dev, TDC_CTRL_LOAD_UTC, BASE_DP_TDC_REGS + TDC_REG_CTRL);
/* set TAI time to zero */
tdc_writel(dev, 0, BASE_DP_TDC_REGS + TDC_REG_START_UTC);
tdc_writel(dev, TDC_CTRL_LOAD_UTC, BASE_DP_TDC_REGS + TDC_REG_CTRL);
return 0;
return 0;
}
static int tdc_enable_termination(struct wrtd_tdc_dev *tdc,
int channel, int enable)
{
uint32_t ien;
uint32_t ien;
ien = tdc_readl(tdc, BASE_DP_TDC_REGS + TDC_REG_INPUT_ENABLE);
ien = tdc_readl(tdc, BASE_DP_TDC_REGS + TDC_REG_INPUT_ENABLE);
if (enable)
ien |= (1 << channel);
else
ien &= ~(1 << channel);
if (enable)
ien |= (1 << channel);
else
ien &= ~(1 << channel);
tdc_writel(tdc, ien, BASE_DP_TDC_REGS + TDC_REG_INPUT_ENABLE);
tdc_writel(tdc, ien, BASE_DP_TDC_REGS + TDC_REG_INPUT_ENABLE);
return 0;
return 0;
}
static int tdc_channels_init(struct wrtd_tdc_dev *tdc)
{
int i;
int i;
for (i = 0; i < TDC_NUM_CHANNELS; i++) {
/* termination is off by default */
tdc_enable_termination(tdc, i, 0);
}
for (i = 0; i < TDC_NUM_CHANNELS; i++) {
/* termination is off by default */
tdc_enable_termination(tdc, i, 0);
}
return 0;
return 0;
}
static void tdc_enable_acquisition(struct wrtd_tdc_dev *tdc, int enable)
{
uint32_t ien;
ien = tdc_readl(tdc, BASE_DP_TDC_REGS + TDC_REG_INPUT_ENABLE);
if (enable) {
/* Enable TDC acquisition */
tdc_writel(tdc, ien | TDC_INPUT_ENABLE_CH_ALL | TDC_INPUT_ENABLE_FLAG,
BASE_DP_TDC_REGS + TDC_REG_INPUT_ENABLE);
/* Enable ACAM acquisition */
tdc_writel(tdc, TDC_CTRL_EN_ACQ, BASE_DP_TDC_REGS + TDC_REG_CTRL);
} else {
/* Disable ACAM acquisition */
tdc_writel(tdc, TDC_CTRL_DIS_ACQ, BASE_DP_TDC_REGS + TDC_REG_CTRL);
/* Disable TDC acquisition */
tdc_writel(tdc, ien & ~(TDC_INPUT_ENABLE_CH_ALL | TDC_INPUT_ENABLE_FLAG),
uint32_t ien;
ien = tdc_readl(tdc, BASE_DP_TDC_REGS + TDC_REG_INPUT_ENABLE);
if (enable) {
/* Enable TDC acquisition */
tdc_writel(tdc, ien | TDC_INPUT_ENABLE_CH_ALL | TDC_INPUT_ENABLE_FLAG,
BASE_DP_TDC_REGS + TDC_REG_INPUT_ENABLE);
/* Enable ACAM acquisition */
tdc_writel(tdc, TDC_CTRL_EN_ACQ, BASE_DP_TDC_REGS + TDC_REG_CTRL);
} else {
/* Disable ACAM acquisition */
tdc_writel(tdc, TDC_CTRL_DIS_ACQ, BASE_DP_TDC_REGS + TDC_REG_CTRL);
/* Disable TDC acquisition */
tdc_writel(tdc, ien & ~(TDC_INPUT_ENABLE_CH_ALL | TDC_INPUT_ENABLE_FLAG),
BASE_DP_TDC_REGS + TDC_REG_INPUT_ENABLE);
}
}
}
/* FIXME missing all calibration */
static int tdc_init(struct wrtd_tdc_dev *tdc)
{
int err;
int err;
pr_debug("%s: Initializing the TDC...\n\r", __func__);
pr_debug("%s: Initializing the TDC...\n\r", __func__);
/* Initialize the TDC FIFO (channels disabled, default dead time) */
tdc_writel(tdc, 0x0, BASE_DP_TDC_DIRECT + DR_REG_CHAN_ENABLE);
tdc_writel(tdc, DEFAULT_DEAD_TIME, BASE_DP_TDC_DIRECT + DR_REG_DEAD_TIME);
/* Initialize the TDC FIFO (channels disabled, default dead time) */
tdc_writel(tdc, 0x0, BASE_DP_TDC_DIRECT + DR_REG_CHAN_ENABLE);
tdc_writel(tdc, DEFAULT_DEAD_TIME, BASE_DP_TDC_DIRECT + DR_REG_DEAD_TIME);
#ifndef SIMULATION
tdc_enable_acquisition(tdc, 0);
tdc_enable_acquisition(tdc, 0);
err = tdc_acam_init(tdc);
if (err)
return err;
err = tdc_acam_init(tdc);
if (err)
return err;
err = tdc_time_init(tdc);
if (err)
return err;
err = tdc_time_init(tdc);
if (err)
return err;
err = tdc_channels_init(tdc);
if (err)
return err;
err = tdc_channels_init(tdc);
if (err)
return err;
tdc_enable_acquisition(tdc, 1);
tdc_enable_acquisition(tdc, 1);
#endif
pr_debug("%s: TDC initialization complete\n\r", __func__);
pr_debug("%s: TDC initialization complete\n\r", __func__);
return 0;
return 0;
}
static inline int tdc_wr_link_up(struct wrtd_tdc_dev *tdc)
{
return tdc_readl(tdc, BASE_DP_TDC_REGS + TDC_REG_WR_STAT) & TDC_WR_STAT_LINK;
return tdc_readl(tdc, BASE_DP_TDC_REGS + TDC_REG_WR_STAT) & TDC_WR_STAT_LINK;
}
static inline int tdc_wr_time_locked(struct wrtd_tdc_dev *tdc)
{
return tdc_readl(tdc, BASE_DP_TDC_REGS + TDC_REG_WR_STAT) & TDC_WR_STAT_AUX_LOCKED;
return tdc_readl(tdc, BASE_DP_TDC_REGS + TDC_REG_WR_STAT) & TDC_WR_STAT_AUX_LOCKED;
}
static void tdc_wr_enable_lock(struct wrtd_tdc_dev *tdc, int enable)
{
tdc_writel(tdc, TDC_CTRL_DIS_ACQ, BASE_DP_TDC_REGS + TDC_REG_CTRL);
tdc_writel(tdc, TDC_CTRL_DIS_ACQ, BASE_DP_TDC_REGS + TDC_REG_CTRL);
tdc_writel(tdc, enable ? TDC_WR_CTRL_ENABLE : 0, BASE_DP_TDC_REGS + TDC_REG_WR_CTRL);
tdc_writel(tdc, TDC_CTRL_EN_ACQ, BASE_DP_TDC_REGS + TDC_REG_CTRL);
tdc_writel(tdc, TDC_CTRL_EN_ACQ, BASE_DP_TDC_REGS + TDC_REG_CTRL);
}
static inline int tdc_wr_time_ready(struct wrtd_tdc_dev *tdc)
{
return tdc_readl(tdc, BASE_DP_TDC_REGS + TDC_REG_WR_STAT) & TDC_WR_STAT_TIME_VALID;
return tdc_readl(tdc, BASE_DP_TDC_REGS + TDC_REG_WR_STAT) & TDC_WR_STAT_TIME_VALID;
}
static inline int tdc_wr_sync_timeout(void)
......@@ -256,11 +256,11 @@ static void tdc_input(struct wrtd_tdc_dev *tdc)
ev.ts.ns = tdc_readl(tdc, BASE_DP_TDC_DIRECT + DR_REG_FIFO_R1) * 8;
meta = tdc_readl(tdc, BASE_DP_TDC_DIRECT + DR_REG_FIFO_R2);
/* Conversion from ACAM TDC bins to WR already done in gateware
(including overflow checks), result is in 2e-9 ns ticks */
/* Conversion from ACAM TDC bins to WR already done in gateware
(including overflow checks), result is in 2e-9 ns ticks */
uint32_t frac = meta & 0x3ffff;
/* Change frac resolution from 2e-9 ns to 2e-32 ns */
/* Change frac resolution from 2e-9 ns to 2e-32 ns */
ev.ts.frac = frac << (32 - 9);
int channel = (meta >> 19) & 0x7;
......@@ -269,7 +269,7 @@ static void tdc_input(struct wrtd_tdc_dev *tdc)
ev.id[0] = 'L';
ev.id[1] = 'C';
ev.id[2] = '-';
ev.id[3] = 'I';
ev.id[3] = 'I';
ev.id[4] = '1' + channel; /* channel starts from 0. */
ev.flags = 0;
......
......@@ -23,24 +23,27 @@
/**
* WRTD timestamp format
*/
struct wrtd_tstamp {
// TODO: maybe distinguish between UTC and TAI
// TODO: maybe distinguish between UTC and TAI
typedef struct wrtd_tstamp {
/** TAI seconds since 1/1/1970 (Unix Epoch Time). */
uint32_t seconds;
/** Number of nanoseconds. Wraps at 10e9. */
uint32_t ns;
/** Number of fractional nanoseconds. Unit is 2e-32 ns. */
uint32_t frac;
};
} wrtd_tstamp;
struct wrtd_event {
/* Time of the event. */
/** Time of the event. */
struct wrtd_tstamp ts;
/* Event id. */
/** Event id. */
char id[WRTD_ID_LEN];
/* Sequence number. */
/** Sequence number. */
uint32_t seq;
/* Associated flags. */
/** Associated flags. */
unsigned char flags;
};
......
......@@ -38,7 +38,7 @@ enum wrtd_status wrtd_attr_set_alarm_period(struct wrtd_dev *wrtd,
if (value->seconds != 0)
return wrtd_return_error(wrtd, WRTD_ERROR_INVALID_VALUE,
"Invalid value (%d) for function %s, \
"Invalid value (%d) for function %s, \
parameter value.seconds",
value->seconds, __func__);
......@@ -153,7 +153,7 @@ enum wrtd_status wrtd_attr_set_alarm_repeat_count(struct wrtd_dev *wrtd,
if (value < 0)
return wrtd_return_error(wrtd, WRTD_ERROR_INVALID_VALUE,
"Invalid value (%d) for function %s, \
"Invalid value (%d) for function %s, \
parameter value", value, __func__);
status = wrtd_find_alarm(wrtd, rep_cap_id, &idx, __func__);
......@@ -194,7 +194,7 @@ enum wrtd_status wrtd_attr_set_rule_repeat_count(struct wrtd_dev *wrtd,
if (value < 0)
return wrtd_return_error(wrtd, WRTD_ERROR_INVALID_VALUE,
"Invalid value (%d) for function %s, \
"Invalid value (%d) for function %s, \
parameter value", value, __func__);
status = wrtd_find_rule(wrtd, rep_cap_id, &idx, __func__);
......@@ -409,7 +409,7 @@ enum wrtd_status wrtd_attr_set_rule_delay(struct wrtd_dev *wrtd,
if (value->seconds != 0)
return wrtd_return_error(wrtd, WRTD_ERROR_INVALID_VALUE,
"Invalid value (%d) for function %s, \
"Invalid value (%d) for function %s, \
parameter value.seconds",
value->seconds, __func__);
......@@ -452,7 +452,7 @@ enum wrtd_status wrtd_attr_set_rule_holdoff(struct wrtd_dev *wrtd,
if (value->seconds != 0)
return wrtd_return_error(wrtd, WRTD_ERROR_INVALID_VALUE,
"Invalid value (%d) for function %s, \
"Invalid value (%d) for function %s, \
parameter value.seconds",
value->seconds, __func__);
......@@ -495,7 +495,7 @@ enum wrtd_status wrtd_attr_set_rule_resync_period(struct wrtd_dev *wrtd,
if (value->seconds != 0)
return wrtd_return_error(wrtd, WRTD_ERROR_INVALID_VALUE,
"Invalid value (%d) for function %s, \
"Invalid value (%d) for function %s, \
parameter value.seconds",
value->seconds, __func__);
......@@ -850,6 +850,29 @@ enum wrtd_status wrtd_attr_get_rule_send_late(struct wrtd_dev *wrtd,
return WRTD_SUCCESS;
}
enum wrtd_status wrtd_attr_get_sys_time(struct wrtd_dev *wrtd,
struct wrtd_tstamp *value)
{
enum wrtd_status status;
struct wrtd_config_msg msg;
if(wrtd == NULL){
return WRTD_ERROR_NOT_INITIALIZED;
}
/* Always use CPU #0 (it does exist!). */
status = wrtd_msg_get_config(wrtd, 0, &msg, __func__);
WRTD_RETURN_IF_ERROR(status);
*value = msg.now;
return WRTD_SUCCESS;
}
/**
*@} End group Time
*/
enum wrtd_status wrtd_attr_global(struct wrtd_dev *wrtd,
const char *rep_cap_id)
{
......
......@@ -123,7 +123,7 @@ enum wrtd_status wrtd_log_read(struct wrtd_dev *wrtd,
}
/* Clean up errno to be able to distinguish between error cases and
normal behaviour when the function return less messages
normal behaviour when the function returns less messages
than expected */
errno = 0;
......@@ -135,7 +135,7 @@ enum wrtd_status wrtd_log_read(struct wrtd_dev *wrtd,
}
if (ret < 0)
return wrtd_return_error
(wrtd, WRTD_ERROR_INTERNAL, "%s/%s: %s",
(wrtd, WRTD_ERROR_INTERNAL, "%s/%s(poll): %s",
caller_func, __func__, trtl_strerror(errno));
for (i = 0; i < wrtd->nbr_cpus; ++i) {
......@@ -144,7 +144,7 @@ enum wrtd_status wrtd_log_read(struct wrtd_dev *wrtd,
ret = trtl_msg_async_recv(wrtd->trtl, i, WRTD_HMQ, &msg, 1);
if (ret <= 0)
return wrtd_return_error
(wrtd, WRTD_ERROR_INTERNAL, "%s/%s: %s",
(wrtd, WRTD_ERROR_INTERNAL, "%s/%s(recv): %s",
caller_func, __func__, trtl_strerror(errno));
memcpy(log, msg.data, sizeof(struct wrtd_log_entry));
......@@ -321,17 +321,16 @@ enum wrtd_status wrtd_fill_roots(struct wrtd_dev *wrtd, const char *caller_func)
if (status != WRTD_SUCCESS)
return status;
if (wrtd->roots[cpu].ver_major != WRTD_VERSION_MAJOR) {
status = wrtd_return_error
/* Invalidate root. */
wrtd->roots[cpu].ver_major = 0;
return wrtd_return_error
(wrtd, WRTD_ERROR_VERSION_MISMATCH,
"%s/%s: incorrect major version (%d instead of %d)",
caller_func, __func__,
wrtd->roots[cpu].ver_major,
WRTD_VERSION_MAJOR);
/* Invalidate root. */
wrtd->roots[cpu].ver_major = 0;
return status;
}
if (wrtd->roots[cpu].ver_minor != WRTD_VERSION_MINOR) {
if (wrtd->roots[cpu].ver_minor > WRTD_VERSION_MINOR) {
/* Invalidate root. */
wrtd->roots[cpu].ver_major = 0;
return wrtd_return_error
......@@ -621,4 +620,3 @@ inline enum wrtd_status wrtd_rule_check_disabled(struct wrtd_dev *wrtd, unsigned
caller_func, __func__);
return WRTD_SUCCESS;
}
......@@ -11,13 +11,6 @@
#define WRTD_RETURN_IF_ERROR(status) if(status != WRTD_SUCCESS) return status
/* A complete log entry needs 35+1+35+1+9+1+8=90 + 1 for null termination.
1st field: log tstamp in YYYY-MM-DD,hh:mm:ss.xxx.yyy.zzz+fff format.
2nd field: event tstamp in YYYY-MM-DD,hh:mm:ss.xxx.yyy.zzz+fff format.
3rd field: log type, padded with spaces to 9 characters.
4th field: log reason, padded with spaces to 8 characters. */
#define WRTD_LOG_ENTRY_SIZE 91
#define WRTD_DEFAULT_TIMEOUT 1000
/* Add nanoseconds to an existing wrtd timestamp */
......@@ -302,6 +295,8 @@ enum wrtd_status wrtd_attr_set_rule_send_late(struct wrtd_dev *wrtd,
enum wrtd_status wrtd_attr_get_rule_send_late(struct wrtd_dev *wrtd,
const char *rep_cap_id,
bool *value);
enum wrtd_status wrtd_attr_get_sys_time(struct wrtd_dev *wrtd,
struct wrtd_tstamp *value);
enum wrtd_status wrtd_attr_global(struct wrtd_dev *wrtd,
const char *rep_cap_id);
......
......@@ -20,21 +20,31 @@
* @{
*/
enum wrtd_status wrtd_init(const char *resource_name,
bool reset,
const char *options_str,
struct wrtd_dev **dev)
/**
* Initialize the WRTD device and obtain the WRTD device token.
*
* @param[in] resource_name Underlying MockTurtle device ID in
* the form of **MTxxx** or **trtl-xxxx**.
* @param[in] reset Reserved for future use.
* @param[in] options_str Reserved for future use.
* @param[out] wrtd Pointer to WRTD device token.
* @return #wrtd_status
*/
wrtd_status wrtd_init(const char *resource_name,
bool reset,
const char *options_str,
wrtd_dev **wrtd)
{
int i;
struct trtl_dev *trtl;
const struct trtl_config_rom *cfgrom;
struct wrtd_config_msg msg;
enum wrtd_status status;
wrtd_status status;
static int initialized;
/* In case of error... */
*dev = NULL;
*wrtd = NULL;
/* Initialize (only once).*/
if (!initialized) {
......@@ -62,9 +72,9 @@ enum wrtd_status wrtd_init(const char *resource_name,
if (trtl == NULL)
return WRTD_ERROR_RESOURCE_UNKNOWN;
struct wrtd_dev *res;
wrtd_dev *res;
res = malloc(sizeof(struct wrtd_dev));
res = malloc(sizeof(wrtd_dev));
if (res == NULL)
return WRTD_ERROR_OUT_OF_MEMORY;
memset(res, 0, sizeof(*res));
......@@ -92,32 +102,55 @@ enum wrtd_status wrtd_init(const char *resource_name,
res->nbr_cpus = cfgrom->n_cpu;
*dev = res;
*wrtd = res;
return WRTD_SUCCESS;
}
enum wrtd_status wrtd_close(struct wrtd_dev *dev)
/**
* Close a WRTD device and release all resources.
*
* @param[in] wrtd Device token.
* @return #wrtd_status
*/
wrtd_status wrtd_close(wrtd_dev *wrtd)
{
if(dev == NULL){
if(wrtd == NULL){
return WRTD_ERROR_NOT_INITIALIZED;
}
trtl_close(dev->trtl);
trtl_close(wrtd->trtl);
free(dev->alarms);
free(dev->rules);
free(dev);
free(wrtd->alarms);
free(wrtd->rules);
free(wrtd);
return WRTD_SUCCESS;
}
enum wrtd_status wrtd_reset(struct wrtd_dev *dev)
/**
* Reset a WRTD device. This will remove all defined Alarms and Rules.
*
* @param[in] wrtd Device token.
* @return #wrtd_status
*/
wrtd_status wrtd_reset(wrtd_dev *wrtd)
{
if(dev == NULL){
wrtd_status status;
if(wrtd == NULL){
return WRTD_ERROR_NOT_INITIALIZED;
}
status = wrtd_disable_all_alarms(wrtd);
WRTD_RETURN_IF_ERROR(status);
status = wrtd_disable_all_rules(wrtd);
WRTD_RETURN_IF_ERROR(status);
status = wrtd_remove_all_alarms(wrtd);
WRTD_RETURN_IF_ERROR(status);
status = wrtd_remove_all_rules(wrtd);
WRTD_RETURN_IF_ERROR(status);
return WRTD_SUCCESS;
}
......@@ -131,17 +164,17 @@ enum wrtd_status wrtd_reset(struct wrtd_dev *dev)
* buffer overflow.
*
* @param[in] wrtd Device token.
* @param[out] error_code #wrtd_status pointer to return the error code.
* @param[out] error_code #wrtd_status pointer to return the error code. Ignored if NULL.
* @param[in] error_description_buffer_size Size of pre-allocated `error_description` buffer.
* @param[out] error_description Buffer to store the detailed error message string.
* @return #wrtd_status. See also IVI-3.2, section 3.1.2.1.
*/
enum wrtd_status wrtd_get_error(struct wrtd_dev *wrtd,
enum wrtd_status *error_code,
int32_t error_description_buffer_size,
char *error_description)
wrtd_status wrtd_get_error(wrtd_dev *wrtd,
wrtd_status *error_code,
int32_t error_description_buffer_size,
char *error_description)
{
enum wrtd_status status;
wrtd_status status;
int ret;
if(wrtd == NULL){
......@@ -200,21 +233,25 @@ enum wrtd_status wrtd_get_error(struct wrtd_dev *wrtd,
* Convert a #wrtd_status error code to a string. Modelled after the IVI-C
* ErrorMessage function.
*
* @param[in] wrtd Device token.
* @param[in] wrtd Device token. Can be NULL to allow calling this function
* even when initialisation has failed.
* @param[in] err_code #wrtd_status error code to convert.
* @param[out] err_message Buffer of at least 256 bytes to store the resulting string.
* @return #wrtd_status
*/
enum wrtd_status wrtd_error_message(struct wrtd_dev *wrtd,
enum wrtd_status err_code,
char *err_message)
wrtd_status wrtd_error_message(wrtd_dev *wrtd,
wrtd_status err_code,
char *err_message)
{
if(err_message == NULL){
return wrtd_return_error
(wrtd, WRTD_ERROR_NULL_POINTER,
"Null pointer passed for function %s, \
parameter err_message", __func__);
if (err_message == NULL) {
if (wrtd != NULL)
return wrtd_return_error
(wrtd, WRTD_ERROR_NULL_POINTER,
"Null pointer passed for function %s, \
parameter err_message", __func__);
return WRTD_ERROR_NULL_POINTER;
}
switch(err_code){
......@@ -287,10 +324,13 @@ enum wrtd_status wrtd_error_message(struct wrtd_dev *wrtd,
break;
default:
return wrtd_return_error(wrtd, WRTD_ERROR_INVALID_VALUE,
"Invalid value (%d) for function %s, \
parameter err_code",
err_code, __func__);
if (wrtd != NULL)
return wrtd_return_error(wrtd, WRTD_ERROR_INVALID_VALUE,
"Invalid value (%d) for function %s, \
parameter err_code",
err_code, __func__);
return WRTD_ERROR_INVALID_VALUE;
}
return WRTD_SUCCESS;
......@@ -317,12 +357,16 @@ enum wrtd_status wrtd_error_message(struct wrtd_dev *wrtd,
* @param[in] value Value to write to the attribute.
* @return #wrtd_status
*/
enum wrtd_status wrtd_set_attr_bool(struct wrtd_dev *wrtd,
const char *rep_cap_id,
enum wrtd_attr id,
bool value)
wrtd_status wrtd_set_attr_bool(wrtd_dev *wrtd,
const char *rep_cap_id,
wrtd_attr id,
bool value)
{
enum wrtd_status status;
wrtd_status status;
if(wrtd == NULL){
return WRTD_ERROR_NOT_INITIALIZED;
}
status = wrtd_validate_id(wrtd, rep_cap_id, __func__);
WRTD_RETURN_IF_ERROR(status);
......@@ -366,16 +410,27 @@ enum wrtd_status wrtd_set_attr_bool(struct wrtd_dev *wrtd,
* @param[out] value Retrieved attribute value.
* @return #wrtd_status
*/
enum wrtd_status wrtd_get_attr_bool(struct wrtd_dev *wrtd,
const char *rep_cap_id,
enum wrtd_attr id,
bool *value)
wrtd_status wrtd_get_attr_bool(wrtd_dev *wrtd,
const char *rep_cap_id,
wrtd_attr id,
bool *value)
{
enum wrtd_status status;
wrtd_status status;
if(wrtd == NULL){
return WRTD_ERROR_NOT_INITIALIZED;
}
status = wrtd_validate_id(wrtd, rep_cap_id, __func__);
WRTD_RETURN_IF_ERROR(status);
if(value == NULL){
return wrtd_return_error
(wrtd, WRTD_ERROR_NULL_POINTER,
"Null pointer passed for function %s, \
parameter value", __func__);
}
switch(id) {
case WRTD_ATTR_EVENT_LOG_ENABLED:
status = wrtd_attr_global
......@@ -422,12 +477,16 @@ enum wrtd_status wrtd_get_attr_bool(struct wrtd_dev *wrtd,
* @param[in] value Value to write to the attribute.
* @return #wrtd_status
*/
enum wrtd_status wrtd_set_attr_int32(struct wrtd_dev *wrtd,
const char *rep_cap_id,
enum wrtd_attr id,
int32_t value)
wrtd_status wrtd_set_attr_int32(wrtd_dev *wrtd,
const char *rep_cap_id,
wrtd_attr id,
int32_t value)
{
enum wrtd_status status;
wrtd_status status;
if(wrtd == NULL){
return WRTD_ERROR_NOT_INITIALIZED;
}
status = wrtd_validate_id(wrtd, rep_cap_id, __func__);
WRTD_RETURN_IF_ERROR(status);
......@@ -473,16 +532,27 @@ enum wrtd_status wrtd_set_attr_int32(struct wrtd_dev *wrtd,
* @param[out] value Retrieved attribute value.
* @return #wrtd_status
*/
enum wrtd_status wrtd_get_attr_int32(struct wrtd_dev *wrtd,
const char *rep_cap_id,
enum wrtd_attr id,
int32_t *value)
wrtd_status wrtd_get_attr_int32(wrtd_dev *wrtd,
const char *rep_cap_id,
wrtd_attr id,
int32_t *value)
{
enum wrtd_status status;
wrtd_status status;
if(wrtd == NULL){
return WRTD_ERROR_NOT_INITIALIZED;
}
status = wrtd_validate_id(wrtd, rep_cap_id, __func__);
WRTD_RETURN_IF_ERROR(status);
if(value == NULL){
return wrtd_return_error
(wrtd, WRTD_ERROR_NULL_POINTER,
"Null pointer passed for function %s, \
parameter value", __func__);
}
switch(id) {
case WRTD_MAJOR_VERSION:
status = wrtd_attr_global
......@@ -557,12 +627,16 @@ enum wrtd_status wrtd_get_attr_int32(struct wrtd_dev *wrtd,
* @param[in] value Value to write to the attribute.
* @return #wrtd_status
*/
enum wrtd_status wrtd_set_attr_string(struct wrtd_dev *wrtd,
const char *rep_cap_id,
enum wrtd_attr id,
const char *value)
wrtd_status wrtd_set_attr_string(wrtd_dev *wrtd,
const char *rep_cap_id,
wrtd_attr id,
const char *value)
{
enum wrtd_status status;
wrtd_status status;
if(wrtd == NULL){
return WRTD_ERROR_NOT_INITIALIZED;
}
status = wrtd_validate_id(wrtd, rep_cap_id, __func__);
WRTD_RETURN_IF_ERROR(status);
......@@ -598,17 +672,28 @@ enum wrtd_status wrtd_set_attr_string(struct wrtd_dev *wrtd,
* @param[out] value Retrieved attribute value.
* @return #wrtd_status. See also IVI-3.2, section 3.1.2.1.
*/
enum wrtd_status wrtd_get_attr_string(struct wrtd_dev *wrtd,
const char *rep_cap_id,
enum wrtd_attr id,
int32_t value_buffer_size,
char *value)
wrtd_status wrtd_get_attr_string(wrtd_dev *wrtd,
const char *rep_cap_id,
wrtd_attr id,
int32_t value_buffer_size,
char *value)
{
enum wrtd_status status;
wrtd_status status;
if(wrtd == NULL){
return WRTD_ERROR_NOT_INITIALIZED;
}
status = wrtd_validate_id(wrtd, rep_cap_id, __func__);
WRTD_RETURN_IF_ERROR(status);
if(value == NULL){
return wrtd_return_error
(wrtd, WRTD_ERROR_NULL_POINTER,
"Null pointer passed for function %s, \
parameter value", __func__);
}
switch(id) {
case WRTD_ATTR_RULE_SOURCE:
return wrtd_attr_get_rule_source
......@@ -634,16 +719,27 @@ enum wrtd_status wrtd_get_attr_string(struct wrtd_dev *wrtd,
* @param[in] value Value to write to the attribute.
* @return #wrtd_status
*/
enum wrtd_status wrtd_set_attr_tstamp(struct wrtd_dev *wrtd,
const char *rep_cap_id,
enum wrtd_attr id,
const struct wrtd_tstamp *value)
wrtd_status wrtd_set_attr_tstamp(wrtd_dev *wrtd,
const char *rep_cap_id,
wrtd_attr id,
const wrtd_tstamp *value)
{
enum wrtd_status status;
wrtd_status status;
if(wrtd == NULL){
return WRTD_ERROR_NOT_INITIALIZED;
}
status = wrtd_validate_id(wrtd, rep_cap_id, __func__);
WRTD_RETURN_IF_ERROR(status);
if(value == NULL){
return wrtd_return_error
(wrtd, WRTD_ERROR_NULL_POINTER,
"Null pointer passed for function %s, \
parameter value", __func__);
}
switch(id) {
case WRTD_ATTR_ALARM_TIME:
return wrtd_attr_set_alarm_time
......@@ -660,6 +756,7 @@ enum wrtd_status wrtd_set_attr_tstamp(struct wrtd_dev *wrtd,
case WRTD_ATTR_RULE_RESYNC_PERIOD:
return wrtd_attr_set_rule_resync_period
(wrtd, rep_cap_id, value);
case WRTD_ATTR_SYS_TIME:
case WRTD_ATTR_STAT_RULE_RX_LAST:
case WRTD_ATTR_STAT_RULE_TX_LAST:
case WRTD_ATTR_STAT_RULE_MISSED_LAST:
......@@ -688,17 +785,34 @@ enum wrtd_status wrtd_set_attr_tstamp(struct wrtd_dev *wrtd,
* @param[out] value Retrieved attribute value.
* @return #wrtd_status
*/
enum wrtd_status wrtd_get_attr_tstamp(struct wrtd_dev *wrtd,
const char *rep_cap_id,
enum wrtd_attr id,
struct wrtd_tstamp *value)
wrtd_status wrtd_get_attr_tstamp(wrtd_dev *wrtd,
const char *rep_cap_id,
wrtd_attr id,
wrtd_tstamp *value)
{
enum wrtd_status status;
wrtd_status status;
if(wrtd == NULL){
return WRTD_ERROR_NOT_INITIALIZED;
}
status = wrtd_validate_id(wrtd, rep_cap_id, __func__);
WRTD_RETURN_IF_ERROR(status);
if(value == NULL){
return wrtd_return_error
(wrtd, WRTD_ERROR_NULL_POINTER,
"Null pointer passed for function %s, \
parameter value", __func__);
}
switch(id) {
case WRTD_ATTR_SYS_TIME:
status = wrtd_attr_global
(wrtd, rep_cap_id);
WRTD_RETURN_IF_ERROR(status);
return wrtd_attr_get_sys_time
(wrtd, value);
case WRTD_ATTR_ALARM_TIME:
return wrtd_attr_get_alarm_time
(wrtd, rep_cap_id, value);
......@@ -746,35 +860,6 @@ enum wrtd_status wrtd_get_attr_tstamp(struct wrtd_dev *wrtd,
*@} End group Attributes
*/
/**
* @defgroup Time
* Time-related functions.
* @{
*/
enum wrtd_status wrtd_get_sys_time(struct wrtd_dev *wrtd,
struct wrtd_tstamp *time)
{
struct wrtd_config_msg msg;
enum wrtd_status status;
if(wrtd == NULL){
return WRTD_ERROR_NOT_INITIALIZED;
}
/* Always use CPU #0 (it does exist!). */
status = wrtd_msg_get_config(wrtd, 0, &msg, __func__);
WRTD_RETURN_IF_ERROR(status);
*time = msg.now;
return WRTD_SUCCESS;
}
/**
*@} End group Time
*/
/**
* @defgroup Event Log
* Functions to retrieve and clear entries from the event log.
......@@ -790,17 +875,51 @@ enum wrtd_status wrtd_get_sys_time(struct wrtd_dev *wrtd,
* the buffer_size < 0 case, which produces an error instead of allowing a potential
* buffer overflow.
*
* Event log entries use the format `log tstamp|event tstamp|log type|log reason`, where:
* - `log tstamp` is the timestamp of when the event was logged. This can be zero (1/1/1970)
* for some time-critical events which favour speed over logging information.
* - `event tstamp` is the timestamp of when the event happened.
* - `log type` is the type of the event.
* - `log tstamp` is the specific reason for this type of event.
*
* All event log entries have a fixed length of #WRTD_LOG_ENTRY_SIZE.
*
* Timestamps are represented using the format `YYYY-MM-DD,hh:mm:ss.mmm.uuu.nnn+fff`, where:
* - `YYYY-MM-DD` is the year, month and day.
* - `hh:mm:ss` is hours, minutes and seconds.
* - `mmm.uuu.nnn` is milliseconds, microseconds and nanoseconds.
* - `fff` is fractional nanoseconds. The unit is 2e-9 nanoseconds.
*
* Log types and reasons can be one of the following (in the form `type|reason`):
* - `GENERATED|ALARM` : An Event was generated because of an Alarm.
* - `GENERATED|DEVICE_x`: An Event was generated from device x.
* - `CONSUMED|START` : An Event with a local channel output as destination has been
* forwarded and scheduled to execute. The exact meaning of this log type is
* application-specific.
* - `CONSUMED|DONE` : An Event with a local channel output as destination has
* finished executing. The exact meaning of this log type is application-specific.
* - `DISCARDED|NO SYNC` : An Event was discarded because the device was not syncrhonized
to White Rabbit. See also #WRTD_ATTR_STAT_RULE_MISSED_NOSYNC.
* - `DISCARDED|HOLD OFF`: An Event was discarded because violated the programmed hold-off
* value. See also #WRTD_ATTR_STAT_RULE_MISSED_HOLDOFF.
* - `DISCARDED|TIME OUT`: An Event was discarded because it arrived too late. See also
* #WRTD_ATTR_STAT_RULE_MISSED_LATE.
* - `DISCARDED|OVERFLOW`: An Event was discarded because of internal buffer overflows.
* This may happen if the Event rate is too high. See also #WRTD_ATTR_STAT_RULE_MISSED_OVERFLOW.
* - `NETWORK|TX` : An Event was sent over the White Rabbit network.
* - `NETWORK|RX` : An Event was received over the White Rabbit network.
*
* @param[in] wrtd Device token.
* @param[in] log_entry_buffer_size Size of pre-allocated `log_entry` buffer.
* @param[out] log_entry Buffer to store the log message string.
* @return #wrtd_status. See also IVI-3.2, section 3.1.2.1.
*/
enum wrtd_status wrtd_get_next_event_log_entry(struct wrtd_dev *wrtd,
int32_t log_entry_buffer_size,
char *log_entry)
wrtd_status wrtd_get_next_event_log_entry(wrtd_dev *wrtd,
int32_t log_entry_buffer_size,
char *log_entry)
{
struct wrtd_log_entry log;
enum wrtd_status status;
wrtd_status status;
struct tm *tm;
if(wrtd == NULL){
......@@ -961,7 +1080,14 @@ enum wrtd_status wrtd_get_next_event_log_entry(struct wrtd_dev *wrtd,
return WRTD_SUCCESS;
}
enum wrtd_status wrtd_clear_event_log_entries(struct wrtd_dev *wrtd)
/**
* Clear all entries from the Event Log. Modelled after the IVI-C
* ClearEventLog function (from IVI-3.15).
*
* @param[in] wrtd Device token.
* @return #wrtd_status.
*/
wrtd_status wrtd_clear_event_log_entries(wrtd_dev *wrtd)
{
int ret, i;
......@@ -990,10 +1116,18 @@ enum wrtd_status wrtd_clear_event_log_entries(struct wrtd_dev *wrtd)
* @{
*/
enum wrtd_status wrtd_add_alarm(struct wrtd_dev *wrtd,
const char *rep_cap_id)
/**
* Create a new Alarm. Complies with the IVI "Parameter style"
* for repeated capabilities (see IVI-3.4, section 12).
*
* @param[in] wrtd Device token.
* @param[in] rep_cap_id Name for the new Alarm.
* @return #wrtd_status
*/
wrtd_status wrtd_add_alarm(wrtd_dev *wrtd,
const char *rep_cap_id)
{
enum wrtd_status status;
wrtd_status status;
unsigned int i;
int idx;
struct wrtd_alarm *alarm;
......@@ -1040,10 +1174,20 @@ enum wrtd_status wrtd_add_alarm(struct wrtd_dev *wrtd,
return WRTD_SUCCESS;
}
enum wrtd_status wrtd_disable_all_alarms(struct wrtd_dev *wrtd)
/**
* Disable all defined Alarms. Complies with the IVI "Parameter style"
* for repeated capabilities (see IVI-3.4, section 12).
*
* See also, #WRTD_ATTR_ALARM_ENABLED and #wrtd_set_attr_bool
* for disabling a single Alarm.
*
* @param[in] wrtd Device token.
* @return #wrtd_status
*/
wrtd_status wrtd_disable_all_alarms(wrtd_dev *wrtd)
{
unsigned idx;
enum wrtd_status status;
wrtd_status status;
struct wrtd_alarm *alarm;
int changed = 0;
......@@ -1069,10 +1213,19 @@ enum wrtd_status wrtd_disable_all_alarms(struct wrtd_dev *wrtd)
return WRTD_SUCCESS;
}
enum wrtd_status wrtd_remove_alarm(struct wrtd_dev *wrtd,
const char *rep_cap_id)
/**
* Remove an Alarm. The Alarm must not be enabled.
* Complies with the IVI "Parameter style"
* for repeated capabilities (see IVI-3.4, section 12).
*
* @param[in] wrtd Device token.
* @param[in] rep_cap_id Name of the Alarm to remove.
* @return #wrtd_status
*/
wrtd_status wrtd_remove_alarm(wrtd_dev *wrtd,
const char *rep_cap_id)
{
enum wrtd_status status;
wrtd_status status;
unsigned idx;
struct wrtd_alarm *alarm;
......@@ -1097,9 +1250,17 @@ enum wrtd_status wrtd_remove_alarm(struct wrtd_dev *wrtd,
return WRTD_SUCCESS;
}
extern enum wrtd_status wrtd_remove_all_alarms(struct wrtd_dev *wrtd)
/**
* Remove all defined Alarms. The Alarms must not be enabled.
* Complies with the IVI "Parameter style"
* for repeated capabilities (see IVI-3.4, section 12).
*
* @param[in] wrtd Device token.
* @return #wrtd_status
*/
extern wrtd_status wrtd_remove_all_alarms(wrtd_dev *wrtd)
{
enum wrtd_status status;
wrtd_status status;
unsigned idx;
struct wrtd_alarm *alarm;
......@@ -1128,12 +1289,28 @@ extern enum wrtd_status wrtd_remove_all_alarms(struct wrtd_dev *wrtd)
return WRTD_SUCCESS;
}
enum wrtd_status wrtd_get_alarm_name(struct wrtd_dev *wrtd,
int32_t index,
int32_t name_buffer_size,
char *rep_cap_id)
/**
* Retrieve the name of an Alarm, based on the provided index.
* Complies with the IVI "Parameter style"
* for repeated capabilities (see IVI-3.4, section 12).
*
* This function complies with IVI-3.2, section 3.1.2.1 (Additional Compliance Rules
* for C Functions with ViChar Array Output Parameters), with the exception of
* the buffer_size < 0 case, which produces an error instead of allowing a potential
* buffer overflow.
*
* @param[in] wrtd Device token.
* @param[in] index index of the Alarm.
* @param[in] name_buffer_size Size of pre-allocated `name` buffer.
* @param[out] name Buffer to store the retrieved Alarm name.
* @return #wrtd_status. See also IVI-3.2, section 3.1.2.1.
*/
wrtd_status wrtd_get_alarm_name(wrtd_dev *wrtd,
int32_t index,
int32_t name_buffer_size,
char *name)
{
enum wrtd_status status;
wrtd_status status;
unsigned i;
unsigned n;
......@@ -1152,7 +1329,7 @@ enum wrtd_status wrtd_get_alarm_name(struct wrtd_dev *wrtd,
continue;
if (n == index) {
status = wrtd_id_copy_buf
(wrtd, rep_cap_id, name_buffer_size, aid, __func__);
(wrtd, name, name_buffer_size, aid, __func__);
WRTD_RETURN_IF_ERROR(status);
return WRTD_SUCCESS;
}
......@@ -1174,10 +1351,18 @@ enum wrtd_status wrtd_get_alarm_name(struct wrtd_dev *wrtd,
* @{
*/
enum wrtd_status wrtd_add_rule(struct wrtd_dev *wrtd,
const char *rep_cap_id)
/**
* Create a new Rule. Complies with the IVI "Parameter style"
* for repeated capabilities (see IVI-3.4, section 12).
*
* @param[in] wrtd Device token.
* @param[in] rep_cap_id Name for the new Rule.
* @return #wrtd_status
*/
wrtd_status wrtd_add_rule(wrtd_dev *wrtd,
const char *rep_cap_id)
{
enum wrtd_status status;
wrtd_status status;
unsigned int i;
int idx;
struct wrtd_rule *rule;
......@@ -1226,10 +1411,20 @@ enum wrtd_status wrtd_add_rule(struct wrtd_dev *wrtd,
return WRTD_SUCCESS;
}
enum wrtd_status wrtd_disable_all_rules(struct wrtd_dev *wrtd)
/**
* Disable all defined Rules. Complies with the IVI "Parameter style"
* for repeated capabilities (see IVI-3.4, section 12).
*
* See also, #WRTD_ATTR_RULE_ENABLED and #wrtd_set_attr_bool
* for disabling a single Rule.
*
* @param[in] wrtd Device token.
* @return #wrtd_status
*/
wrtd_status wrtd_disable_all_rules(wrtd_dev *wrtd)
{
unsigned idx;
enum wrtd_status status;
wrtd_status status;
struct wrtd_lib_rule *r;
int changed = 0;
......@@ -1254,10 +1449,19 @@ enum wrtd_status wrtd_disable_all_rules(struct wrtd_dev *wrtd)
return WRTD_SUCCESS;
}
enum wrtd_status wrtd_remove_rule(struct wrtd_dev *wrtd,
const char *rep_cap_id)
/**
* Remove a Rule. The Rule must not be enabled.
* Complies with the IVI "Parameter style"
* for repeated capabilities (see IVI-3.4, section 12).
*
* @param[in] wrtd Device token.
* @param[in] rep_cap_id Name of the Rule to remove.
* @return #wrtd_status
*/
wrtd_status wrtd_remove_rule(wrtd_dev *wrtd,
const char *rep_cap_id)
{
enum wrtd_status status;
wrtd_status status;
unsigned idx;
struct wrtd_rule *rule;
......@@ -1282,9 +1486,17 @@ enum wrtd_status wrtd_remove_rule(struct wrtd_dev *wrtd,
return WRTD_SUCCESS;
}
enum wrtd_status wrtd_remove_all_rules(struct wrtd_dev *wrtd)
/**
* Remove all defined Rules. The Rules must not be enabled.
* Complies with the IVI "Parameter style"
* for repeated capabilities (see IVI-3.4, section 12).
*
* @param[in] wrtd Device token.
* @return #wrtd_status
*/
wrtd_status wrtd_remove_all_rules(wrtd_dev *wrtd)
{
enum wrtd_status status;
wrtd_status status;
unsigned idx;
struct wrtd_rule *rule;
......@@ -1315,12 +1527,28 @@ enum wrtd_status wrtd_remove_all_rules(struct wrtd_dev *wrtd)
return WRTD_SUCCESS;
}
enum wrtd_status wrtd_get_rule_name(struct wrtd_dev *wrtd,
int32_t index,
int32_t name_buffer_size,
char *rep_cap_id)
/**
* Retrieve the name of a Rule, based on the provided index.
* Complies with the IVI "Parameter style"
* for repeated capabilities (see IVI-3.4, section 12).
*
* This function complies with IVI-3.2, section 3.1.2.1 (Additional Compliance Rules
* for C Functions with ViChar Array Output Parameters), with the exception of
* the buffer_size < 0 case, which produces an error instead of allowing a potential
* buffer overflow.
*
* @param[in] wrtd Device token.
* @param[in] index index of the Rule.
* @param[in] name_buffer_size Size of pre-allocated `name` buffer.
* @param[out] name Buffer to store the retrieved Rule name.
* @return #wrtd_status. See also IVI-3.2, section 3.1.2.1.
*/
wrtd_status wrtd_get_rule_name(wrtd_dev *wrtd,
int32_t index,
int32_t name_buffer_size,
char *name)
{
enum wrtd_status status;
wrtd_status status;
unsigned i;
unsigned n;
......@@ -1339,7 +1567,7 @@ enum wrtd_status wrtd_get_rule_name(struct wrtd_dev *wrtd,
continue;
if (n == index) {
status = wrtd_id_copy_buf
(wrtd, rep_cap_id, name_buffer_size, aid, __func__);
(wrtd, name, name_buffer_size, aid, __func__);
WRTD_RETURN_IF_ERROR(status);
return WRTD_SUCCESS;
}
......@@ -1351,10 +1579,17 @@ enum wrtd_status wrtd_get_rule_name(struct wrtd_dev *wrtd,
parameter index.", index, __func__);
}
enum wrtd_status wrtd_reset_rule_stats(struct wrtd_dev *wrtd,
const char *rep_cap_id)
/**
* Reset all statistics for the given rule. The Rule must not be enabled.
*
* @param[in] wrtd Device token.
* @param[in] rep_cap_id Name of the Rule to reset its statistics.
* @return #wrtd_status
*/
wrtd_status wrtd_reset_rule_stats(wrtd_dev *wrtd,
const char *rep_cap_id)
{
enum wrtd_status status;
wrtd_status status;
unsigned idx;
struct wrtd_rule *rule;
......
......@@ -18,7 +18,7 @@ extern "C" {
#include "wrtd-common.h"
// NOTE: corresponds roughly to IVI ViSession
struct wrtd_dev;
typedef struct wrtd_dev wrtd_dev;
/**
* @enum wrtd_status
......@@ -28,7 +28,7 @@ struct wrtd_dev;
* the #wrtd_error_message function. The latest error can
* be retrieved with the #wrtd_get_error function.
*/
enum wrtd_status {
typedef enum wrtd_status {
/** No error. */
WRTD_SUCCESS = 0,
......@@ -86,14 +86,14 @@ enum wrtd_status {
/** always last entry in this enum */
__WRTD_ERROR_MAX_NUMBER,
};
} wrtd_status;
/**
* @enum wrtd_attr
* White Rabbit Trigger Distribution attributes.
* Names and values inspired by IVI-3.2 and IVI-3.15.
*
* Attributes can be of type `bool`, `int32`, `string`, or `timestamp`.
* Attributes can be of type `bool`, `int32`, `string`, or `tstamp` (#wrtd_tstamp).
* Access can be `RW` (read/write), `RO` (read-only) or `WO` (write-only).
* Furthermore an attribute can be `local` (related to a specific Alarm/Rule)
* or `global` (it applies to the whole device).
......@@ -109,13 +109,15 @@ enum wrtd_status {
* - #wrtd_set_attr_tstamp
* - #wrtd_get_attr_tstamp
*/
enum wrtd_attr {
typedef enum wrtd_attr {
/** Same as *IVI_INSTR_SPECIFIC_ATTR_BASE*. */
__WRTD_ATTR_BASE = 1150000,
/** `RO` `int32` `global` Major part of WRTD version supported by the device. */
/** `RO` `int32` `global` Major part of WRTD version supported by the device.
It must be equal to the major version of the WRTD library. */
WRTD_MAJOR_VERSION = __WRTD_ATTR_BASE + 0x00,
/** `RO` `int32` `global` Minor part of WRTD version supported by the device. */
/** `RO` `int32` `global` Minor part of WRTD version supported by the device.
It must be less than or equal to the minor version of the WRTD library. */
WRTD_MINOR_VERSION = __WRTD_ATTR_BASE + 0x01,
/** `RO` `bool` `global` True if the Event Log is empty. */
WRTD_ATTR_EVENT_LOG_EMPTY = __WRTD_ATTR_BASE + 0x02,
......@@ -123,184 +125,221 @@ enum wrtd_attr {
WRTD_ATTR_EVENT_LOG_ENABLED = __WRTD_ATTR_BASE + 0x03,
/** `RO` `bool` `global` True if the device is synchronized to White Rabbit time. */
WRTD_ATTR_IS_TIME_SYNCHRONIZED = __WRTD_ATTR_BASE + 0x04,
/** `RO` `tstamp` `global` Current system time. */
WRTD_ATTR_SYS_TIME = __WRTD_ATTR_BASE + 0x05,
/** `RO` `int32` `global` Number of defined Alarms. */
WRTD_ATTR_ALARM_COUNT = __WRTD_ATTR_BASE + 0x05,
WRTD_ATTR_ALARM_COUNT = __WRTD_ATTR_BASE + 0x10,
/** `RW` `bool` `local` Enable/disable an Alarm. */
WRTD_ATTR_ALARM_ENABLED = __WRTD_ATTR_BASE + 0x06,
WRTD_ATTR_ALARM_ENABLED = __WRTD_ATTR_BASE + 0x11,
/** `RW` `tstamp` `local` Specifies at what time to trigger an Alarm. */
WRTD_ATTR_ALARM_TIME = __WRTD_ATTR_BASE + 0x07,
WRTD_ATTR_ALARM_TIME = __WRTD_ATTR_BASE + 0x12,
/** `RW` `tstamp` `local` Specifies the Alarm period. 0 means no repetitions. */
WRTD_ATTR_ALARM_PERIOD = __WRTD_ATTR_BASE + 0x08,
WRTD_ATTR_ALARM_PERIOD = __WRTD_ATTR_BASE + 0x13,
/** `RW` `int32` `local` Specifies the number of times an Alarm will occur at the
period specified by #WRTD_ATTR_ALARM_PERIOD, before becoming automatically disabled.
0 means infinite repetitions. When read, it returns the remaining repetitions. */
WRTD_ATTR_ALARM_REPEAT_COUNT = __WRTD_ATTR_BASE + 0x09,
WRTD_ATTR_ALARM_REPEAT_COUNT = __WRTD_ATTR_BASE + 0x14,
/** `RO` `int32` `global` Number of defined Rules. */
WRTD_ATTR_RULE_COUNT = __WRTD_ATTR_BASE + 0x0A,
WRTD_ATTR_RULE_COUNT = __WRTD_ATTR_BASE + 0x20,
/** `RW` `bool` `local` Enable/disable a Rule. */
WRTD_ATTR_RULE_ENABLED = __WRTD_ATTR_BASE + 0x0B,
WRTD_ATTR_RULE_ENABLED = __WRTD_ATTR_BASE + 0x21,
/** `RW` `int32` `local` Specifies the number of times a Rule will fire before
becoming automatically disabled. 0 means infinite. When read, it returns the
remaining repetitions. */
WRTD_ATTR_RULE_REPEAT_COUNT = __WRTD_ATTR_BASE + 0x0C,
WRTD_ATTR_RULE_REPEAT_COUNT = __WRTD_ATTR_BASE + 0x22,
/** `RW` `string` `local` Get/set Rule source. Rule sources can be:
- Local source Event IDs (in the form of **LCI-x**)
- Alarm IDs (any ID with an **alarm** prefix)
- Any other string which will be interpreted as a network message Event ID. */
WRTD_ATTR_RULE_SOURCE = __WRTD_ATTR_BASE + 0x0D,
WRTD_ATTR_RULE_SOURCE = __WRTD_ATTR_BASE + 0x23,
/** `RW` `string` `local` Get/set Rule destinations. Rule destinations can be:
- Local destination Event IDs (in the form of **LCO-x**)
- Any other string which will be interpreted as a network message Event ID. */
WRTD_ATTR_RULE_DESTINATION = __WRTD_ATTR_BASE + 0x0E,
WRTD_ATTR_RULE_DESTINATION = __WRTD_ATTR_BASE + 0x24,
/** `RW` `bool` `local` If true, events that arrive late (with a timestamp in
the past) will still be executed; otherwise they are logged and discarded. */
WRTD_ATTR_RULE_SEND_LATE = __WRTD_ATTR_BASE + 0x0F,
WRTD_ATTR_RULE_SEND_LATE = __WRTD_ATTR_BASE + 0x25,
/** `RW` `tstamp` `local` Specifies the delay to add to the timestamp of the source
Event before forwarding it to its destination. The provided value must be less
than 1 second. */
WRTD_ATTR_RULE_DELAY = __WRTD_ATTR_BASE + 0x10,
WRTD_ATTR_RULE_DELAY = __WRTD_ATTR_BASE + 0x26,
/** `RW` `tstamp` `local` Specifies the "dead time" between two source Events. Any
new event that arrives during this time, will be logged and discarded. The provided
value must be less than 1 second. */
WRTD_ATTR_RULE_HOLDOFF = __WRTD_ATTR_BASE + 0x11,
WRTD_ATTR_RULE_HOLDOFF = __WRTD_ATTR_BASE + 0x27,
/** `RW` `tstamp` `local` Re-align the source Event timestamp to a multiple
of this value. As an example, if the Event timestamp is **00:00:12.000.123.456**
and RESYNC_PERIOD is **00:00:00.000.005.000** (5us), the Event timestamp will
be re-aligned to **00:00:12.000.125.000**. This calculation is done after applying
the delay defined by #WRTD_ATTR_RULE_DELAY. The provided value must be less than
1 second. */
WRTD_ATTR_RULE_RESYNC_PERIOD = __WRTD_ATTR_BASE + 0x12,
WRTD_ATTR_RULE_RESYNC_PERIOD = __WRTD_ATTR_BASE + 0x28,
/** `RW` `int32` `local` Further re-align the source Event timestamp to a multiple
of #WRTD_ATTR_RULE_RESYNC_PERIOD. As an example, if RESYNC_PERIOD
is 5us and the provided value is 10, 50us will be added to the Event timestamp,
after applying #WRTD_ATTR_RULE_DELAY and re-aligning it to the RESYNC_PERIOD. */
WRTD_ATTR_RULE_RESYNC_FACTOR = __WRTD_ATTR_BASE + 0x13,
WRTD_ATTR_RULE_RESYNC_FACTOR = __WRTD_ATTR_BASE + 0x29,
/** `RO` `int32` `local` Number of received Events for a Rule. */
WRTD_ATTR_STAT_RULE_RX_EVENTS = __WRTD_ATTR_BASE + 0x14,
WRTD_ATTR_STAT_RULE_RX_EVENTS = __WRTD_ATTR_BASE + 0x30,
/** `RO` `tstamp` `local` Timestamp of most recently received Event for a Rule. */
WRTD_ATTR_STAT_RULE_RX_LAST = __WRTD_ATTR_BASE + 0x15,
WRTD_ATTR_STAT_RULE_RX_LAST = __WRTD_ATTR_BASE + 0x31,
/** `RO` `int32` `local` Number of transmitted Events for a Rule. */
WRTD_ATTR_STAT_RULE_TX_EVENTS = __WRTD_ATTR_BASE + 0x16,
WRTD_ATTR_STAT_RULE_TX_EVENTS = __WRTD_ATTR_BASE + 0x32,
/** `RO` `tstamp` `local` Timestamp of most recently transmitted Event for a Rule. */
WRTD_ATTR_STAT_RULE_TX_LAST = __WRTD_ATTR_BASE + 0x17,
WRTD_ATTR_STAT_RULE_TX_LAST = __WRTD_ATTR_BASE + 0x33,
/** `RO` `int32` `local` Number of received Events that arrived too late.
See also #WRTD_ATTR_RULE_SEND_LATE. */
WRTD_ATTR_STAT_RULE_MISSED_LATE = __WRTD_ATTR_BASE + 0x18,
WRTD_ATTR_STAT_RULE_MISSED_LATE = __WRTD_ATTR_BASE + 0x34,
/** `RO` `int32` `local` Number of received Events that arrived during hold-off.
See also #WRTD_ATTR_RULE_HOLDOFF. */
WRTD_ATTR_STAT_RULE_MISSED_HOLDOFF = __WRTD_ATTR_BASE + 0x19,
WRTD_ATTR_STAT_RULE_MISSED_HOLDOFF = __WRTD_ATTR_BASE + 0x35,
/** `RO` `int32` `local` Number of received Events that were discarded because the
device was not synchronized to White Rabbit.
See also #WRTD_ATTR_IS_TIME_SYNCHRONIZED. */
WRTD_ATTR_STAT_RULE_MISSED_NOSYNC = __WRTD_ATTR_BASE + 0x1A,
WRTD_ATTR_STAT_RULE_MISSED_NOSYNC = __WRTD_ATTR_BASE + 0x36,
/** `RO` `int32` `local` Number of received Events that were discarded because of
internal buffer overflows. This may happen if the Event rate is too high. */
WRTD_ATTR_STAT_RULE_MISSED_OVERFLOW = __WRTD_ATTR_BASE + 0x1B,
WRTD_ATTR_STAT_RULE_MISSED_OVERFLOW = __WRTD_ATTR_BASE + 0x37,
/** `RO` `tstamp` `local` Timestamp of most recently missed Event. */
WRTD_ATTR_STAT_RULE_MISSED_LAST = __WRTD_ATTR_BASE + 0x1C,
WRTD_ATTR_STAT_RULE_MISSED_LAST = __WRTD_ATTR_BASE + 0x38,
/** `RO` `tstamp` `local` Minimum latency between the Event timestamp and its
reception by WRTD. */
WRTD_ATTR_STAT_RULE_RX_LATENCY_MIN = __WRTD_ATTR_BASE + 0x1D,
reception by WRTD. */
WRTD_ATTR_STAT_RULE_RX_LATENCY_MIN = __WRTD_ATTR_BASE + 0x39,
/** `RO` `tstamp` `local` Maximum latency between the Event timestamp and its
reception by WRTD. */
WRTD_ATTR_STAT_RULE_RX_LATENCY_MAX = __WRTD_ATTR_BASE + 0x1E,
reception by WRTD. */
WRTD_ATTR_STAT_RULE_RX_LATENCY_MAX = __WRTD_ATTR_BASE + 0x3A,
/** `RO` `tstamp` `local` Average latency between the Event timestamp and its
reception by WRTD. */
WRTD_ATTR_STAT_RULE_RX_LATENCY_AVG = __WRTD_ATTR_BASE + 0x1F,
reception by WRTD. */
WRTD_ATTR_STAT_RULE_RX_LATENCY_AVG = __WRTD_ATTR_BASE + 0x3B,
/** `RO` `tstamp` `local` Average period for source Events. */
WRTD_ATTR_STAT_RULE_RX_PERIOD_AVG = __WRTD_ATTR_BASE + 0x20,
WRTD_ATTR_STAT_RULE_RX_PERIOD_AVG = __WRTD_ATTR_BASE + 0x3C,
/** Always last entry in this enum */
__WRTD_ATTR_MAX_NUMBER,
};
}wrtd_attr;
/** A repeated capability identifier for global attributes. */
#define WRTD_GLOBAL_REP_CAP_ID "WGRCI"
/** Size (in characters, including null termination) of an event log enty. */
#define WRTD_LOG_ENTRY_SIZE 91
/* ------------------------------------------------------------------- */
/* Function prototypes for the official WRTD API. Documented in wrtd.c */
enum wrtd_status wrtd_init(const char *resource_name,
bool reset,
const char *options_str,
struct wrtd_dev **wrtd);
enum wrtd_status wrtd_close(struct wrtd_dev *wrtd);
enum wrtd_status wrtd_reset(struct wrtd_dev *wrtd);
enum wrtd_status wrtd_get_error(struct wrtd_dev *wrtd,
enum wrtd_status *error_code,
int32_t error_description_buffer_size,
char *error_description);
enum wrtd_status wrtd_error_message(struct wrtd_dev *wrtd,
enum wrtd_status err_code,
char *err_message);
enum wrtd_status wrtd_set_attr_bool(struct wrtd_dev *wrtd,
const char *rep_cap_id,
enum wrtd_attr id,
bool value);
enum wrtd_status wrtd_get_attr_bool(struct wrtd_dev *wrtd,
const char *rep_cap_id,
enum wrtd_attr id,
bool *value);
enum wrtd_status wrtd_set_attr_int32(struct wrtd_dev *wrtd,
const char *rep_cap_id,
enum wrtd_attr id,
int32_t value);
enum wrtd_status wrtd_get_attr_int32(struct wrtd_dev *wrtd,
const char *rep_cap_id,
enum wrtd_attr id,
int32_t *value);
enum wrtd_status wrtd_set_attr_string(struct wrtd_dev *wrtd,
const char *rep_cap_id,
enum wrtd_attr id,
const char *value);
enum wrtd_status wrtd_get_attr_string(struct wrtd_dev *wrtd,
const char *rep_cap_id,
enum wrtd_attr id,
int32_t value_buf_size,
char *value);
enum wrtd_status wrtd_set_attr_tstamp(struct wrtd_dev *wrtd,
const char *rep_cap_id,
enum wrtd_attr id,
const struct wrtd_tstamp *value);
enum wrtd_status wrtd_get_attr_tstamp(struct wrtd_dev *wrtd,
const char *rep_cap_id,
enum wrtd_attr id,
struct wrtd_tstamp *value);
enum wrtd_status wrtd_get_sys_time(struct wrtd_dev *wrtd,
struct wrtd_tstamp *time);
enum wrtd_status wrtd_clear_event_log_entries(struct wrtd_dev *wrtd);
enum wrtd_status wrtd_get_next_event_log_entry(struct wrtd_dev *wrtd,
int32_t log_entry_buffer_size,
char *log_entry);
enum wrtd_status wrtd_add_alarm(struct wrtd_dev *wrtd,
const char *rep_cap_id);
enum wrtd_status wrtd_disable_all_alarms(struct wrtd_dev *wrtd);
enum wrtd_status wrtd_remove_alarm(struct wrtd_dev *wrtd,
const char *rep_cap_id);
enum wrtd_status wrtd_remove_all_alarms(struct wrtd_dev *wrtd);
enum wrtd_status wrtd_get_alarm_name(struct wrtd_dev *wrtd,
int32_t index,
int32_t name_buffer_size,
char *rep_cap_id);
enum wrtd_status wrtd_add_rule(struct wrtd_dev *wrtd,
const char *rep_cap_id);
enum wrtd_status wrtd_disable_all_rules(struct wrtd_dev *wrtd);
enum wrtd_status wrtd_remove_rule(struct wrtd_dev *wrtd,
/* ------------------------------------------------------------------- */
/* Initialisation */
wrtd_status wrtd_init(const char *resource_name,
bool reset,
const char *options_str,
wrtd_dev **wrtd);
wrtd_status wrtd_close(wrtd_dev *wrtd);
wrtd_status wrtd_reset(wrtd_dev *wrtd);
/* Errors */
wrtd_status wrtd_get_error(wrtd_dev *wrtd,
wrtd_status *error_code,
int32_t error_description_buffer_size,
char *error_description);
wrtd_status wrtd_error_message(wrtd_dev *wrtd,
wrtd_status err_code,
char *err_message);
/* Attributes */
wrtd_status wrtd_set_attr_bool(wrtd_dev *wrtd,
const char *rep_cap_id,
wrtd_attr id,
bool value);
wrtd_status wrtd_get_attr_bool(wrtd_dev *wrtd,
const char *rep_cap_id,
wrtd_attr id,
bool *value);
wrtd_status wrtd_set_attr_int32(wrtd_dev *wrtd,
const char *rep_cap_id,
wrtd_attr id,
int32_t value);
wrtd_status wrtd_get_attr_int32(wrtd_dev *wrtd,
const char *rep_cap_id,
wrtd_attr id,
int32_t *value);
wrtd_status wrtd_set_attr_string(wrtd_dev *wrtd,
const char *rep_cap_id,
wrtd_attr id,
const char *value);
wrtd_status wrtd_get_attr_string(wrtd_dev *wrtd,
const char *rep_cap_id,
wrtd_attr id,
int32_t value_buf_size,
char *value);
wrtd_status wrtd_set_attr_tstamp(wrtd_dev *wrtd,
const char *rep_cap_id,
wrtd_attr id,
const wrtd_tstamp *value);
wrtd_status wrtd_get_attr_tstamp(wrtd_dev *wrtd,
const char *rep_cap_id,
wrtd_attr id,
wrtd_tstamp *value);
/* Event Log */
wrtd_status wrtd_clear_event_log_entries(wrtd_dev *wrtd);
wrtd_status wrtd_get_next_event_log_entry(wrtd_dev *wrtd,
int32_t log_entry_buffer_size,
char *log_entry);
/* Alarms */
wrtd_status wrtd_add_alarm(wrtd_dev *wrtd,
const char *rep_cap_id);
wrtd_status wrtd_disable_all_alarms(wrtd_dev *wrtd);
wrtd_status wrtd_remove_alarm(wrtd_dev *wrtd,
const char *rep_cap_id);
wrtd_status wrtd_remove_all_alarms(wrtd_dev *wrtd);
wrtd_status wrtd_get_alarm_name(wrtd_dev *wrtd,
int32_t index,
int32_t name_buffer_size,
char *name);
/* Rules */
wrtd_status wrtd_add_rule(wrtd_dev *wrtd,
const char *rep_cap_id);
wrtd_status wrtd_disable_all_rules(wrtd_dev *wrtd);
wrtd_status wrtd_remove_rule(wrtd_dev *wrtd,
const char *rep_cap_id);
wrtd_status wrtd_remove_all_rules(wrtd_dev *wrtd);
wrtd_status wrtd_get_rule_name(wrtd_dev *wrtd,
int32_t index,
int32_t name_buffer_size,
char *name);
wrtd_status wrtd_reset_rule_stats(wrtd_dev *wrtd,
const char *rep_cap_id);
enum wrtd_status wrtd_remove_all_rules(struct wrtd_dev *wrtd);
enum wrtd_status wrtd_get_rule_name(struct wrtd_dev *wrtd,
int32_t index,
int32_t name_buffer_size,
char *rep_cap_id);
enum wrtd_status wrtd_reset_rule_stats(struct wrtd_dev *wrtd,
const char *rep_cap_id);
#ifdef __cplusplus
};
......
/*
* Copyright (C) 2014-2016 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@cern.ch>
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
/**
* @file wrtd-config.c
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
* Copyright (c) 2018-2019 CERN (home.cern)
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
#include <stdint.h>
......@@ -118,11 +107,11 @@ static void disp_rule_conf(const struct wrtd_rule *rule, unsigned nbr_rules)
disp_id(cfg->dest_id);
printf("\n"
" dest_cpu: %u", cfg->dest_cpu);
if (cfg->dest_cpu == WRTD_DEST_CPU_LOCAL)
printf(" (local)");
else if (cfg->dest_cpu == WRTD_DEST_CH_NET)
printf(" (net)");
printf("\n");
if (cfg->dest_cpu == WRTD_DEST_CPU_LOCAL)
printf(" (local)");
else if (cfg->dest_cpu == WRTD_DEST_CH_NET)
printf(" (net)");
printf("\n");
printf(" dest_ch: %u\n", cfg->dest_ch);
printf(" enabled: %d\n", cfg->enabled);
printf(" hash: %u, hash_chain: %d\n",
......@@ -367,7 +356,8 @@ static enum wrtd_status wrtd_cmd_sys_time(struct wrtd_dev *wrtd,
struct wrtd_tstamp t;
enum wrtd_status res;
res = wrtd_get_sys_time(wrtd, &t);
res = wrtd_get_attr_tstamp(wrtd, WRTD_GLOBAL_REP_CAP_ID,
WRTD_ATTR_SYS_TIME, &t);
WRTD_RETURN_IF_ERROR(res);
print_ts(&t);
......@@ -481,7 +471,8 @@ static enum wrtd_status wrtd_cmd_set_alarm(struct wrtd_dev *wrtd,
/* Set time. */
status = wrtd_get_sys_time(wrtd, &ts);
status = wrtd_get_attr_tstamp(wrtd, WRTD_GLOBAL_REP_CAP_ID,
WRTD_ATTR_SYS_TIME, &ts);
WRTD_RETURN_IF_ERROR(status);
ts_add_ps(&ts, delay);
......@@ -700,7 +691,7 @@ static enum wrtd_status wrtd_cmd_enable_rule(struct wrtd_dev *wrtd,
}
static enum wrtd_status wrtd_cmd_disable_rule(struct wrtd_dev *wrtd,
int argc, char *argv[])
int argc, char *argv[])
{
return wrtd_cmd_enable_disable_rule(wrtd, argc, argv, false);
}
......@@ -866,78 +857,78 @@ static struct wrtd_commands cmds[] = {
{ "clear-log", "", "clear pending log entries",
wrtd_cmd_clear_log },
#if 0
{ "state", "", "shows input state",
wrtd_cmd_state },
{ "enable", "", "enable the input",
wrtd_cmd_enable },
{ "disable", "", "disable the input",
wrtd_cmd_disable },
{ "deadtime", "<number>", "sets the dead time in pico-seconds",
wrtd_cmd_set_dead_time },
{ "delay", "<number>", "sets the input delay in pico-seconds",
wrtd_cmd_set_delay },
{ "mode", "<mode>", "sets triggering mode (see Trigger Modes)",
wrtd_cmd_set_mode },
{ "assign", "<trig-id>", "assigns a trigger (see Trigger ID)",
wrtd_cmd_assign },
{ "unassign", "", "un-assigns the currently assigned trigger",
wrtd_cmd_unassign },
{ "arm", "", "arms the input",
wrtd_cmd_arm },
{ "disarm", "", "disarms the input",
wrtd_cmd_disarm },
{ "reset", "", "resets statistics counters",
wrtd_cmd_reset },
{ "global_reset", "", "resets global statistics counters",
wrtd_cmd_global_reset },
{ "swtrig", "", "sends a software trigger",
wrtd_cmd_sw_trigger },
{ "log_level", "<level>", "set logging level (see Log Levels)",
wrtd_cmd_log_level },
{ "state", "", "shows input state",
wrtd_cmd_state },
{ "enable", "", "enable the input",
wrtd_cmd_enable },
{ "disable", "", "disable the input",
wrtd_cmd_disable },
{ "deadtime", "<number>", "sets the dead time in pico-seconds",
wrtd_cmd_set_dead_time },
{ "delay", "<number>", "sets the input delay in pico-seconds",
wrtd_cmd_set_delay },
{ "mode", "<mode>", "sets triggering mode (see Trigger Modes)",
wrtd_cmd_set_mode },
{ "assign", "<trig-id>", "assigns a trigger (see Trigger ID)",
wrtd_cmd_assign },
{ "unassign", "", "un-assigns the currently assigned trigger",
wrtd_cmd_unassign },
{ "arm", "", "arms the input",
wrtd_cmd_arm },
{ "disarm", "", "disarms the input",
wrtd_cmd_disarm },
{ "reset", "", "resets statistics counters",
wrtd_cmd_reset },
{ "global_reset", "", "resets global statistics counters",
wrtd_cmd_global_reset },
{ "swtrig", "", "sends a software trigger",
wrtd_cmd_sw_trigger },
{ "log_level", "<level>", "set logging level (see Log Levels)",
wrtd_cmd_log_level },
#endif
{ NULL }
{ NULL }
};
static void help(void)
{
fprintf(stderr, "wrtd-config -D DEVICE -- COMMAND [cmd-options]\n\n");
fprintf(stderr, "It configures a White-Rabbit Trigger-Distribution node\n\n");
fprintf(stderr, "-D\tdevice id\n");
help_commands(cmds);
exit(1);
fprintf(stderr, "wrtd-config -D DEVICE -- COMMAND [cmd-options]\n\n");
fprintf(stderr, "It configures a White-Rabbit Trigger-Distribution node\n\n");
fprintf(stderr, "-D\tdevice id\n");
help_commands(cmds);
exit(1);
}
int main(int argc, char *argv[])
{
struct wrtd_dev *wrtd;
struct wrtd_dev *wrtd;
const char *dev_name = NULL;
enum wrtd_status status;
char *cmd = NULL;
char *cmd = NULL;
char c;
int i;
char error_description[256];
enum wrtd_status error_code;
while ((c = getopt (argc, argv, "+hD:c:")) != -1) {
switch (c) {
case 'h':
case '?':
help();
break;
case 'D':
int i;
char error_description[256];
enum wrtd_status error_code;
while ((c = getopt (argc, argv, "+hD:c:")) != -1) {
switch (c) {
case 'h':
case '?':
help();
break;
case 'D':
dev_name = optarg;
break;
}
}
break;
}
}
if (dev_name == NULL || optind == argc) {
help();
exit(1);
}
if (dev_name == NULL || optind == argc) {
help();
exit(1);
}
/* Find command. */
cmd = argv[optind++];
for (i = 0; cmds[i].handler; i++) {
for (i = 0; cmds[i].handler; i++) {
if(!strcmp(cmds[i].name, cmd))
break;
}
......@@ -947,13 +938,13 @@ int main(int argc, char *argv[])
}
/* Open. */
status = wrtd_init(dev_name, 0, NULL, &wrtd);
if (status != WRTD_SUCCESS) {
fprintf(stderr,
status = wrtd_init(dev_name, 0, NULL, &wrtd);
if (status != WRTD_SUCCESS) {
fprintf(stderr,
"Cannot open WRTD %s, status=0x%08x (errno=%s)\n",
dev_name, status, strerror(errno));
return 1;
}
}
status = cmds[i].handler(wrtd, argc - optind, argv + optind);
if (status != WRTD_SUCCESS) {
......@@ -963,7 +954,7 @@ int main(int argc, char *argv[])
cmd, error_description);
}
wrtd_close(wrtd);
wrtd_close(wrtd);
return 0;
}
/*
* Copyright (C) 2014-2016 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@cern.ch>
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
/**
* @file wrtd-inout-common.c
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
* Copyright (c) 2018-2019 CERN (home.cern)
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
#include <stdint.h>
......@@ -46,137 +35,137 @@ void print_ts(const struct wrtd_tstamp *ts)
void help_commands(struct wrtd_commands *cmds)
{
int i;
int i;
fprintf(stderr, "Available commands:\n");
for(i = 0; cmds[i].handler; i++) {
fprintf(stderr, " %s %s\n\t%s\n\n",
cmds[i].name, cmds[i].parm, cmds[i].desc);
}
fprintf(stderr, "Available commands:\n");
for(i = 0; cmds[i].handler; i++) {
fprintf(stderr, " %s %s\n\t%s\n\n",
cmds[i].name, cmds[i].parm, cmds[i].desc);
}
}
#if 0
void help_log_level()
{
fprintf(stderr, "Log Levels\n");
fprintf(stderr, "You can set more than one log level. Here the list of valid log level strings:\n\n");
fprintf(stderr, "\toff, Raw, Sent, Promiscious, Executed, Missed.\n\n");
fprintf(stderr, "For details about their meaning refer, for example, to the library documentation.\n\n");
fprintf(stderr, "Log Levels\n");
fprintf(stderr, "You can set more than one log level. Here the list of valid log level strings:\n\n");
fprintf(stderr, "\toff, Raw, Sent, Promiscious, Executed, Missed.\n\n");
fprintf(stderr, "For details about their meaning refer, for example, to the library documentation.\n\n");
}
void help_trig_mode()
{
fprintf(stderr, "Trigger Modes\n");
fprintf(stderr, "You can active only one trigger mode at time. Following the list of valid trigger mode strings:\n\n");
fprintf(stderr, "\tauto, single\n\n");
fprintf(stderr, "For details about their meaning refer, for example, to the library documentation.\n\n");
fprintf(stderr, "Trigger Modes\n");
fprintf(stderr, "You can active only one trigger mode at time. Following the list of valid trigger mode strings:\n\n");
fprintf(stderr, "\tauto, single\n\n");
fprintf(stderr, "For details about their meaning refer, for example, to the library documentation.\n\n");
}
void help_trig_id()
{
fprintf(stderr, "Trigger ID\n");
fprintf(stderr, "The trigger Id is made of 3 number separated by a colon\n\n");
fprintf(stderr, "\t<number>:<number>:<number>\n\n");
fprintf(stderr, "Looking at them from their semantic point of view:\n\n");
fprintf(stderr, "\t<system>:<port>:<trigger>\n\n");
fprintf(stderr, "For details about their meaning refer, for example, to the library documentation.\n\n");
fprintf(stderr, "Trigger ID\n");
fprintf(stderr, "The trigger Id is made of 3 number separated by a colon\n\n");
fprintf(stderr, "\t<number>:<number>:<number>\n\n");
fprintf(stderr, "Looking at them from their semantic point of view:\n\n");
fprintf(stderr, "\t<system>:<port>:<trigger>\n\n");
fprintf(stderr, "For details about their meaning refer, for example, to the library documentation.\n\n");
}
void decode_flags(char *buf, uint32_t flags)
{
int l;
strcpy(buf,"");
if( flags & WRTD_ENABLED )
strcat(buf, "Enabled ");
if( flags & WRTD_TRIGGER_ASSIGNED )
strcat(buf, "TrigAssigned ");
if( flags & WRTD_LAST_VALID )
strcat(buf, "LastTimestampValid ");
if( flags & WRTD_ARMED )
strcat(buf, "Armed ");
if( flags & WRTD_TRIGGERED )
strcat(buf, "Triggered ");
if( flags & WRTD_NO_WR )
strcat(buf, "NoWRTiming ");
l = strlen(buf);
if(l)
buf[l-1] = 0;
int l;
strcpy(buf,"");
if( flags & WRTD_ENABLED )
strcat(buf, "Enabled ");
if( flags & WRTD_TRIGGER_ASSIGNED )
strcat(buf, "TrigAssigned ");
if( flags & WRTD_LAST_VALID )
strcat(buf, "LastTimestampValid ");
if( flags & WRTD_ARMED )
strcat(buf, "Armed ");
if( flags & WRTD_TRIGGERED )
strcat(buf, "Triggered ");
if( flags & WRTD_NO_WR )
strcat(buf, "NoWRTiming ");
l = strlen(buf);
if(l)
buf[l-1] = 0;
}
void decode_mode(char *buf, int mode)
{
switch(mode)
{
switch(mode)
{
case WRTD_TRIGGER_MODE_AUTO:
strcpy(buf, "Auto");
break;
strcpy(buf, "Auto");
break;
case WRTD_TRIGGER_MODE_SINGLE:
strcpy(buf, "Single shot");
break;
strcpy(buf, "Single shot");
break;
default:
strcpy(buf,"?");
break;
}
strcpy(buf,"?");
break;
}
}
void format_ts(char *buf, struct wr_timestamp ts, int with_seconds)
{
uint64_t picoseconds = (uint64_t) ts.ticks * 8000 + (uint64_t)ts.frac * 8000ULL / 4096ULL;
if(with_seconds)
{
sprintf (buf, "%llu:%03llu,%03llu,%03llu ns + %3llu ps",
(long long)(ts.seconds),
(picoseconds / (1000LL * 1000 * 1000)),
(picoseconds / (1000LL * 1000) % 1000),
(picoseconds / (1000LL) % 1000),
(picoseconds % 1000LL));
} else {
sprintf (buf, "%03llu,%03llu,%03llu ns + %3llu ps",
(picoseconds / (1000LL * 1000 * 1000)),
(picoseconds / (1000LL * 1000) % 1000),
(picoseconds / (1000LL) % 1000),
(picoseconds % 1000LL));
}
uint64_t picoseconds = (uint64_t) ts.ticks * 8000 + (uint64_t)ts.frac * 8000ULL / 4096ULL;
if(with_seconds)
{
sprintf (buf, "%llu:%03llu,%03llu,%03llu ns + %3llu ps",
(long long)(ts.seconds),
(picoseconds / (1000LL * 1000 * 1000)),
(picoseconds / (1000LL * 1000) % 1000),
(picoseconds / (1000LL) % 1000),
(picoseconds % 1000LL));
} else {
sprintf (buf, "%03llu,%03llu,%03llu ns + %3llu ps",
(picoseconds / (1000LL * 1000 * 1000)),
(picoseconds / (1000LL * 1000) % 1000),
(picoseconds / (1000LL) % 1000),
(picoseconds % 1000LL));
}
}
void format_ago(char *buf, struct wr_timestamp ts, struct wr_timestamp current)
{
uint64_t delta = current.seconds - ts.seconds;
char when[16];
if (delta < 0)
{
sprintf(when, "future");
delta = -delta;
} else {
sprintf(when, "past");
}
if(delta < 60)
sprintf(buf, "%lu seconds in the %s", delta, when);
else if (delta < 3600)
sprintf(buf, "%lu minutes in the %s", delta/60, when);
else if (delta < 3600*24)
sprintf(buf, "%lu hours in the %s", delta/3600, when);
else
sprintf(buf, "%lu days in the %s", delta/(24*3600), when);
uint64_t delta = current.seconds - ts.seconds;
char when[16];
if (delta < 0)
{
sprintf(when, "future");
delta = -delta;
} else {
sprintf(when, "past");
}
if(delta < 60)
sprintf(buf, "%lu seconds in the %s", delta, when);
else if (delta < 3600)
sprintf(buf, "%lu minutes in the %s", delta/60, when);
else if (delta < 3600*24)
sprintf(buf, "%lu hours in the %s", delta/3600, when);
else
sprintf(buf, "%lu days in the %s", delta/(24*3600), when);
}
void format_id(char *buf, struct wrtd_trig_id id)
{
sprintf( buf, "%04x:%04x:%08x", id.system, id.source_port,id.trigger);
sprintf( buf, "%04x:%04x:%08x", id.system, id.source_port,id.trigger);
}
uint64_t ts_to_picos(struct wr_timestamp ts)
{
return (uint64_t) ts.seconds * 1000LL * 1000 * 1000 * 1000
+ (uint64_t) ts.ticks * 8000ULL +
+ (uint64_t) ts.frac * 8000LL / 4096LL;
return (uint64_t) ts.seconds * 1000LL * 1000 * 1000 * 1000
+ (uint64_t) ts.ticks * 8000ULL +
+ (uint64_t) ts.frac * 8000LL / 4096LL;
}
#endif
......@@ -195,11 +184,11 @@ int parse_delay(char *dly, uint64_t *delay_ps)
mult=1;
switch(last) {
case 'm': mult = 1000ULL * 1000 * 1000; l--; break;
case 'u': mult = 1000ULL * 1000; l--; break;
case 'n': mult = 1000ULL; l--; break;
case 'p': mult = 1; l--; break;
default: mult = 1; break;
case 'm': mult = 1000ULL * 1000 * 1000; l--; break;
case 'u': mult = 1000ULL * 1000; l--; break;
case 'n': mult = 1000ULL; l--; break;
case 'p': mult = 1; l--; break;
default: mult = 1; break;
}
dly[l] = 0;
......@@ -269,19 +258,19 @@ void ts_sub_ps(struct wrtd_tstamp *ts, uint64_t ps)
#if 0
int parse_mode (char *mode_str, enum wrtd_trigger_mode *mode)
{
if(!strcmp(mode_str, "auto"))
*mode = WRTD_TRIGGER_MODE_AUTO;
else if(!strcmp(mode_str, "single"))
*mode = WRTD_TRIGGER_MODE_SINGLE;
else
return -1;
return 0;
if(!strcmp(mode_str, "auto"))
*mode = WRTD_TRIGGER_MODE_AUTO;
else if(!strcmp(mode_str, "single"))
*mode = WRTD_TRIGGER_MODE_SINGLE;
else
return -1;
return 0;
}
int parse_trigger_id(const char *str, struct wrtd_trig_id *id)
{
return (sscanf(str,"%x:%x:%x", &id->system, &id->source_port, &id->trigger) == 3 ? 0 : -1);
return (sscanf(str,"%x:%x:%x", &id->system, &id->source_port, &id->trigger) == 3 ? 0 : -1);
}
int parse_log_level (char *list[], int count, int *log_level)
......
/*
* Copyright (C) 2014-2016 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@cern.ch>
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
/**
* @file wrtd-internal.h
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* Copyright (c) 2018-2019 CERN (home.cern)
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
#ifndef __WRTD_TOOLS_INTERNAL_H__
......@@ -24,9 +13,9 @@
#include <libwrtd.h>
struct wrtd_commands {
const char *name;
const char *parm;
const char *desc;
const char *name;
const char *parm;
const char *desc;
enum wrtd_status (*handler)(struct wrtd_dev *wrtd,
int argc, char **argv);
};
......
/*
* Copyright (C) 2014-2016 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@cern.ch>
/**
* @file wrtd-logging.c
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
* Copyright (c) 2018-2019 CERN (home.cern)
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
#include <stdint.h>
......@@ -29,21 +19,20 @@
static void help(void)
{
fprintf(stderr, "wrtd-logging -D <device>\n");
fprintf(stderr, "It shows logging information coming from Real-Time applications\n");
fprintf(stderr, "-D device id\n");
fprintf(stderr, "-n number of messages to read (0 means infinite)\n");
exit(1);
fprintf(stderr, "wrtd-logging -D <device>\n");
fprintf(stderr, "It shows logging information coming from Real-Time applications\n");
fprintf(stderr, "-D device id\n");
fprintf(stderr, "-n number of messages to read (0 means infinite)\n");
exit(1);
}
static void print_logging (struct wrtd_dev *wrtd, int n_read)
{
char log_entry[WRTD_LOG_ENTRY_SIZE];
enum wrtd_status status;
unsigned int i;
for(i = 0; i < n_read || n_read == 0; i++) {
for(i = 0; i < n_read || n_read == 0; i++) {
do {
status = wrtd_get_next_event_log_entry(
wrtd, WRTD_LOG_ENTRY_SIZE, log_entry);
......@@ -63,48 +52,48 @@ static void print_logging (struct wrtd_dev *wrtd, int n_read)
int main(int argc, char *argv[])
{
struct wrtd_dev *wrtd;
struct wrtd_dev *wrtd;
const char *dev_name = NULL;
enum wrtd_status status;
char *endptr;
int n_read = 0;
char c;
int n_read = 0;
char c;
while ((c = getopt (argc, argv, "hD:n:")) != -1) {
switch (c) {
default:
help();
break;
case 'D':
while ((c = getopt (argc, argv, "hD:n:")) != -1) {
switch (c) {
default:
help();
break;
case 'D':
dev_name = optarg;
break;
case 'n':
n_read = strtoul(optarg, &endptr, 0);
if (*endptr != 0) {
fprintf(stderr, "bad value for -n");
exit (1);
}
break;
}
}
break;
case 'n':
n_read = strtoul(optarg, &endptr, 0);
if (*endptr != 0) {
fprintf(stderr, "bad value for -n");
exit (1);
}
break;
}
}
if (dev_name == NULL) {
help();
exit(1);
}
if (dev_name == NULL) {
help();
exit(1);
}
/* Open. */
status = wrtd_init(dev_name, 0, NULL, &wrtd);
if (status != WRTD_SUCCESS) {
status = wrtd_init(dev_name, 0, NULL, &wrtd);
if (status != WRTD_SUCCESS) {
char error_message[256];
enum wrtd_status error_status;
wrtd_get_error (wrtd, &error_status, 256, error_message);
fprintf(stderr, "Cannot open WRTD: %s\n", error_message);
return 1;
}
}
print_logging (wrtd, n_read);
print_logging (wrtd, n_read);
wrtd_close(wrtd);
exit(0);
wrtd_close(wrtd);
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