Root/cntr/tools/cntr/cntr.c

1/*
2 * cntr/cntr.c - CNTR control tool
3 *
4 * Written 2010 by Werner Almesberger
5 * Copyright 2010 Werner Almesberger
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 */
12
13
14#include <stdlib.h>
15#include <stdio.h>
16#include <signal.h>
17#include <usb.h>
18#include <sys/time.h>
19
20#include "f32xbase/usb.h"
21#include "cntr/ep0.h"
22#include "cntr/usb-ids.h"
23
24
25#define FROM_DEV CNTR_FROM_DEV(0)
26#define TO_DEV CNTR_TO_DEV(0)
27
28#define DEFAULT_CLOCK_DEV_S 0.1 /* 100 ms, typ. NTP over WLAN dev. */
29#define BUF_SIZE 256
30
31
32static int debug = 0;
33static int verbose = 0;
34
35
36/* ----- CRC, shared with firmware ----------------------------------------- */
37
38
39/* crc32() */
40
41#include "cntr/crc32.c"
42
43
44/* ----- reset ------------------------------------------------------------- */
45
46
47static void reset_cntr(usb_dev_handle *dev)
48{
49    int res;
50
51    res = usb_control_msg(dev, TO_DEV, CNTR_RESET, 0, 0, NULL, 0, 1000);
52    if (res < 0) {
53        fprintf(stderr, "CNTR_RESET: %d\n", res);
54        exit(1);
55    }
56}
57
58
59/* ----- identify ---------------------------------------------------------- */
60
61
62static void identify_cntr(usb_dev_handle *dev)
63{
64    const struct usb_device *device = usb_device(dev);
65    uint8_t ids[3];
66    char buf[BUF_SIZE+1]; /* +1 for terminating \0 */
67    int res;
68
69    printf("%04x:%04x ",
70        device->descriptor.idVendor, device->descriptor.idProduct);
71
72    res = usb_control_msg(dev, FROM_DEV, CNTR_ID, 0, 0,
73        (char *) ids, sizeof(ids), 1000);
74    if (res < 0) {
75        fprintf(stderr, "CNTR_ID: %s\n", usb_strerror());
76        exit(1);
77    }
78
79    printf("protocol %u.%u hw %u\n", ids[0], ids[1], ids[2]);
80
81    res = usb_control_msg(dev, FROM_DEV, CNTR_BUILD, 0, 0,
82        buf, sizeof(buf), 1000);
83    if (res < 0) {
84        fprintf(stderr, "CNTR_BUILD: %s\n", usb_strerror());
85        exit(1);
86    }
87    buf[res] = 0;
88    printf("%10s%s\n", "", buf);
89}
90
91
92/* ---- packet reception --------------------------------------------------- */
93
94
95struct sample {
96    double t0, t1;
97    uint64_t cntr;
98};
99
100
101static unsigned packets = 0, crc_errors = 0, inv_errors = 0;
102
103
104static int get_sample(usb_dev_handle *dev, struct sample *s)
105{
106    static uint32_t last = 0, high = 0;
107    struct timeval t0, t1;
108    int res, bad;
109    uint8_t buf[12];
110    uint32_t cntr, inv, crc, expect;
111
112    gettimeofday(&t0, NULL);
113    res = usb_control_msg(dev, FROM_DEV, CNTR_READ, 0, 0,
114        (char *) buf, sizeof(buf), 1000);
115    gettimeofday(&t1, NULL);
116    if (res < 0) {
117        fprintf(stderr, "CNTR_READ: %s\n", usb_strerror());
118        exit(1);
119    }
120    packets++;
121    cntr = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
122    crc = buf[4] | (buf[5] << 8) | (buf[6] << 16) | (buf[7] << 24);
123    inv = buf[8] | (buf[9] << 8) | (buf[10] << 16) | (buf[11] << 24);
124    expect = crc32(cntr, ~0);
125    bad = 0;
126    if (crc != expect) {
127        if (verbose)
128            fprintf(stderr, "\nCRC error (count 0x%08x->0x%08x "
129                "CRC 0x%08x/0x%08x)\n",
130                (unsigned) last, (unsigned) cntr, (unsigned) crc,
131                (unsigned) expect);
132        bad = 1;
133        crc_errors++;
134    }
135    if (cntr != (inv ^ 0xffffffff)) {
136        if (verbose)
137            fprintf(stderr,
138                "\ninverted counter error (0x%08x->0x%08x, "
139                "inv 0x%08x)\n",
140                (unsigned) last, (unsigned) cntr, (unsigned) inv);
141        bad = 1;
142        inv_errors++;
143    }
144    if (bad)
145        return 0;
146    if (last > cntr)
147        high++;
148    last = cntr;
149    s->t0 = t0.tv_sec+t0.tv_usec/1000000.0;
150    s->t1 = t1.tv_sec+t1.tv_usec/1000000.0;
151    s->cntr = (uint64_t) high << 32 | cntr;
152    if (debug)
153        printf("0x%llx 0x%lx\n",
154            (unsigned long long ) s->cntr, (unsigned long) cntr);
155    return 1;
156}
157
158
159/* ---- SIGINT (Ctrl-C) ---------------------------------------------------- */
160
161
162static volatile int stop = 0;
163
164
165static void set_stop(int sig)
166{
167    stop = 1;
168}
169
170
171static void arm_stop(void)
172{
173    signal(SIGINT, set_stop);
174}
175
176
177/* ---- output ------------------------------------------------------------- */
178
179
180static void print_f(double f, int digits)
181{
182    char *f_exp;
183
184    if (f > 1000000.0) {
185        f /= 1000000.0;
186        f_exp = "M";
187    } else if (f > 1000.0) {
188        f /= 1000.0;
189        f_exp = "k";
190    } else {
191        f_exp = "";
192    }
193    printf("%1.*f %sHz", digits, f, f_exp);
194}
195
196
197/* ---- burst counter ------------------------------------------------------ */
198
199
200/*
201 * Here is when the various samples are taken:
202 *
203 * Activity --------XXXXXXXXXXXXX-------------
204 * ^ ^ ^^
205 * | | last||
206 * start stable now
207 * |<-t(idle)->|
208 *
209 * "start" is the sample before counter activity
210 * "stable" is the first sample after counter activity
211 * "last" is the sample immediately preceding "now"
212 * "now" is the sample currently being processed
213 *
214 * The count is printed if t(idle) >= timeout.
215 */
216
217static void count_bursts(usb_dev_handle *dev, double timeout)
218{
219    struct sample start, stable, now, last;
220    uint64_t dc, delta_n = 0;
221    double delta_sum = 0, dt;
222    arm_stop();
223
224    while (!get_sample(dev, &start));
225    stable = last = start;
226    while (!stop) {
227        while (!get_sample(dev, &now))
228            if (stop)
229                break;
230        delta_sum += now.t1-last.t1;
231        delta_n++;
232        last = now;
233        if (stable.cntr != now.cntr) {
234            stable = now;
235            continue;
236        }
237        if (now.t0-stable.t1 < timeout)
238            continue;
239        if (now.cntr != start.cntr) {
240            dc = now.cntr-start.cntr;
241            dt = stable.t1-start.t1-delta_sum/delta_n;
242            printf("%llu ~ ", (unsigned long long) dc);
243            if (dt > 0)
244                print_f(dc/dt, 3);
245            printf("\n");
246            fflush(stdout);
247        }
248        start = now;
249    }
250}
251
252
253/* ----- measurements ------------------------------------------------------ */
254
255
256static void measure(usb_dev_handle *dev, double clock_dev_s, double error_goal)
257{
258    struct sample start, now;
259    uint64_t dc;
260    double dt, f, error;
261    char *error_exp;
262    int i;
263
264    arm_stop();
265
266    /*
267     * The round-trip time for getting the first sample is one of the
268     * error terms. The smaller we can make it, the better. Thus, we try a
269     * few times to improve our first result.
270     */
271    while (!get_sample(dev, &start));
272    for (i = 0; i != 10; i++) {
273        while (!get_sample(dev, &now));
274        if (now.t1-now.t0 < start.t1-start.t0) {
275            if (debug)
276                fprintf(stderr, "improve %g -> %g\n",
277                    start.t1-start.t0, now.t1-now.t0);
278            start = now;
279        }
280    }
281    while (!stop) {
282        usleep(100000);
283        while (!get_sample(dev, &now))
284            if (stop)
285                break;
286        dc = now.cntr-start.cntr;
287        dt = (now.t0+now.t1)/2.0-(start.t0+start.t1)/2.0;
288        f = dc/dt;
289        if (dc)
290            error = 1.0/dc; /* one count */
291        else
292            error = 0;
293        error += (start.t1-start.t0)/dt/2.0; /* start sample read */
294        error += (now.t1-now.t0)/dt/2.0; /* last sample read */
295        error += clock_dev_s/dt; /* system clock dev. */
296        if (error >= 1) {
297            printf("\r(wait) ");
298            fflush(stdout);
299            continue;
300        }
301        if (dc && error <= error_goal)
302            stop = 1;
303
304        error_exp = "%";
305        error *= 100.0;
306        if (error < 0.1) {
307            error_exp = " ppm";
308            error *= 10000.0;
309            if (error < 1.0) {
310                error_exp = " ppb";
311                error *= 1000.0;
312            }
313        }
314        
315        printf("\r%6.1f ", dt);
316        print_f(f, 9);
317        printf(" %3.3f%s ", error, error_exp);
318        fflush(stdout);
319    }
320    printf(
321        "\n%llu counts, %u packets, %u CRC error%s, %u invert error%s\n",
322        (unsigned long long) (now.cntr-start.cntr),
323        packets, crc_errors, crc_errors == 1 ? "" : "s",
324        inv_errors, inv_errors == 1 ? "" : "s");
325}
326
327
328/* ----- command-line parsing ---------------------------------------------- */
329
330
331static void usage(const char *name)
332{
333    fprintf(stderr,
334"usage: %s [-c clock_dev] [-d] [-v] [accuracy_ppm]\n"
335"%6s %s -b [-d] [-v] [timeout_s]\n"
336"%6s %s -i\n"
337"%6s %s -r\n\n"
338" accuracy_ppm stop when specified accuracy is reached (default: never\n"
339" stop)\n"
340" timeout_s silence period between bursts, in seconds (default: 1s )\n"
341" -b count bursts separated by silence periods\n"
342" -c clock_dev maximum deviation of the system clock, in seconds\n"
343" (default: %g s)\n"
344" -d debug mode. Print counter values.\n"
345" -i identify the CNTR board\n"
346" -r reset the CNTR board\n"
347" -v verbose reporting of communication errors\n"
348    , name, "", name, "", name, "", name, DEFAULT_CLOCK_DEV_S);
349    exit(1);
350}
351
352
353int main(int argc, char *const *argv)
354{
355    usb_dev_handle *dev;
356    int c, burst = 0, identify = 0, reset = 0;
357    double clock_dev_s = DEFAULT_CLOCK_DEV_S;
358    double error_goal = 0;
359    char *end;
360
361    while ((c = getopt(argc, argv, "bc:dirv")) != EOF)
362        switch (c) {
363        case 'b':
364            burst = 1;
365            break;
366        case 'c':
367            clock_dev_s = strtod(argv[optind], &end);
368            if (*end)
369                usage(*argv);
370            break;
371        case 'd':
372            debug = 1;
373            break;
374        case 'i':
375            identify = 1;
376            break;
377        case 'r':
378            reset = 1;
379            break;
380        case 'v':
381            verbose = 1;
382            break;
383        default:
384            usage(*argv);
385        }
386    if (burst+identify+reset > 1)
387        usage(*argv);
388
389    switch (argc-optind) {
390    case 0:
391        break;
392    case 1:
393        if (identify || reset)
394            usage(*argv);
395        error_goal = strtod(argv[optind], &end)/1000000.0;
396        if (*end)
397            usage(*argv);
398        break;
399    default:
400        usage(*argv);
401    }
402
403    dev = open_usb(USB_VENDOR, USB_PRODUCT);
404    if (!dev) {
405        fprintf(stderr, ":-(\n");
406        return 1;
407    }
408
409    if (identify) {
410        identify_cntr(dev);
411        return 0;
412    }
413
414    if (reset) {
415        reset_cntr(dev);
416        return 0;
417    }
418
419    if (burst) {
420        count_bursts(dev, error_goal ? error_goal : 1);
421        return 0;
422    }
423
424    measure(dev, clock_dev_s, error_goal);
425
426    return 0;
427}
428

Archive Download this file



interactive