Root/
Source at commit 159a128d2894792efa08f706c1e24173a283c6b8 created 11 years 1 month ago. By Werner Almesberger, ubb-la.c/gui.c: show units in light blue to better separate them from numbers | |
---|---|
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 | |
47 | static uint32_t old_icmr; |
48 | |
49 | |
50 | static void disable_interrupts(void) |
51 | { |
52 | old_icmr = ICMR; |
53 | ICMSR = 0xffffffff; |
54 | } |
55 | |
56 | |
57 | static void enable_interrupts(void) |
58 | { |
59 | ICMCR = ~old_icmr; |
60 | } |
61 | |
62 | |
63 | /* ----- DMA control ------------------------------------------------------- */ |
64 | |
65 | |
66 | static uint32_t old_dmac; |
67 | |
68 | |
69 | static 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 | |
78 | static 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 | |
93 | static void dma_cleanup(void) |
94 | { |
95 | DMAC = old_dmac; |
96 | dma_stop(); |
97 | } |
98 | |
99 | |
100 | static 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 | |
112 | static void wait_dma_done(void) |
113 | { |
114 | while (!(DCS(DMA) & DCS_TT)); |
115 | } |
116 | |
117 | |
118 | /* ----- MMC control ------------------------------------------------------- */ |
119 | |
120 | |
121 | static 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 | |
164 | quit: |
165 | enable_interrupts(); |
166 | dma_cleanup(); |
167 | |
168 | return 0; |
169 | } |
170 | |
171 | |
172 | static 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 | |
237 | static 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 | |
271 | static 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 | |
303 | static 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 | |
345 | static 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 | |
360 | static 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 | |
373 | static 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 | |
391 | int 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 |
Branches:
master