Date:2010-08-25 23:47:45 (10 years 1 month ago)
Author:Werner Almesberger
Commit:c7303e4ac152c793db957c4ff453451b8098e5b7
Message:Made communication with CNTR board more robust. Added documentation.

- cntr/README: description of the counter board and its application
- cntr/fw/common/crc32.c: variant of CRC32-IEEE802.3 shared by firmware and
measurement application
- cntr/fw/cntr/ep0.c (my_setup), cntr/tools/cntr/cntr.c (get_sample):
protect the counter value with a CRC and an one's complement copy
- cntr/fw/include/cntr/ep0.h: oops, wasn't checked into repository
- cntr/tools/cntr/cntr.c: added section titles
- cntr/tools/cntr/cntr.c (measure): show communication statistics at the end
- cntr/tools/cntr/cntr.c (measure, usage, main): new option -d to enable
reporting of communication errors
- cntr/tools/cntr/cntr.c (set_stop, measure): let user stop measurement with
SIGINT
- cntr/tools/cntr/cntr.c (measure): get multiple "first samples" and keep
the one with the shortest round-trip time
- cntr/tools/cntr/cntr.c (measure): changed unit "ppk" (1/1000) to percent
(1/100)
- cntr/tools/cntr/cntr.c (usage, main): command-line argument is now the
accuracy goal, while the system clock deviation is set with the new
option -c
- TODO: some more things to do
Files: TODO (1 diff)
cntr/README (1 diff)
cntr/fw/cntr/ep0.c (3 diffs)
cntr/fw/include/cntr/ep0.h (1 diff)
cntr/tools/cntr/cntr.c (9 diffs)

Change Details

