]> sigrok.org Git - libsigrok.git/blame - src/input/vcd.c
Build: Set local include directories in Makefile.am
[libsigrok.git] / src / input / vcd.c
CommitLineData
99eaa206 1/*
50985c20 2 * This file is part of the libsigrok project.
99eaa206 3 *
0157808d 4 * Copyright (C) 2012 Petteri Aimonen <jpa@sr.mail.kapsi.fi>
7db06394 5 * Copyright (C) 2014 Bert Vermeulen <bert@biot.com>
99eaa206
PA
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 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
0157808d
PA
21/* The VCD input module has the following options:
22 *
ba7dd8bb 23 * numchannels: Maximum number of channels to use. The channels are
0157808d
PA
24 * detected in the same order as they are listed
25 * in the $var sections of the VCD file.
26 *
27 * skip: Allows skipping until given timestamp in the file.
28 * This can speed up analyzing of long captures.
29 *
30 * Value < 0: Skip until first timestamp listed in
31 * the file. (default)
32 *
33 * Value = 0: Do not skip, instead generate samples
34 * beginning from timestamp 0.
35 *
36 * Value > 0: Start at the given timestamp.
37 *
38 * downsample: Divide the samplerate by the given factor.
39 * This can speed up analyzing of long captures.
40 *
6b7ace48
PA
41 * compress: Compress idle periods longer than this value.
42 * This can speed up analyzing of long captures.
43 * Default 0 = don't compress.
44 *
0157808d
PA
45 * Based on Verilog standard IEEE Std 1364-2001 Version C
46 *
47 * Supported features:
48 * - $var with 'wire' and 'reg' types of scalar variables
49 * - $timescale definition for samplerate
50 * - multiple character variable identifiers
51 *
52 * Most important unsupported features:
53 * - vector variables (bit vectors etc.)
54 * - analog, integer and real number variables
55 * - $dumpvars initial value declaration
8be87469 56 * - $scope namespaces
ba7dd8bb 57 * - more than 64 channels
0157808d
PA
58 */
59
99eaa206
PA
60#include <stdlib.h>
61#include <glib.h>
62#include <stdio.h>
61a429c9 63#include <string.h>
c1aae900 64#include <libsigrok/libsigrok.h>
99eaa206
PA
65#include "libsigrok-internal.h"
66
3544f848 67#define LOG_PREFIX "input/vcd"
99eaa206 68
3f239f08 69#define DEFAULT_NUM_CHANNELS 8
e4c8a4d7
BV
70#define CHUNKSIZE 1024
71
72struct context {
c10ef17c 73 gboolean started;
7db06394 74 gboolean got_header;
e4c8a4d7 75 uint64_t samplerate;
7db06394
BV
76 unsigned int maxchannels;
77 unsigned int channelcount;
e4c8a4d7
BV
78 int downsample;
79 unsigned compress;
80 int64_t skip;
7db06394 81 gboolean skip_until_end;
ba7dd8bb 82 GSList *channels;
e4c8a4d7
BV
83};
84
ba7dd8bb 85struct vcd_channel {
e4c8a4d7
BV
86 gchar *name;
87 gchar *identifier;
88};
89
e4c8a4d7 90/*
7db06394 91 * Reads a single VCD section from input file and parses it to name/contents.
99eaa206
PA
92 * e.g. $timescale 1ps $end => "timescale" "1ps"
93 */
7db06394 94static gboolean parse_section(GString *buf, gchar **name, gchar **contents)
99eaa206 95{
7db06394 96 GString *sname, *scontent;
99eaa206 97 gboolean status;
7db06394
BV
98 unsigned int pos;
99
100 *name = *contents = NULL;
101 status = FALSE;
102 pos = 0;
cd1b0e8f 103
7db06394
BV
104 /* Skip any initial white-space. */
105 while (pos < buf->len && g_ascii_isspace(buf->str[pos]))
106 pos++;
cd1b0e8f 107
99eaa206 108 /* Section tag should start with $. */
7db06394 109 if (buf->str[pos++] != '$')
99eaa206 110 return FALSE;
cd1b0e8f 111
99eaa206 112 sname = g_string_sized_new(32);
7db06394
BV
113 scontent = g_string_sized_new(128);
114
115 /* Read the section tag. */
116 while (pos < buf->len && !g_ascii_isspace(buf->str[pos]))
117 g_string_append_c(sname, buf->str[pos++]);
118
119 /* Skip whitespace before content. */
120 while (pos < buf->len && g_ascii_isspace(buf->str[pos]))
121 pos++;
122
123 /* Read the content. */
124 while (pos < buf->len - 4 && strncmp(buf->str + pos, "$end", 4))
125 g_string_append_c(scontent, buf->str[pos++]);
126
127 if (sname->len && pos < buf->len - 4 && !strncmp(buf->str + pos, "$end", 4)) {
128 status = TRUE;
129 pos += 4;
130 while (pos < buf->len && g_ascii_isspace(buf->str[pos]))
131 pos++;
132 g_string_erase(buf, 0, pos);
133 }
99eaa206 134
99eaa206 135 *name = g_string_free(sname, !status);
7db06394
BV
136 *contents = g_string_free(scontent, !status);
137 if (*contents)
138 g_strchomp(*contents);
139
99eaa206
PA
140 return status;
141}
142
ba7dd8bb 143static void free_channel(void *data)
db9679af 144{
ba7dd8bb
UH
145 struct vcd_channel *vcd_ch = data;
146 g_free(vcd_ch->name);
147 g_free(vcd_ch->identifier);
148 g_free(vcd_ch);
db9679af
ML
149}
150
99eaa206
PA
151/* Remove empty parts from an array returned by g_strsplit. */
152static void remove_empty_parts(gchar **parts)
153{
154 gchar **src = parts;
155 gchar **dest = parts;
e4c8a4d7 156 while (*src != NULL) {
99eaa206 157 if (**src != '\0')
99eaa206 158 *dest++ = *src;
99eaa206
PA
159 src++;
160 }
cd1b0e8f 161
99eaa206
PA
162 *dest = NULL;
163}
164
e4c8a4d7
BV
165/*
166 * Parse VCD header to get values for context structure.
99eaa206
PA
167 * The context structure should be zeroed before calling this.
168 */
7db06394 169static gboolean parse_header(const struct sr_input *in, GString *buf)
99eaa206 170{
ba7dd8bb 171 struct vcd_channel *vcd_ch;
7db06394
BV
172 uint64_t p, q;
173 struct context *inc;
174 gboolean status;
175 gchar *name, *contents, **parts;
99eaa206 176
7db06394
BV
177 inc = in->priv;
178 name = contents = NULL;
179 status = FALSE;
180 while (parse_section(buf, &name, &contents)) {
99eaa206 181 sr_dbg("Section '%s', contents '%s'.", name, contents);
cd1b0e8f 182
e4c8a4d7 183 if (g_strcmp0(name, "enddefinitions") == 0) {
99eaa206
PA
184 status = TRUE;
185 break;
e4c8a4d7
BV
186 } else if (g_strcmp0(name, "timescale") == 0) {
187 /*
188 * The standard allows for values 1, 10 or 100
189 * and units s, ms, us, ns, ps and fs.
7db06394 190 */
e4c8a4d7 191 if (sr_parse_period(contents, &p, &q) == SR_OK) {
7db06394 192 inc->samplerate = q / p;
e4c8a4d7 193 if (q % p != 0) {
99eaa206 194 /* Does not happen unless time value is non-standard */
0157808d 195 sr_warn("Inexact rounding of samplerate, %" PRIu64 " / %" PRIu64 " to %" PRIu64 " Hz.",
7db06394 196 q, p, inc->samplerate);
99eaa206 197 }
cd1b0e8f 198
7db06394 199 sr_dbg("Samplerate: %" PRIu64, inc->samplerate);
e4c8a4d7 200 } else {
99eaa206
PA
201 sr_err("Parsing timescale failed.");
202 }
e4c8a4d7 203 } else if (g_strcmp0(name, "var") == 0) {
99eaa206 204 /* Format: $var type size identifier reference $end */
7db06394 205 parts = g_strsplit_set(contents, " \r\n\t", 0);
99eaa206 206 remove_empty_parts(parts);
cd1b0e8f 207
99eaa206 208 if (g_strv_length(parts) != 4)
8be87469 209 sr_warn("$var section should have 4 items");
99eaa206 210 else if (g_strcmp0(parts[0], "reg") != 0 && g_strcmp0(parts[0], "wire") != 0)
8be87469 211 sr_info("Unsupported signal type: '%s'", parts[0]);
99eaa206 212 else if (strtol(parts[1], NULL, 10) != 1)
8be87469 213 sr_info("Unsupported signal size: '%s'", parts[1]);
7db06394
BV
214 else if (inc->channelcount >= inc->maxchannels)
215 sr_warn("Skipping '%s' because only %d channels requested.",
216 parts[3], inc->maxchannels);
e4c8a4d7 217 else {
7db06394
BV
218 sr_info("Channel %d is '%s' identified by '%s'.",
219 inc->channelcount, parts[3], parts[2]);
ba7dd8bb
UH
220 vcd_ch = g_malloc(sizeof(struct vcd_channel));
221 vcd_ch->identifier = g_strdup(parts[2]);
222 vcd_ch->name = g_strdup(parts[3]);
7db06394
BV
223 inc->channels = g_slist_append(inc->channels, vcd_ch);
224 inc->channelcount++;
99eaa206 225 }
cd1b0e8f 226
99eaa206
PA
227 g_strfreev(parts);
228 }
cd1b0e8f 229
99eaa206
PA
230 g_free(name); name = NULL;
231 g_free(contents); contents = NULL;
232 }
99eaa206
PA
233 g_free(name);
234 g_free(contents);
cd1b0e8f 235
7db06394
BV
236 inc->got_header = status;
237
99eaa206
PA
238 return status;
239}
240
7db06394 241static int format_match(GHashTable *metadata)
99eaa206 242{
7db06394 243 GString *buf, *tmpbuf;
99eaa206 244 gboolean status;
7db06394 245 gchar *name, *contents;
cd1b0e8f 246
7db06394
BV
247 buf = g_hash_table_lookup(metadata, GINT_TO_POINTER(SR_INPUT_META_HEADER));
248 tmpbuf = g_string_new_len(buf->str, buf->len);
99eaa206 249
e4c8a4d7
BV
250 /*
251 * If we can parse the first section correctly,
99eaa206
PA
252 * then it is assumed to be a VCD file.
253 */
7db06394
BV
254 status = parse_section(tmpbuf, &name, &contents);
255 g_string_free(tmpbuf, TRUE);
99eaa206
PA
256 g_free(name);
257 g_free(contents);
cd1b0e8f 258
4f979115 259 return status ? SR_OK : SR_ERR;
99eaa206
PA
260}
261
61a429c9
PA
262/* Send N samples of the given value. */
263static void send_samples(const struct sr_dev_inst *sdi, uint64_t sample, uint64_t count)
264{
265 struct sr_datafeed_packet packet;
266 struct sr_datafeed_logic logic;
267 uint64_t buffer[CHUNKSIZE];
268 uint64_t i;
269 unsigned chunksize = CHUNKSIZE;
cd1b0e8f 270
61a429c9
PA
271 if (count < chunksize)
272 chunksize = count;
273
274 for (i = 0; i < chunksize; i++)
61a429c9 275 buffer[i] = sample;
cd1b0e8f 276
61a429c9 277 packet.type = SR_DF_LOGIC;
cd1b0e8f 278 packet.payload = &logic;
61a429c9
PA
279 logic.unitsize = sizeof(uint64_t);
280 logic.data = buffer;
cd1b0e8f 281
e4c8a4d7 282 while (count) {
61a429c9
PA
283 if (count < chunksize)
284 chunksize = count;
cd1b0e8f 285
61a429c9 286 logic.length = sizeof(uint64_t) * chunksize;
cd1b0e8f 287
61a429c9
PA
288 sr_session_send(sdi, &packet);
289 count -= chunksize;
290 }
291}
292
7db06394
BV
293/* Parse a set of lines from the data section. */
294static void parse_contents(const struct sr_input *in, char *data)
61a429c9 295{
7db06394
BV
296 struct context *inc;
297 struct vcd_channel *vcd_ch;
298 GSList *l;
299 uint64_t timestamp, prev_timestamp, prev_values;
300 unsigned int bit, i, j;
301 char **tokens;
cd1b0e8f 302
7db06394
BV
303 inc = in->priv;
304 prev_timestamp = prev_values = 0;
cd1b0e8f 305
61a429c9 306 /* Read one space-delimited token at a time. */
7db06394
BV
307 tokens = g_strsplit_set(data, " \t\r\n", 0);
308 remove_empty_parts(tokens);
309 for (i = 0; tokens[i]; i++) {
310 if (inc->skip_until_end) {
311 if (!strcmp(tokens[i], "$end")) {
312 /* Done with unhandled/unknown section. */
313 inc->skip_until_end = FALSE;
314 break;
315 }
316 }
317 if (tokens[i][0] == '#' && g_ascii_isdigit(tokens[i][1])) {
61a429c9 318 /* Numeric value beginning with # is a new timestamp value */
7db06394 319 timestamp = strtoull(tokens[i] + 1, NULL, 10);
cd1b0e8f 320
7db06394
BV
321 if (inc->downsample > 1)
322 timestamp /= inc->downsample;
cd1b0e8f 323
e4c8a4d7
BV
324 /*
325 * Skip < 0 => skip until first timestamp.
0157808d
PA
326 * Skip = 0 => don't skip
327 * Skip > 0 => skip until timestamp >= skip.
328 */
7db06394
BV
329 if (inc->skip < 0) {
330 inc->skip = timestamp;
8be87469 331 prev_timestamp = timestamp;
7db06394
BV
332 } else if (inc->skip > 0 && timestamp < (uint64_t)inc->skip) {
333 prev_timestamp = inc->skip;
334 } else if (timestamp == prev_timestamp) {
8be87469 335 /* Ignore repeated timestamps (e.g. sigrok outputs these) */
7db06394
BV
336 } else {
337 if (inc->compress != 0 && timestamp - prev_timestamp > inc->compress) {
6b7ace48 338 /* Compress long idle periods */
7db06394 339 prev_timestamp = timestamp - inc->compress;
6b7ace48 340 }
cd1b0e8f 341
61a429c9 342 sr_dbg("New timestamp: %" PRIu64, timestamp);
cd1b0e8f 343
61a429c9 344 /* Generate samples from prev_timestamp up to timestamp - 1. */
7db06394 345 send_samples(in->sdi, prev_values, timestamp - prev_timestamp);
0157808d 346 prev_timestamp = timestamp;
61a429c9 347 }
7db06394
BV
348 } else if (tokens[i][0] == '$' && tokens[i][1] != '\0') {
349 /*
350 * This is probably a $dumpvars, $comment or similar.
351 * $dump* contain useful data.
352 */
353 if (g_strcmp0(tokens[i], "$dumpvars") == 0
354 || g_strcmp0(tokens[i], "$dumpon") == 0
355 || g_strcmp0(tokens[i], "$dumpoff") == 0
356 || g_strcmp0(tokens[i], "$end") == 0) {
8be87469 357 /* Ignore, parse contents as normally. */
e4c8a4d7 358 } else {
7db06394
BV
359 /* Ignore this and future lines until $end. */
360 inc->skip_until_end = TRUE;
361 break;
8be87469 362 }
7db06394
BV
363 } else if (strchr("bBrR", tokens[i][0]) != NULL) {
364 /* A vector value, not supported yet. */
365 break;
366 } else if (strchr("01xXzZ", tokens[i][0]) != NULL) {
61a429c9 367 /* A new 1-bit sample value */
7db06394
BV
368 bit = (tokens[i][0] == '1');
369
370 /*
371 * The identifier is either the next character, or, if
372 * there was whitespace after the bit, the next token.
373 */
374 if (tokens[i][1] == '\0') {
375 if (!tokens[++i])
376 /* Missing identifier */
377 continue;
378 } else {
379 for (j = 1; tokens[i][j]; j++)
380 tokens[i][j - 1] = tokens[i][j];
381 tokens[i][j - 1] = '\0';
61a429c9 382 }
cd1b0e8f 383
7db06394 384 for (j = 0, l = inc->channels; j < inc->channelcount && l; j++, l = l->next) {
ba7dd8bb 385 vcd_ch = l->data;
7db06394 386 if (g_strcmp0(tokens[i], vcd_ch->identifier) == 0) {
ba7dd8bb 387 /* Found our channel */
61a429c9 388 if (bit)
7db06394 389 prev_values |= (uint64_t)1 << j;
61a429c9 390 else
7db06394 391 prev_values &= ~((uint64_t)1 << j);
61a429c9
PA
392 break;
393 }
394 }
7db06394
BV
395 if (j == inc->channelcount)
396 sr_dbg("Did not find channel for identifier '%s'.", tokens[i]);
e4c8a4d7 397 } else {
7db06394 398 sr_warn("Skipping unknown token '%s'.", tokens[i]);
8be87469 399 }
7db06394
BV
400 }
401 g_strfreev(tokens);
402}
403
404static int init(struct sr_input *in, GHashTable *options)
405{
7db06394
BV
406 int num_channels, i;
407 char name[16];
408 struct context *inc;
409
7db06394
BV
410 num_channels = g_variant_get_int32(g_hash_table_lookup(options, "numchannels"));
411 if (num_channels < 1) {
412 sr_err("Invalid value for numchannels: must be at least 1.");
413 return SR_ERR_ARG;
414 }
415 if (num_channels > 64) {
416 sr_err("No more than 64 channels supported.");
417 return SR_ERR_ARG;
418 }
d0181813 419 inc = in->priv = g_malloc0(sizeof(struct context));
7db06394 420 inc->maxchannels = num_channels;
cd1b0e8f 421
7db06394
BV
422 inc->downsample = g_variant_get_int32(g_hash_table_lookup(options, "downsample"));
423 if (inc->downsample < 1)
424 inc->downsample = 1;
425
426 inc->compress = g_variant_get_int32(g_hash_table_lookup(options, "compress"));
427 inc->skip = g_variant_get_int32(g_hash_table_lookup(options, "skip"));
428 inc->skip /= inc->downsample;
429
aac29cc1 430 in->sdi = g_malloc0(sizeof(struct sr_dev_inst));
7db06394
BV
431 in->priv = inc;
432
433 for (i = 0; i < num_channels; i++) {
434 snprintf(name, 16, "%d", i);
5e23fcab 435 sr_channel_new(in->sdi, i, SR_CHANNEL_LOGIC, TRUE, name);
61a429c9 436 }
cd1b0e8f 437
7db06394 438 return SR_OK;
61a429c9
PA
439}
440
7db06394
BV
441static gboolean have_header(GString *buf)
442{
443 unsigned int pos;
444 char *p;
445
446 if (!(p = g_strstr_len(buf->str, buf->len, "$enddefinitions")))
447 return FALSE;
448 pos = p - buf->str + 15;
449 while (pos < buf->len - 4 && g_ascii_isspace(buf->str[pos]))
450 pos++;
451 if (!strncmp(buf->str + pos, "$end", 4))
452 return TRUE;
453
454 return FALSE;
455}
456
7066fd46 457static int process_buffer(struct sr_input *in)
99eaa206 458{
99eaa206 459 struct sr_datafeed_packet packet;
2df1e819
BV
460 struct sr_datafeed_meta meta;
461 struct sr_config *src;
7db06394 462 struct context *inc;
2df1e819 463 uint64_t samplerate;
7db06394
BV
464 char *p;
465
7db06394 466 inc = in->priv;
d0181813 467 if (!inc->started) {
7db06394 468 std_session_send_df_header(in->sdi, LOG_PREFIX);
99eaa206 469
7db06394
BV
470 packet.type = SR_DF_META;
471 packet.payload = &meta;
472 samplerate = inc->samplerate / inc->downsample;
473 src = sr_config_new(SR_CONF_SAMPLERATE, g_variant_new_uint64(samplerate));
474 meta.config = g_slist_append(NULL, src);
475 sr_session_send(in->sdi, &packet);
476 sr_config_free(src);
d0181813
BV
477
478 inc->started = TRUE;
99eaa206
PA
479 }
480
7db06394
BV
481 while ((p = g_strrstr_len(in->buf->str, in->buf->len, "\n"))) {
482 *p = '\0';
483 g_strstrip(in->buf->str);
484 if (in->buf->str[0] != '\0')
485 parse_contents(in, in->buf->str);
486 g_string_erase(in->buf, 0, p - in->buf->str + 1);
487 }
99eaa206 488
7db06394
BV
489 return SR_OK;
490}
cd1b0e8f 491
7066fd46
BV
492static int receive(struct sr_input *in, GString *buf)
493{
494 struct context *inc;
495 int ret;
496
497 g_string_append_len(in->buf, buf->str, buf->len);
498
499 inc = in->priv;
500 if (!inc->got_header) {
501 if (!have_header(in->buf))
502 return SR_OK;
73931b7c 503 if (!parse_header(in, in->buf))
7066fd46
BV
504 /* There was a header in there, but it was malformed. */
505 return SR_ERR;
506
507 in->sdi_ready = TRUE;
508 /* sdi is ready, notify frontend. */
509 return SR_OK;
510 }
511
512 ret = process_buffer(in);
513
514 return ret;
515}
516
517static int end(struct sr_input *in)
7db06394 518{
c10ef17c 519 struct sr_datafeed_packet packet;
7db06394 520 struct context *inc;
7066fd46
BV
521 int ret;
522
523 if (in->sdi_ready)
524 ret = process_buffer(in);
525 else
526 ret = SR_OK;
99eaa206 527
7db06394 528 inc = in->priv;
c10ef17c 529 if (inc->started) {
c10ef17c
BV
530 packet.type = SR_DF_END;
531 sr_session_send(in->sdi, &packet);
532 }
533
7066fd46
BV
534 return ret;
535}
536
d5cc282f 537static void cleanup(struct sr_input *in)
7066fd46
BV
538{
539 struct context *inc;
540
541 inc = in->priv;
7db06394 542 g_slist_free_full(inc->channels, free_channel);
99eaa206
PA
543}
544
7db06394 545static struct sr_option options[] = {
5e83cd74 546 { "numchannels", "Number of channels", "Number of channels", NULL, NULL },
7db06394
BV
547 { "skip", "Skip", "Skip until timestamp", NULL, NULL },
548 { "downsample", "Downsample", "Divide samplerate by factor", NULL, NULL },
549 { "compress", "Compress", "Compress idle periods longer than this value", NULL, NULL },
06ad20be 550 ALL_ZERO
7db06394
BV
551};
552
553static struct sr_option *get_options(void)
554{
555 if (!options[0].def) {
556 options[0].def = g_variant_ref_sink(g_variant_new_int32(DEFAULT_NUM_CHANNELS));
557 options[1].def = g_variant_ref_sink(g_variant_new_int32(-1));
558 options[2].def = g_variant_ref_sink(g_variant_new_int32(1));
559 options[3].def = g_variant_ref_sink(g_variant_new_int32(0));
560 }
561
562 return options;
563}
564
d4c93774 565SR_PRIV struct sr_input_module input_vcd = {
99eaa206 566 .id = "vcd",
7db06394 567 .name = "VCD",
17bfaca6 568 .desc = "Value Change Dump",
c7bc82ff 569 .exts = (const char*[]){"vcd", NULL},
7db06394
BV
570 .metadata = { SR_INPUT_META_HEADER | SR_INPUT_META_REQUIRED },
571 .options = get_options,
99eaa206
PA
572 .format_match = format_match,
573 .init = init,
7db06394 574 .receive = receive,
7066fd46 575 .end = end,
7db06394 576 .cleanup = cleanup,
99eaa206 577};