Root/ubb-la/ubb-la.c

1/*
2 * ubb-la.c - UBB logic analyzer
3 *
4 * Written 2013 by Werner Almesberger
5 * Copyright 2013 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#include <stdint.h>
14#include <stdlib.h>
15#include <stdio.h>
16#include <unistd.h>
17#include <string.h>
18#include <math.h>
19#include <assert.h>
20#include <sys/mman.h>
21
22#include <ubb/ubb.h>
23#include <ubb/regs4740.h>
24#include <ubb/mmcclk.h>
25#include <ubb/physmem.h>
26
27#include "gui.h"
28
29
30#define DMA 5
31
32#define KEY_MASK 0x5fc0000
33
34/*
35 * The initial skip is for samples loaded into the FIFO when starting the
36 * command, before waiting for the trigger. A completely filled FIFO would
37 * hold 16 * 32 bits or 128 samples. In reality, we observe 122 samples for
38 * sample rates up to 24 MHz and 123 samples for 42 or 56 MHz.
39 */
40
41#define INITIAL_SKIP 122 /* should be 123 for higher speeds */
42
43
44/* ----- Enable/disable interrupts ----------------------------------------- */
45
46
47static uint32_t old_icmr;
48
49
50static void disable_interrupts(void)
51{
52    old_icmr = ICMR;
53    ICMSR = 0xffffffff;
54}
55
56
57static void enable_interrupts(void)
58{
59    ICMCR = ~old_icmr;
60}
61
62
63/* ----- DMA control ------------------------------------------------------- */
64
65
66static uint32_t old_dmac;
67
68
69static void dma_stop(void)
70{
71        DCS(DMA) =
72            DCS_TT | /* Transfer terminated */
73            DCS_HLT; /* DMA halt */
74        DCS(DMA) = 0; /* reset DMA channel */
75}
76
77
78static void dma_init(void)
79{
80    old_dmac = DMAC;
81
82    DMAC = DMAC_DMAE; /* activate the DMA controller (in case it's off) */
83    dma_stop();
84
85    DCM(DMA) =
86        DCM_DAI | /* destination address increment */
87        (DCM_TSZ_32BYTE << DCM_TSZ_SHIFT);
88            /* transfer size is 32 bytes */
89        DRT(DMA) = DRT_MSC_RX; /* MSC receive-fifo-full transfer request */
90}
91
92
93static void dma_cleanup(void)
94{
95    DMAC = old_dmac;
96    dma_stop();
97}
98
99
100static void dma_setup(unsigned long buf, int nibbles)
101{
102    assert(!(nibbles & 63));
103
104/* need to reset DSA, DTA, DTC when done. uncertain about DCS */
105    DCS(DMA) = DCS_NDES; /* no-descriptor transfer */
106    DSA(DMA) = REG_PADDR(MSC_RXFIFO); /* source */
107    DTA(DMA) = buf; /* destination */
108    DTC(DMA) = nibbles >> 6; /* 32 bytes per transfer */
109}
110
111
112static void wait_dma_done(void)
113{
114    while (!(DCS(DMA) & DCS_TT));
115}
116
117
118/* ----- MMC control ------------------------------------------------------- */
119
120
121static int xfer(unsigned long buf, int nibbles,
122    uint32_t trigger, uint32_t mask)
123{
124    dma_init();
125    dma_setup(buf, nibbles);
126
127    MSC_STRPCL = MSC_STRPCRL_START_CLOCK; /* start the bus clock */
128    MSC_RESTO = MSC_RESTO_MASK; /* maximum response time-out */
129    MSC_RDTO = MSC_RDTO_MASK;
130    MSC_BLKLEN = nibbles >> 1;
131
132    MSC_CMDAT =
133        MSC_CMDAT_BUS_WIDTH_4 << MSC_CMDAT_BUS_WIDTH_SHIFT |
134        MSC_CMDAT_DMA_EN | /* DMA */
135        MSC_CMDAT_DATA_EN | /* with data transfer */
136        MSC_CMDAT_RESPONSE_FORMAT_NONE; /* no response required */
137
138    MSC_STRPCL = MSC_STRPCRL_START_OP;
139
140    while (MSC_STAT & MSC_STAT_DATA_FIFO_EMPTY);
141
142    IN(UBB_CMD);
143
144    disable_interrupts();
145
146    while ((PDPIN & mask) != trigger)
147        if ((PDPIN & KEY_MASK) != KEY_MASK)
148            goto quit;
149
150    DCS(DMA) =
151        DCS_NDES | /* no descriptor */
152        DCS_CTE; /* enable channel */
153
154    enable_interrupts();
155
156    wait_dma_done();
157
158//printf("MSC_STAT = %08x\n", MSC_STAT);
159
160    dma_cleanup();
161
162    return 1;
163
164quit:
165    enable_interrupts();
166    dma_cleanup();
167
168    return 0;
169}
170
171
172static void xfers(unsigned long *bufs, int n_bufs, int nibbles,
173    const struct mmcclk *clk, const struct mmcclk *fast_clk)
174{
175    int i = 0;
176
177    dma_init();
178
179    MSC_STRPCL = MSC_STRPCRL_START_CLOCK; /* start the bus clock */
180    MSC_RESTO = MSC_RESTO_MASK; /* maximum response time-out */
181    MSC_RDTO = MSC_RDTO_MASK;
182    MSC_BLKLEN = nibbles >> 1;
183
184    MSC_CMDAT =
185        MSC_CMDAT_BUS_WIDTH_4 << MSC_CMDAT_BUS_WIDTH_SHIFT |
186        MSC_CMDAT_DMA_EN | /* DMA */
187        MSC_CMDAT_DATA_EN | /* with data transfer */
188        MSC_CMDAT_RESPONSE_FORMAT_NONE; /* no response required */
189
190    disable_interrupts();
191
192    OUT(UBB_CMD);
193    dma_setup(bufs[0], nibbles);
194
195    while (1) {
196        MSC_STRPCL = MSC_STRPCRL_START_OP;
197
198        MSC_CLKRT = fast_clk->clkrt;
199        MSCCDR = 0;
200
201        while (!(MSC_STAT & MSC_STAT_END_CMD_RES));
202
203        IN(UBB_CMD);
204
205        MSCCDR = clk->clkdiv;
206        MSC_CLKRT = clk->clkrt;
207
208        DCS(DMA) =
209            DCS_NDES | /* no descriptor */
210            DCS_CTE; /* enable channel */
211
212        /*
213         * @@@ We could enable interrupts while waiting, particularly
214         * at low sample rates, as long as the probability of missing
215         * the end of the DMA transfer is acceptably low.
216         */
217        wait_dma_done();
218
219        if (++i == n_bufs)
220            break;
221
222        OUT(UBB_CMD);
223        dma_setup(bufs[i], nibbles);
224
225        while (!(MSC_STAT & MSC_STAT_DATA_TRAN_DONE));
226    }
227
228    enable_interrupts();
229
230    dma_cleanup();
231}
232
233
234/* ----- ASCII output ------------------------------------------------------ */
235
236
237static void print_samples(FILE *file, uint8_t *buf, int skip, int nibbles)
238{
239    uint8_t v, last = 0xff;
240    int i, count = 0;
241
242    for (i = skip; i != nibbles; i++) {
243        v = (buf[i >> 1] >> (4*(~i & 1))) & 0xf;
244        if (v == last) {
245            count++;
246        } else {
247            switch (count) {
248            case 0:
249                break;
250            case 1:
251                printf("%X", last);
252                break;
253            default:
254                printf("%X{%d}", last, count);
255                break;
256            }
257            last = v;
258            count = 1;
259        }
260    }
261    if (count == 1)
262        printf("%X\n", last);
263    else
264        printf("%X{%d}\n", last, count);
265}
266
267
268/* ----- Capture ----------------------------------------------------------- */
269
270
271static int do_buf(int nibbles, uint32_t trigger, uint32_t mask,
272    const struct mmcclk *clk, int use_gui)
273{
274    uint8_t *buf = physmem_malloc(4096);
275    struct physmem_vec vec;
276    int n;
277
278    if (mlockall(MCL_CURRENT | MCL_FUTURE)) {
279        perror("mlockall");
280        exit(1);
281    }
282
283    memset(buf, 0, 4096);
284    physmem_flush(buf, 4096);
285
286    n = physmem_xlat(buf, nibbles >> 1, &vec, 1);
287    if (n != 1) {
288        fprintf(stderr, "physmem_xlat_vec: expected 1, got %d\n", n);
289        exit(1);
290    }
291    if (!xfer(vec.addr, nibbles, trigger, mask))
292        return 0;
293
294    if (use_gui)
295        gui(buf, INITIAL_SKIP, nibbles, clk->bus_clk_hz);
296    else
297        print_samples(stdout, buf, INITIAL_SKIP, nibbles);
298
299    return 1;
300}
301
302
303static void do_bufs(int n_bufs, int nibbles,
304    const struct mmcclk *clk, const struct mmcclk *fast_clk)
305{
306    uint8_t *bufs[n_bufs];
307    struct physmem_vec vecs[n_bufs];
308    unsigned long addrs[n_bufs];
309    int i,n;
310
311    if (mlockall(MCL_CURRENT | MCL_FUTURE)) {
312        perror("mlockall");
313        exit(1);
314    }
315
316    for (i = 0; i != n_bufs; i++) {
317        bufs[i] = physmem_malloc(4096);
318        memset(bufs[i], 0, 4096);
319        physmem_flush(bufs[i], 4096);
320
321        n = physmem_xlat(bufs[i], nibbles >> 1, vecs+i, 1);
322        if (n != 1) {
323            fprintf(stderr,
324                "physmem_xlat_vec: expected 1, got %d\n", n);
325            exit(1);
326        }
327        addrs[i] = vecs[i].addr;
328    }
329    assert(!fast_clk->clkdiv);
330    xfers(addrs, n_bufs, nibbles, clk, fast_clk);
331
332    for (i = 0; i != n_bufs; i++)
333        print_samples(stdout, bufs[i], 0, nibbles);
334}
335
336
337/* ----- Command-line processing ------------------------------------------- */
338
339
340/*
341 * Among equal bus rates, pick the configuration with the fastest MMC clock.
342 * It'll save a few nanoseconds.
343 */
344
345static void frequency(struct mmcclk *clk, int hz, int all)
346{
347    struct mmcclk mmc;
348
349    mmcclk_first(&mmc, 0,
350        MMCCLK_FLAG_RD_ONLY | (all ? MMCCLK_FLAG_ALL : 0));
351    *clk = mmc;
352    while (mmcclk_next(&mmc))
353        if (fabs(clk->bus_clk_hz-hz) > fabs(mmc.bus_clk_hz-hz) ||
354            (fabs(clk->bus_clk_hz-hz) == fabs(mmc.bus_clk_hz-hz) &&
355            clk->clkdiv > mmc.clkdiv))
356            *clk = mmc;
357}
358
359
360static unsigned long xlat_pins(unsigned long pins)
361{
362    if (pins & ~0x1fUL) {
363        fprintf(stderr, "invalid trigger set/mask: 0x%lx\n", pins);
364        exit(1);
365    }
366    pins <<= 10;
367    if (pins & (0x10 << 10))
368        pins = (pins ^ (0x10 << 10)) | UBB_CLK;
369    return pins;
370}
371
372
373static void usage(const char *name)
374{
375    fprintf(stderr,
376"usage: %s [-C] [-t pattern/mask] [(-f|-F) frequency_MHz] [-g] [-n N]\n\n"
377" -C output the MMC clock on CLK/TRIG (for debugging)\n"
378" -f freq_MHz select the specified frequency (default; 1 MHz)\n"
379" -F freq_MHz like -f, but also allow \"overclocking\"\n"
380" -g display the captured waveforms graphically (default:\n"
381" print as text to standard output)\n"
382" -n N capture N buffers worth of samples without waiting for a\n"
383" trigger\n"
384" -t pattern/mask start capture at the specified pattern (DAT0 = 1, etc.,\n"
385" CLK = 16). Default: any change on TRIG.\n"
386    , name);
387    exit(1);
388}
389
390
391int main(int argc, char **argv)
392{
393    double freq_mhz = 1;
394    int all = 0;
395    unsigned long trigger = 1, mask = 0;
396    unsigned long multi = 0;
397    int clkout = 0;
398    int use_gui = 0;
399    struct mmcclk clk, fast_clk;
400    char *end;
401    int c, res;
402
403    while ((c = getopt(argc, argv, "Cf:F:gn:t:")) != EOF)
404        switch (c) {
405        case 'C':
406            clkout = 1;
407            break;
408        case 'F':
409            all = 1;
410            /* fall through */
411        case 'f':
412            freq_mhz = strtod(optarg, &end);
413            if (*end)
414                usage(*argv);
415            break;
416        case 'g':
417            use_gui = 1;
418            break;
419        case 'n':
420            multi = strtoul(optarg, &end, 0);
421            if (*end)
422                usage(*argv);
423            break;
424        case 't':
425            trigger = strtoul(optarg, &end, 0);
426            if (*end != '/')
427                usage(*argv);
428            mask = strtoul(end+1, &end, 0);
429            if (*end)
430                usage(*argv);
431            trigger = xlat_pins(trigger);
432            mask = xlat_pins(mask);
433            break;
434        default:
435            usage(*argv);
436        }
437
438    if (optind != argc)
439        usage(*argv);
440
441    ubb_open(UBB_ALL);
442    PDFUNS = UBB_DAT0 | UBB_DAT1 | UBB_DAT2 | UBB_DAT3;
443    if (clkout)
444        PDFUNS = UBB_CLK;
445    OUT(UBB_CMD);
446    CLR(UBB_CMD);
447    PDFUNC = UBB_CMD;
448
449    frequency(&clk, 1e6*freq_mhz, all);
450    fprintf(stderr, "bus %g MHz controller %g MHz\n", clk.bus_clk_hz/1e6,
451        clk.sys_clk_hz/(clk.clkdiv+1.0)/1e6);
452    mmcclk_start(&clk);
453
454    if (trigger == 1) {
455        trigger = ~PDPIN & UBB_CLK;
456        mask = UBB_CLK;
457    }
458
459    if (use_gui)
460        gui_init();
461
462    if (!multi) {
463        res = !do_buf(8128, trigger, mask, &clk, use_gui);
464    } else {
465        frequency(&fast_clk, 84e6, 1);
466        do_bufs(multi, 8128, &clk, &fast_clk);
467        res = 0;
468    }
469
470    mmcclk_stop();
471    ubb_close(UBB_ALL);
472
473    return res;
474}
475

Archive Download this file

Branches:
master



interactive