TODO
105105
106106- measure duty cycle
107107
108- display activity on clock input and duty cycle
108- use the LED to display activity on clock input and duty cycle
109109
110110- consider using a comparator and a DAC to allow for programmable logic levels
111111
112112- evaluate termination resistance
113113
114114- document circuit design
115
116- record beats between 16 bit counter polls and use them for the estimate
117  of lost cycles (2*1 is way too optimistic)
118
119- include system clock resolution in accuracy calculation
120
121- consider running shorter sliding windows to estimate drift
122
123- consider detecting unusual half-periods
124
125- consider using a reversed USB connector, to avoid having to cross D+/D- and,
126  worse, VBUS and GND
127
128- test input performance by counting a source that emits a known number of
129  cycles
130
131- consider using historical margins to sanity-check the current margin (if any
132  old.max < curr.min or old.min > curr.max, we have a problem) and to further
133  narrow the effective margin, thus achieving faster convergence. We would have
134  to consider temperature drift of the frequency source in this case.
cntr/README
1Arbitrary-precision counter
2===========================
3
4Theory of operation
5-------------------
6
7The arbitrary-precision counter counts clock cycles of a frequency
8source that is assumed to be free from drift. It compares the count
9with the host's system clock. If the system clock is synchronized with
10an accurate NTP reference, measurements with arbitrarily high accuracy
11can be obtained.
12
13In practice, this is limited by the the frequency source's drift and
14the time one is willing to wait. If NTP maintains the system time
15with an accuracy of +/- 100 ms, obtaining measurements with an
16accuracy of +/- 1 ppm would take about 28 hours.
17
18Additional error sources, such as the round-trip time when requesting
19a sample from the microcontroller, are also considered in the accuracy
20calculation.
21
22The counter consists of a board based on a C8051F320 microcontroller
23and the control software on the host. The microcontroller counts
24events in a free-running 16 bit counter that is regularly read and
25extended to 32 bits. The 32 bit counter is periodically queried by
26the host.
27
28The microcontroller's counter can count at a frequency of up to 3 MHz.
29(SYSCLK/4)
30
31In order to protect against transmission errors not detected by USB's
32CRC, which are occur relatively often, each packet is protected by a
33CRC-32 and an inverted copy of the payload. Corrupted packets are
34rejected by the host.
35
36The 32 bit counter wraps around at most once very 21.8 ms. The 32 bit
37counter wraps around at most every 1431 s. The host extends the 32 bit
38counter to 64 bits, and calculates frequency and accuracy from the
39count and the run time of the measurement application.
40
41
42Performing a measurement
43------------------------
44
45To perform a measurement, connect the CNTR board's probe input to the
46clock source and then run the "cntr" application on the host. An
47accuracy goal (in ppm) can be specified on the command line (see
48below).
49
50The host polls the microcontroller every 100 ms and displays the run
51time (in seconds), the measured frequency, and the accuracy achieved
52so far.
53
54Measurements can be stopped by pressing ^C or by specifying an
55accuracy goal. At the end, the total number of events counted and
56communication statistics are displayed.
57
58
59Updating the firmware
60---------------------
61
62The protocol revision and the build date of the firmware of the CNTR
63board can be queried with "cntr -i".
64
65To update the firmware, run
66cntr -r && sleep 1 && dfu-util -d 0x20b7:0xcb72 -D cntr.bin
cntr/fw/cntr/ep0.c
2222#include "cntr/ep0.h"
2323#include "version.h"
2424
25
2625#define debug(...)
2726#define error(...)
2827
...... 
6160#define BUILD_OFFSET 7 /* '#' plus "65535" plus ' ' */
6261
6362
63/* crc32() */
64#include "cntr/crc32.c"
65
66
6467static __bit my_setup(struct setup_request *setup) __reentrant
6568{
66    unsigned tmp;
69    uint32_t tmp;
6770    uint8_t size, i;
6871
6972    switch (setup->bmRequestType | setup->bRequest << 8) {
...... 
102105        buf[1] = cntr[1];
103106        buf[2] = cntr[2];
104107        buf[3] = cntr[3];
105        usb_send(&ep0, buf, 4, NULL, NULL);
108        tmp = (uint32_t) buf[0] | ((uint32_t) buf[1] << 8) |
109            ((uint32_t) buf[2] << 16) | ((uint32_t) buf[3] << 24);
110        tmp = crc32(tmp, 0xffffffff);
111        buf[4] = tmp;
112        buf[5] = tmp >> 8;
113        buf[6] = tmp >> 16;
114        buf[7] = tmp >> 24;
115        buf[8] = ~cntr[0];
116        buf[9] = ~cntr[1];
117        buf[10] = ~cntr[2];
118        buf[11] = ~cntr[3];
119        usb_send(&ep0, buf, 12, NULL, NULL);
106120        return 1;
107121
108122    default:
cntr/fw/include/cntr/ep0.h
1/*
2 * include/cntr/ep0.h - EP0 extension protocol
3 *
4 * Written 2008-2010 by Werner Almesberger
5 * Copyright 2008-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#ifndef EP0_H
15#define EP0_H
16
17/*
18 * Direction bRequest wValue wIndex wLength
19 *
20 * ->host CNTR_ID - - 3
21 * ->host CNTR_BUILD - - #bytes
22 * host-> CNTR_RESET - - 0
23 *
24 * ->host CNTR_READ - 0 12
25 */
26
27/*
28 * EP0 protocol:
29 *
30 * 0.0 initial release
31 */
32
33#define EP0CNTR_MAJOR 0 /* EP0 protocol, major revision */
34#define EP0CNTR_MINOR 0 /* EP0 protocol, minor revision */
35
36
37/*
38 * bmRequestType:
39 *
40 * D7 D6..5 D4...0
41 * | | |
42 * direction (0 = host->dev)
43 * type (2 = vendor)
44 * recipient (0 = device)
45 */
46
47
48#define CNTR_TO_DEV(req) (0x40 | (req) << 8)
49#define CNTR_FROM_DEV(req) (0xc0 | (req) << 8)
50
51
52enum cntr_requests {
53    CNTR_ID = 0x00,
54    CNTR_BUILD,
55    CNTR_RESET,
56    CNTR_READ = 0x10,
57};
58
59
60void ep0_init(void);
61
62#endif /* !EP0_H */
cntr/tools/cntr/cntr.c
1313
1414#include <stdlib.h>
1515#include <stdio.h>
16#include <signal.h>
1617#include <usb.h>
1718#include <sys/time.h>
1819
...... 
2829#define BUF_SIZE 256
2930
3031
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
3146
3247static void reset_cntr(usb_dev_handle *dev)
3348{
...... 
4156}
4257
4358
59/* ----- identify ---------------------------------------------------------- */
60
61
4462static void identify_cntr(usb_dev_handle *dev)
4563{
4664    const struct usb_device *device = usb_device(dev);
...... 
7189}
7290
7391
92/* ----- measurements ------------------------------------------------------ */
93
94
7495struct sample {
7596    double t0, t1;
7697    uint64_t cntr;
7798};
7899
79100
80static void get_sample(usb_dev_handle *dev, struct sample *s)
101static unsigned packets = 0, crc_errors = 0, inv_errors = 0;
102static volatile int stop = 0;
103
104
105static void set_stop(int sig)
106{
107    stop = 1;
108}
109
110
111static int get_sample(usb_dev_handle *dev, struct sample *s)
81112{
82113    static uint32_t last = 0, high = 0;
83114    struct timeval t0, t1;
84    int res;
85    uint8_t buf[4];
86    uint32_t cntr;
115    int res, bad;
116    uint8_t buf[12];
117    uint32_t cntr, inv, crc, expect;
87118
88119    gettimeofday(&t0, NULL);
89120    res = usb_control_msg(dev, FROM_DEV, CNTR_READ, 0, 0,
...... 
93124        fprintf(stderr, "CNTR_READ: %s\n", usb_strerror());
94125        exit(1);
95126    }
127    packets++;
96128    cntr = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
129    crc = buf[4] | (buf[5] << 8) | (buf[6] << 16) | (buf[7] << 24);
130    inv = buf[8] | (buf[9] << 8) | (buf[10] << 16) | (buf[11] << 24);
131    expect = crc32(cntr, ~0);
132    bad = 0;
133    if (crc != expect) {
134        if (verbose)
135            fprintf(stderr, "\nCRC error (count 0x%08x->0x%08x "
136                "CRC 0x%08x/0x%08x)\n",
137                (unsigned) last, (unsigned) cntr, (unsigned) crc,
138                (unsigned) expect);
139        bad = 1;
140        crc_errors++;
141    }
142    if (cntr != (inv ^ 0xffffffff)) {
143        if (verbose)
144            fprintf(stderr,
145                "\ninverted counter error (0x%08x->0x%08x, "
146                "inv 0x%08x)\n",
147                (unsigned) last, (unsigned) cntr, (unsigned) inv);
148        bad = 1;
149        inv_errors++;
150    }
151    if (bad)
152        return 0;
97153    if (last > cntr)
98154        high++;
99155    last = cntr;
100156    s->t0 = t0.tv_sec+t0.tv_usec/1000000.0;
101157    s->t1 = t1.tv_sec+t1.tv_usec/1000000.0;
102158    s->cntr = (uint64_t) high << 32 | cntr;
159    if (debug)
160        printf("0x%llx 0x%lx\n",
161            (unsigned long long ) s->cntr, (unsigned long) cntr);
162    return 1;
103163}
104164
105165
106static void measure(usb_dev_handle *dev, double clock_dev_s)
166static void measure(usb_dev_handle *dev, double clock_dev_s, double error_goal)
107167{
108168    struct sample start, now;
109169    uint64_t dc;
110170    double dt, f, error;
111    char *f_exp, error_exp;
112
113    get_sample(dev, &start);
114    while (1) {
171    char *f_exp, *error_exp;
172    int i;
173
174    signal(SIGINT, set_stop);
175
176    /*
177     * The round-trip time for getting the first sample is one of the
178     * error terms. The smaller we can make it, the better. Thus, we try a
179     * few times to improve our first result.
180     */
181    while (!get_sample(dev, &start));
182    for (i = 0; i != 10; i++) {
183        while (!get_sample(dev, &now));
184        if (now.t1-now.t0 < start.t1-start.t0) {
185            if (debug)
186                fprintf(stderr, "improve %g -> %g\n",
187                    start.t1-start.t0, now.t1-now.t0);
188            start = now;
189        }
190    }
191    while (!stop) {
115192        usleep(100000);
116        get_sample(dev, &now);
193        while (!get_sample(dev, &now));
117194        dc = now.cntr-start.cntr;
118195        dt = now.t0-start.t0;
119196        f = dc/dt;
...... 
133210        error += (start.t1-start.t0)/dt;/* start sample read */
134211        error += (now.t1-now.t0)/dt; /* last sample read */
135212        error += clock_dev_s/dt; /* system clock deviation */
136        if (error > 1) {
213        if (error >= 1) {
137214            printf("\r(wait) ");
138215            fflush(stdout);
139216            continue;
140217        }
141
142        error_exp = 'k';
143        error *= 1000.0; /* ppm */
144        if (error < 1.0) {
145            error_exp = 'm'; /* ppm */
146            error *= 1000.0;
147        }
148        if (error < 1.0) {
149            error_exp = 'b'; /* ppb */
150            error *= 1000.0;
218        if (dc && error <= error_goal)
219            stop = 1;
220
221        error_exp = "%";
222        error *= 100.0;
223        if (error < 0.1) {
224            error_exp = " ppm";
225            error *= 10000.0;
226            if (error < 1.0) {
227                error_exp = " ppb";
228                error *= 1000.0;
229            }
151230        }
152231
153        printf("\r%6.1f %1.9f %sHz %3.3f pp%c ",
232        printf("\r%6.1f %1.9f %sHz %3.3f%s ",
154233            dt, f, f_exp, error, error_exp);
155234        fflush(stdout);
156235    }
236    printf(
237        "\n%llu counts, %u packets, %u CRC error%s, %u invert error%s\n",
238        (unsigned long long) (now.cntr-start.cntr),
239        packets, crc_errors, crc_errors == 1 ? "" : "s",
240        inv_errors, inv_errors == 1 ? "" : "s");
157241}
158242
159243
244/* ----- command-line parsing ---------------------------------------------- */
245
246
160247static void usage(const char *name)
161248{
162249    fprintf(stderr,
163"usage: %s [clock_dev_s]\n"
250"usage: %s [-c clock_dev] [-d] [-v] [accuracy_ppm]\n"
164251"%6s %s -i\n"
165"%6s %s r\n\n"
166" clock_dev_s is the maximum deviation of the system clock, in seconds\n"
167" (default: %g s)\n"
252"%6s %s -r\n\n"
253" accuracy_ppm stop when specified accuracy is reached (default: never\n"
254" stop)\n"
255" -c clock_dev maximum deviation of the system clock, in seconds\n"
256" (default: %g s)\n"
257" -d debug mode. Print counter values.\n"
168258" -i identify the CNTR board\n"
169259" -r reset the CNTR board\n"
260" -v verbose reporting of communication errors\n"
170261    , name, "", name, "", name, DEFAULT_CLOCK_DEV_S);
171262    exit(1);
172263}
...... 
177268    usb_dev_handle *dev;
178269    int c, identify = 0, reset = 0;
179270    double clock_dev_s = DEFAULT_CLOCK_DEV_S;
271    double error_goal = 0;
180272    char *end;
181273
182    while ((c = getopt(argc, argv, "ir")) != EOF)
274    while ((c = getopt(argc, argv, "c:dir")) != EOF)
183275        switch (c) {
276        case 'c':
277            clock_dev_s = strtod(argv[optind], &end);
278            if (*end)
279                usage(*argv);
280            break;
281        case 'd':
282            debug = 1;
283            break;
184284        case 'i':
185285            identify = 1;
186286            break;
...... 
190290        default:
191291            usage(*argv);
192292        }
293    if (identify && reset)
294        usage(*argv);
193295
194296    switch (argc-optind) {
195297    case 0:
196298        break;
197299    case 1:
198        clock_dev_s = strtod(argv[optind], &end);
300        error_goal = strtod(argv[optind], &end)/1000000.0;
199301        if (*end)
200302            usage(*argv);
201303        break;
...... 
219321        return 0;
220322    }
221323
222    measure(dev, clock_dev_s);
324    measure(dev, clock_dev_s, error_goal);
223325
224326    return 0;
225327}

Archive Download the corresponding diff file



interactive