Commit fbfcd9e9 authored by Alessandro Rubini's avatar Alessandro Rubini

tools: new fmc-fdelay-pulse, to be tested more seriously

Signed-off-by: Alessandro Rubini's avatarAlessandro Rubini <rubini@gnudd.com>
parent d76cee5a
......@@ -1049,6 +1049,81 @@ In a future release we'll support reading concurrently from several
boards.
@c ==========================================================================
@node fmc-fdelay-pulse
@section fmc-fdelay-pulse
The program can be used to program one of the output channels to
output a sequence of pulses. It can parse the following command-line
options:
@table @code
@item -o <output>
Output channels are numbered 1 to 4, as written on the device panel.
Each command incocation can set only one output channel; the
last @t{-o} specified takes precedence.
@item -c <count>
Output repeat count: 0 is the default and means forever
@item -m <mode>
Output mode. Can be @t{pulse}, @t{delay} or @t{disable}.
@item -r <reltime>
Output pulse at a relative time in the future. The time is
a fraction of a second, specified as For pulse mode,
the program adds the current board time in seconds. Please note that
for pulse mode the second is relative, but the fraction is absolute.
So ``@t{-r 1.5}'' will activate aoutput at the middle of the
next second, according to board time. For @i{delay} mode, the
time is a relative delay.
@item -D <date>
Output pulse at a specified date. This is not currently
completed supported (no seconds are parsed, for example)
@c tom says: only seconds:nano
@c @item -f <frequency>
@c
@c Frequency of the output signal. A trailing @t{k} or @t{M}
@c means kilohertz or megaherts. Floating point is allowed
@c but internal approximation is not accounted for. So
@c @t{2.5M} will work, but 2.000001M will probably not.
@c The last frequency or period specified takes precedence.
@c Default frequency is 10Hz.
@item -T <period>
@itemx -w <width>
Period and width of the output signal. A trailing @t{m},
@t{u}, @t{n}, @t{p} means milli, micro, nano, pico, resp.
The parser supports additions and subtractions, e.g.
@t{50m-20n}.
The period defaults to 100ms and the width defaults to 8us
@item -t
Wait for the trigger to happen before returning. The boards reports
a trigger event when the requested pulse sequence is initiated,
either because the absolute time arrived or because an input
pulse was detected and the requested delay elapsed.
@item -p
@itemx -1
Pulse-per-seconds and 10MHz. These are shorthands setting many
parameters.
@item -v
Verbose: report action to stdout before telling the driver.
@end table
@c ##########################################################################
@node Troubleshooting
......
......@@ -2,3 +2,5 @@ fmc-fdelay-list
fmc-fdelay-term
fmc-fdelay-board-time
fmc-fdelay-input
fmc-fdelay-pulse
......@@ -10,6 +10,7 @@ hostprogs-y := fmc-fdelay-list
hostprogs-y += fmc-fdelay-term
hostprogs-y += fmc-fdelay-board-time
hostprogs-y += fmc-fdelay-input
hostprogs-y += fmc-fdelay-pulse
# we are not in the kernel, so we need to piggy-back on "make modules"
all modules: $(hostprogs-y)
......
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "fdelay-lib.h"
#include "tools-common.h"
static void help(char *name)
{
fprintf(stderr, "%s: Use \"%s [-i <index>] [-d <dev>] [<opts>]\n",
name, name);
fprintf(stderr, " options:\n"
" -o <output> ouput channel: 1..4 (default 1)\n"
" -c <count> default is 0 and means forever\n"
" -m <mode> \"pulse\" (default), \"delay\", \"disable\"\n"
" -r <reltime> relative time\n"
" -D <date> absolute time, <secs>:<nano>\n"
" -T <period> period, e.g. \"50m-20n\" -- use m,u,n,p and add/sub\n"
" -w <width> like period; defaults to 50%% period\n"
" -t wait for trigger before exiting\n"
" -p pulse per seconds (sets -D -T -w)\n"
" -1 10MHz (sets -D -T -w)\n"
" -v verbose (report action)\n");
exit(1);
}
struct fdelay_time t_width; /* save width here, add to start before acting */
/* This comes from oldtools/fdelay-pulse-tom.c, unchanged */
static void parse_time(char *s, struct fdelay_time *t)
{
int64_t time_ps = 0;
int64_t extra_seconds = 0;
int64_t sign = 1;
int64_t term = 0;
int64_t scale = 1;
const int64_t one_second = 1000000000000LL;
char c, *buf = s;
while ((c = *buf++) != 0) {
switch (c) {
case '+':
if (scale == one_second)
extra_seconds += sign * term;
else
time_ps += sign * term * scale;
term = 0;
sign = 1;
break;
case '-':
if (scale == one_second)
extra_seconds += sign * term;
else
time_ps += sign * term * scale;
term = 0;
sign = -1;
break;
case 's':
scale = one_second;
break;
case 'm':
scale = 1000000000LL;
break;
case 'u':
scale = 1000000LL;
break;
case 'n':
scale = 1000LL;
break;
case 'p':
scale = 1LL;
break;
default:
if (isdigit(c)) {
term *= 10LL;
term += (int64_t) (c - '0');
break;
} else {
fprintf(stderr,
"Error while parsing time string '%s'\n",
s);
exit(-1);
}
}
}
if (scale == one_second)
extra_seconds += sign * term;
else
time_ps += sign * term * scale;
while (time_ps < 0) {
time_ps += one_second;
extra_seconds--;
}
fdelay_pico_to_time((uint64_t *) & time_ps, t);
t->utc += extra_seconds;
if (0)
printf("dbg: raw %lld, %lld, converted: %lld s %d ns %d ps\n",
extra_seconds,time_ps, t->utc, t->coarse * 8,
t->frac * 8000 / 4096);
}
/* This comes from oldtools/fdelay-pulse-tom.c, unchanged */
static struct fdelay_time ts_add(struct fdelay_time a, struct fdelay_time b)
{
a.frac += b.frac;
if (a.frac >= 4096) {
a.frac -= 4096;
a.coarse++;
}
a.coarse += b.coarse;
if (a.coarse >= 125000000) {
a.coarse -= 125000000;
a.utc++;
}
a.utc += b.utc;
return a;
}
/*
* Some argument parsing is non-trivial, including setting
* the default. These helpers just return void and exit on error
*/
#define COARSE_PER_SEC (125 * 1000 * 1000)
void parse_default(struct fdelay_pulse *p)
{
memset(p, 0, sizeof(*p));
memset(&t_width, 0, sizeof(&t_width));
p->mode = FD_OUT_MODE_PULSE;
p->rep = -1; /* infinite */
/* Default settings are for 10Hz, 1us width */
p->loop.coarse = COARSE_PER_SEC / 10;
t_width.coarse = 125;
}
void parse_pps(struct fdelay_pulse *p)
{
parse_default(p);
t_width.coarse = COARSE_PER_SEC / 100; /* 10ms width */
p->loop.coarse = 0;
p->loop.utc = 1;
}
void parse_10mhz(struct fdelay_pulse *p)
{
parse_default(p);
t_width.coarse = 6 /* 48ns */;
p->loop.coarse = 12 /* 96ns */;
p->loop.frac = 2048 /* 4ns */;
}
void parse_reltime(struct fdelay_pulse *p, char *s)
{
memset(&p->start, 0, sizeof(p->start));
parse_time(s, &p->start);
}
void parse_abstime(struct fdelay_pulse *p, char *s)
{
memset(&p->start, 0, sizeof(p->start));
parse_time(s, &p->start);
}
void parse_period(struct fdelay_pulse *p, char *s)
{
memset(&p->loop, 0, sizeof(p->loop));
parse_time(s, &p->loop);
}
void parse_width(struct fdelay_pulse *p, char *s)
{
memset(&t_width, 0, sizeof(&t_width));
parse_time(s, &t_width);
}
int main(int argc, char **argv)
{
struct fdelay_board *b;
int nboards;
int i, opt, index = -1, dev = -1;
/* our parameters */
int count = 0, channel = 1;
int trigger_wait = 0, verbose = 0;
struct fdelay_pulse p;
/* Standard part of the file (repeated code) */
if (tools_need_help(argc, argv))
help(argv[0]);
nboards = fdelay_init();
if (nboards < 0) {
fprintf(stderr, "%s: fdelay_init(): %s\n", argv[0],
strerror(errno));
exit(1);
}
if (nboards == 0) {
fprintf(stderr, "%s: no boards found\n", argv[0]);
exit(1);
}
if (nboards == 1)
index = 0; /* so it works with no arguments */
parse_default(&p);
/* Parse our specific arguments */
while ((opt = getopt(argc, argv, "d:i:ho:c:m:r:D:T:w:tp1v")) != -1) {
switch (opt) {
char *rest;
case 'i':
index = strtol(optarg, &rest, 0);
if (rest && *rest) {
fprintf(stderr, "%s: Not a number \"%s\"\n",
argv[0], optarg);
exit(1);
}
break;
case 'd':
dev = strtol(optarg, &rest, 0);
if (rest && *rest) {
fprintf(stderr, "%s: Not a number \"%s\"\n",
argv[0], optarg);
exit(1);
}
break;
case 'h':
help(argv[0]);
case 'o':
channel = strtol(optarg, &rest, 0);
if (rest && *rest) {
fprintf(stderr, "%s: Not a number \"%s\"\n",
argv[0], optarg);
exit(1);
}
if (channel < 1 || channel > 4) {
fprintf(stderr, "%s: channel \"%s\" out of range\n",
argv[0], optarg);
exit(1);
}
break;
case 'c':
count = strtol(optarg, &rest, 0);
if (rest && *rest) {
fprintf(stderr, "%s: Not a number \"%s\"\n",
argv[0], optarg);
exit(1);
}
p.rep = count ? count : -1 /* infinite */;
break;
case 'm':
if (!strcmp(optarg, "disable"))
p.mode = FD_OUT_MODE_DISABLED;
else if (!strcmp(optarg, "pulse"))
p.mode = FD_OUT_MODE_PULSE;
else if (!strcmp(optarg, "delay"))
p.mode = FD_OUT_MODE_DELAY;
else {
fprintf(stderr, "%s: invalid mode \"%s\"\n",
argv[0], optarg);
exit(1);
}
break;
case 'r':
parse_reltime(&p, optarg);
break;
case 'D':
parse_abstime(&p, optarg);
break;
#if 0 /* no frequency */
case 'f':
parse_freq(&p, optarg);
break;
#endif
case 'T':
parse_period(&p, optarg);
break;
case 'w':
parse_width(&p, optarg);
break;
case 't':
trigger_wait = 1;
break;
case 'p':
parse_pps(&p);
break;
case '1':
parse_10mhz(&p);
break;
case 'v':
verbose = 1;
break;
}
}
if (optind != argc)
help(argv[0]); /* too many arguments */
if (index < 0 && dev < 0) {
fprintf(stderr, "%s: several boards, please pass -i or -d\n",
argv[0]);
exit(1);
}
b = fdelay_open(index, dev);
if (!b) {
fprintf(stderr, "%s: fdelay_open(): %s\n", argv[0],
strerror(errno));
exit(1);
}
/* Final fixes: if reltime in pulse mode, add current time */
if (p.mode == FD_OUT_MODE_PULSE && p.start.utc == 0) {
struct fdelay_time current_board_time;
fdelay_get_time(b, &current_board_time);
/* Start next second, or next again if too near to overlap */
p.start.utc = current_board_time.utc + 1;
if (current_board_time.coarse > COARSE_PER_SEC * 9 / 10)
p.start.utc++;
}
/* End is start + width, in every situation */
p.end = ts_add(p.start, t_width);
/* In delay mode, default is one pulse only; recover if wrong */
if (p.mode == FD_OUT_MODE_PULSE && p.rep <= 0)
p.rep = 1;
/* Done. Report verbosely and activate the information we parsed */
channel = FDELAY_OUTPUT_USER_TO_HW(channel);
if (verbose)
tools_report_action(channel, &p);
if (fdelay_config_pulse(b, channel, &p) < 0) {
fprintf(stderr, "%s: fdelay_config_pulse(): %s\n",
argv[0], strerror(errno));
exit(1);
}
while (trigger_wait) {
usleep(10 * 1000);
i = fdelay_has_triggered(b, channel);
if (i < 0) {
fprintf(stderr, "%s: waiting for trigger: %s\n",
argv[0], strerror(errno));
exit(1);
}
trigger_wait = !i;
}
fdelay_close(b);
fdelay_exit();
return 0;
}
......@@ -42,3 +42,26 @@ static inline int tools_need_help(int argc, char **argv)
return 1;
return 0;
}
static inline void report_time(char *name, struct fdelay_time *t)
{
printf(" %s utc %10lli, coarse %9li, frac %9li\n",
name, (long long)t->utc, (long)t->coarse, (long)t->frac);
}
static inline void tools_report_action(int channel, struct fdelay_pulse *p)
{
char *mode;
if (p->mode == FD_OUT_MODE_DISABLED) mode = "disable";
else if (p->mode == FD_OUT_MODE_PULSE) mode = "pulse";
else if (p->mode == FD_OUT_MODE_DELAY) mode = "delay";
else mode="--wrong-mode--";
printf("Channel %i, mode %s, repeat %i %s\n",
FDELAY_OUTPUT_HW_TO_USER(channel), mode,
p->rep, p->rep == -1 ? "(infinite)" : "");
report_time("start", &p->start);
report_time("end ", &p->end);
report_time("loop ", &p->loop);
}
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