]> sigrok.org Git - libsigrok.git/blame - src/output/vcd.c
output/csv: use intermediate time_t var, silence compiler warning
[libsigrok.git] / src / output / vcd.c
CommitLineData
4c9ffa83 1/*
50985c20 2 * This file is part of the libsigrok project.
4c9ffa83
UH
3 *
4 * Copyright (C) 2010 Uwe Hermann <uwe@hermann-uwe.de>
13d8e03c 5 * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
8c5bd3d9 6 * Copyright (C) 2017-2020 Gerhard Sittig <gerhard.sittig@gmx.net>
4c9ffa83
UH
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
2ea1fdf1 19 * along with this program; if not, see <http://www.gnu.org/licenses/>.
4c9ffa83
UH
20 */
21
8c5bd3d9
GS
22/*
23 * TODO
24 * - Check the mixed signal queue for completeness and correctness.
25 * - Tune the analog "immediate write" code path for throughput.
26 * - Remove excess diagnostics when the implementation is considered
27 * feature complete and reliable.
28 */
29
6ec6c43b 30#include <config.h>
8c5bd3d9
GS
31
32#include <ctype.h>
33#include <glib.h>
4c9ffa83
UH
34#include <stdlib.h>
35#include <string.h>
8c5bd3d9 36
c1aae900 37#include <libsigrok/libsigrok.h>
45c59c8b 38#include "libsigrok-internal.h"
4c9ffa83 39
3544f848 40#define LOG_PREFIX "output/vcd"
a944a84b 41
8c5bd3d9
GS
42static const int with_queue_stats = 0;
43static const int with_pool_stats = 0;
44
45struct vcd_channel_desc {
46 size_t index;
47 GString *name;
48 enum sr_channeltype type;
49 struct {
50 uint8_t logic;
51 double real;
52 } last;
53 uint64_t last_rcvd_snum;
54};
55
56/** Queued values for a given sample number. */
57struct vcd_queue_item {
58 uint64_t samplenum; /**!< sample number, _not_ timestamp */
59 GString *values; /**!< text of value changes */
60};
61
4c9ffa83 62struct context {
8c5bd3d9
GS
63 size_t enabled_count;
64 size_t logic_count;
65 size_t analog_count;
6cb45d96 66 gboolean header_done;
4ddea314 67 uint64_t period;
8c5bd3d9 68 struct vcd_channel_desc *channels;
b33e7d70 69 uint64_t samplerate;
8c5bd3d9
GS
70 GSList *free_list, *used_list;
71 size_t alloced, freed, reused, pooled;
72 GList *vcd_queue_list;
73 GList *vcd_queue_last;
74 gboolean immediate_write;
75 uint8_t *last_logic;
4c9ffa83
UH
76};
77
8c5bd3d9
GS
78/*
79 * Construct VCD signal identifiers from a sigrok channel index. The
80 * routine returns a GString which the caller is supposed to release.
81 *
82 * There are 94 printable ASCII characters. For larger channel index
83 * numbers multiple letters get concatenated (sticking with letters).
84 *
85 * The current implementation covers these ranges:
86 * - 94 single letter identifiers
87 * - 26 ^ 2 = 676, 94 + 676 = 770 for two letter identifiers
88 * - 26 ^ 3 = 17576, 770 + 17576 = 18346 for three letter identifiers
89 *
90 * This approach can get extended as needed when support for larger
91 * channel counts is desired. Any such extension remains transparent
92 * to call sites.
93 *
94 * TODO This implementation assumes that the software will run on a
95 * machine which uses the ASCII character set. Platforms that use other
96 * representations or non-contiguous character ranges for their alphabet
97 * cannot use a simple addition, instead need to execute table lookups.
98 */
99
100#define VCD_IDENT_CHAR_MIN '!'
101#define VCD_IDENT_CHAR_MAX '~'
102#define VCD_IDENT_COUNT_1CHAR (VCD_IDENT_CHAR_MAX + 1 - VCD_IDENT_CHAR_MIN)
103#define VCD_IDENT_ALPHA_MIN 'a'
104#define VCD_IDENT_ALPHA_MAX 'z'
105#define VCD_IDENT_COUNT_ALPHA (VCD_IDENT_ALPHA_MAX + 1 - VCD_IDENT_ALPHA_MIN)
106#define VCD_IDENT_COUNT_2CHAR (VCD_IDENT_COUNT_ALPHA * VCD_IDENT_COUNT_ALPHA)
107#define VCD_IDENT_COUNT_3CHAR (VCD_IDENT_COUNT_2CHAR * VCD_IDENT_COUNT_ALPHA)
108#define VCD_IDENT_COUNT (VCD_IDENT_COUNT_1CHAR + VCD_IDENT_COUNT_2CHAR + VCD_IDENT_COUNT_3CHAR)
109
110static GString *vcd_identifier(size_t idx)
111{
112 GString *symbol;
113 char c1, c2, c3;
114
115 symbol = g_string_sized_new(4);
116
117 /* First 94 channels, one printable character. */
118 if (idx < VCD_IDENT_COUNT_1CHAR) {
119 c1 = VCD_IDENT_CHAR_MIN + idx;
120 g_string_printf(symbol, "%c", c1);
121 return symbol;
122 }
123 idx -= VCD_IDENT_COUNT_1CHAR;
124
125 /* Next 676 channels, two lower case characters. */
126 if (idx < VCD_IDENT_COUNT_2CHAR) {
127 c2 = VCD_IDENT_ALPHA_MIN + (idx % VCD_IDENT_COUNT_ALPHA);
128 idx /= VCD_IDENT_COUNT_ALPHA;
129 c1 = VCD_IDENT_ALPHA_MIN + (idx % VCD_IDENT_COUNT_ALPHA);
130 idx /= VCD_IDENT_COUNT_ALPHA;
131 if (idx)
132 sr_dbg("VCD identifier creation BUG (two char).");
133 g_string_printf(symbol, "%c%c", c1, c2);
134 return symbol;
135 }
136 idx -= VCD_IDENT_COUNT_2CHAR;
137
138 /* Next 17576 channels, three lower case characters. */
139 if (idx < VCD_IDENT_COUNT_3CHAR) {
140 c3 = VCD_IDENT_ALPHA_MIN + (idx % VCD_IDENT_COUNT_ALPHA);
141 idx /= VCD_IDENT_COUNT_ALPHA;
142 c2 = VCD_IDENT_ALPHA_MIN + (idx % VCD_IDENT_COUNT_ALPHA);
143 idx /= VCD_IDENT_COUNT_ALPHA;
144 c1 = VCD_IDENT_ALPHA_MIN + (idx % VCD_IDENT_COUNT_ALPHA);
145 idx /= VCD_IDENT_COUNT_ALPHA;
146 if (idx)
147 sr_dbg("VCD identifier creation BUG (three char).");
148 g_string_printf(symbol, "%c%c%c", c1, c2, c3);
149 return symbol;
150 }
151 idx -= VCD_IDENT_COUNT_3CHAR;
152
153 /*
154 * TODO
155 * Add combinations with more positions or larger character sets
156 * when support for more channels is required.
157 */
158 sr_dbg("VCD identifier creation ENOTSUPP (need %zu more).", idx);
159 g_string_free(symbol, TRUE);
160
161 return NULL;
162}
163
164/*
165 * Notes on the VCD text output formatting routines:
166 * - Always start new text lines when timestamps get emitted.
167 * - Optionally terminate timestamp lines when the caller asked us to.
168 * - Prepend all values with whitespace, assume they follow a timestamp
169 * or a previously printed value. This works fine from the data point
170 * of view for the start of new lines, as well.
171 * - Put the mandatory whitespace between real (or vector) values and
172 * the following identifier. No whitespace for single bit values.
173 * - For real values callers need not specify "precision" nor the number
174 * of significant digits. The Verilog VCD spec specifically picked the
175 * "%.16g" format such that all bits of the internal presentation of
176 * the IEEE754 floating point value get communicated between the
177 * writer and the reader.
178 */
179
180static void append_vcd_timestamp(GString *s, double ts, gboolean lf)
181{
182
183 g_string_append_c(s, '\n');
184 g_string_append_c(s, '#');
185 g_string_append_printf(s, "%.0f", ts);
186 g_string_append_c(s, lf ? '\n' : ' ');
187}
188
189static void format_vcd_value_bit(GString *s, uint8_t bit_value, GString *id)
190{
191
192 g_string_append_c(s, bit_value ? '1' : '0');
193 g_string_append(s, id->str);
194}
195
196static void format_vcd_value_real(GString *s, double real_value, GString *id)
197{
198
199 g_string_append_c(s, 'r');
200 g_string_append_printf(s, "%.16g", real_value);
201 g_string_append_c(s, ' ');
202 g_string_append(s, id->str);
203}
204
a755b0e1 205static int init(struct sr_output *o, GHashTable *options)
4c9ffa83 206{
4c9ffa83 207 struct context *ctx;
8c5bd3d9 208 size_t alloc_size;
ba7dd8bb 209 struct sr_channel *ch;
4c9ffa83 210 GSList *l;
8c5bd3d9
GS
211 size_t num_enabled, num_logic, num_analog, desc_idx;
212 struct vcd_channel_desc *desc;
757b8c62 213
a755b0e1
BV
214 (void)options;
215
8c5bd3d9
GS
216 /* Determine the number of involved channels. */
217 num_enabled = 0;
218 num_logic = 0;
219 num_analog = 0;
ba7dd8bb
UH
220 for (l = o->sdi->channels; l; l = l->next) {
221 ch = l->data;
ba7dd8bb 222 if (!ch->enabled)
757b8c62 223 continue;
8c5bd3d9
GS
224 if (ch->type == SR_CHANNEL_LOGIC) {
225 num_logic++;
226 } else if (ch->type == SR_CHANNEL_ANALOG) {
227 num_analog++;
228 } else {
229 continue;
230 }
231 num_enabled++;
4c9ffa83 232 }
8c5bd3d9
GS
233 if (num_enabled > VCD_IDENT_COUNT) {
234 sr_err("Only up to %d VCD signals supported.", VCD_IDENT_COUNT);
e46b8fb1 235 return SR_ERR;
fbf1ff5d 236 }
4c9ffa83 237
8c5bd3d9
GS
238 /* Allocate space for channel descriptions. */
239 ctx = g_malloc0(sizeof(*ctx));
d686c5ec 240 o->priv = ctx;
8c5bd3d9
GS
241 ctx->enabled_count = num_enabled;
242 ctx->logic_count = num_logic;
243 ctx->analog_count = num_analog;
244 alloc_size = sizeof(ctx->channels[0]) * ctx->enabled_count;
245 ctx->channels = g_malloc0(alloc_size);
6cb45d96 246
8c5bd3d9
GS
247 /*
248 * Reiterate input descriptions, to fill in output descriptions.
249 * Map channel indices, and assign symbols to VCD channels.
250 */
251 desc_idx = 0;
252 for (l = o->sdi->channels; l; l = l->next) {
6cb45d96 253 ch = l->data;
6cb45d96
BV
254 if (!ch->enabled)
255 continue;
8c5bd3d9
GS
256 desc = &ctx->channels[desc_idx];
257 desc->index = ch->index;
258 desc->name = vcd_identifier(desc_idx);
259 desc->type = ch->type;
260 /*
261 * Make sure to _not_ match next time, to have initial
262 * values dumped when the first sample gets received.
263 */
264 if (desc->type == SR_CHANNEL_LOGIC && num_logic) {
265 num_logic--;
266 desc->last.logic = ~0;
267 } else if (desc->type == SR_CHANNEL_ANALOG && num_analog) {
268 num_analog--;
269 /* "Construct" NaN, avoid a compile time error. */
270 desc->last.real = 0.0;
271 desc->last.real = 0.0 / desc->last.real;
272 } else {
273 g_string_free(desc->name, TRUE);
274 memset(desc, 0, sizeof(*desc));
275 continue;
276 }
277 desc_idx++;
6cb45d96
BV
278 }
279
8c5bd3d9
GS
280 /*
281 * Keep channel counts at hand, and a flag which allows to tune
282 * for special cases' speedup in .receive().
283 */
284 ctx->immediate_write = FALSE;
285 if (ctx->analog_count == 0)
286 ctx->immediate_write = TRUE;
287 if (ctx->logic_count == 0 && ctx->analog_count == 1)
288 ctx->immediate_write = TRUE;
289
290 /*
291 * Keep a copy of the last logic data bitmap around. To avoid
292 * iterating over individual bits when nothing in the set has
293 * changed. The overhead of two byte array compares should
294 * outweight the tenfold bit count compared to byte counts.
295 */
296 alloc_size = (ctx->logic_count + 7) / 8;
297 ctx->last_logic = g_malloc0(alloc_size);
298 if (ctx->logic_count && !ctx->last_logic)
299 return SR_ERR_MALLOC;
300
6cb45d96
BV
301 return SR_OK;
302}
303
17c30d05
GS
304/*
305 * VCD can only handle 1/10/100 factors in the s to fs range. Find a
306 * suitable timescale which satisfies this resolution constraint, yet
307 * won't result in excessive overhead.
308 */
309static uint64_t get_timescale_freq(uint64_t samplerate)
310{
311 uint64_t timescale;
8c5bd3d9 312 size_t max_up_scale;
17c30d05
GS
313
314 /* Go to the next full decade. */
315 timescale = 1;
316 while (timescale < samplerate) {
317 timescale *= 10;
318 }
319
320 /*
321 * Avoid loss of precision, go up a few more decades when needed.
322 * For example switch to 10GHz timescale when samplerate is 400MHz.
323 * Stop after at most factor 100 to not loop endlessly for odd
324 * samplerates, yet provide good enough accuracy.
325 */
326 max_up_scale = 2;
327 while (max_up_scale--) {
328 if (timescale / samplerate * samplerate == timescale)
329 break;
330 timescale *= 10;
331 }
332
333 return timescale;
334}
335
8c5bd3d9 336/* Emit a VCD file header. */
a755b0e1 337static GString *gen_header(const struct sr_output *o)
6cb45d96
BV
338{
339 struct context *ctx;
340 struct sr_channel *ch;
341 GVariant *gvar;
342 GString *header;
343 GSList *l;
344 time_t t;
8c5bd3d9 345 size_t num_channels, i;
6cb45d96 346 char *samplerate_s, *frequency_s, *timestamp;
8c5bd3d9
GS
347 struct vcd_channel_desc *desc;
348 char *type_text, *size_text;
349 int ret;
6cb45d96 350
d686c5ec 351 ctx = o->priv;
4c9ffa83 352
8c5bd3d9
GS
353 /* Get channel count, and samplerate if not done yet. */
354 num_channels = g_slist_length(o->sdi->channels);
355 if (!ctx->samplerate) {
356 ret = sr_config_get(o->sdi->driver, o->sdi, NULL,
357 SR_CONF_SAMPLERATE, &gvar);
358 if (ret == SR_OK) {
359 ctx->samplerate = g_variant_get_uint64(gvar);
360 g_variant_unref(gvar);
361 }
362 }
363 ctx->period = get_timescale_freq(ctx->samplerate);
fbf1ff5d 364 t = time(NULL);
133a37bf 365 timestamp = g_strdup(ctime(&t));
8c5bd3d9
GS
366 timestamp[strlen(timestamp) - 1] = '\0';
367 samplerate_s = NULL;
368 if (ctx->samplerate)
369 samplerate_s = sr_samplerate_string(ctx->samplerate);
370 frequency_s = sr_period_string(1, ctx->period);
371
372 /* Construct the VCD output file header. */
373 header = g_string_sized_new(512);
6cb45d96 374 g_string_printf(header, "$date %s $end\n", timestamp);
8c5bd3d9
GS
375 g_string_append_printf(header, "$version %s %s $end\n",
376 PACKAGE_NAME, sr_package_version_string_get());
377 g_string_append_printf(header, "$comment\n");
378 g_string_append_printf(header,
379 " Acquisition with %zu/%zu channels%s%s\n",
380 ctx->enabled_count, num_channels,
381 samplerate_s ? " at " : "", samplerate_s ? : "");
382 g_string_append_printf(header, "$end\n");
383 g_string_append_printf(header, "$timescale %s $end\n", frequency_s);
384
385 /* List generated VCD signals within a scope. */
386 g_string_append_printf(header, "$scope module %s $end\n", PACKAGE_NAME);
387 i = 0;
388 for (l = o->sdi->channels; l; l = l->next) {
389 ch = l->data;
390 if (!ch->enabled)
391 continue;
392 desc = &ctx->channels[i++];
393 if (desc->type == SR_CHANNEL_LOGIC) {
394 type_text = "wire";
395 size_text = "1";
396 } else if (desc->type == SR_CHANNEL_ANALOG) {
397 type_text = "real";
398 size_text = "64";
399 } else {
400 i--;
401 continue;
402 }
403 g_string_append_printf(header, "$var %s %s %s %s $end\n",
404 type_text, size_text, desc->name->str, ch->name);
405 }
406 g_string_append(header, "$upscope $end\n");
407
408 g_string_append(header, "$enddefinitions $end\n");
409
133a37bf 410 g_free(timestamp);
8c5bd3d9
GS
411 g_free(samplerate_s);
412 g_free(frequency_s);
4c9ffa83 413
8c5bd3d9
GS
414 return header;
415}
6cb45d96 416
8c5bd3d9
GS
417/*
418 * Gets called when a session feed packet was received. Either creates
419 * a VCD file header (once in the output module's lifetime), or an empty
420 * GString. Callers will append the text representation of sample data
421 * to that string as needed.
422 */
423static GString *chk_header(const struct sr_output *o)
424{
425 struct context *ctx;
426 GString *s;
427
428 ctx = o->priv;
429
430 if (!ctx->header_done) {
431 ctx->header_done = TRUE;
432 s = gen_header(o);
433 } else {
434 s = g_string_sized_new(512);
435 }
436
437 return s;
438}
439
440/*
441 * Helpers to "merge sort" sample data that we have received in chunks
442 * at different times in different code paths. Queue the data until we
443 * have seen samples from all involved channels for a given samplenumber.
444 * Data for a given sample number can only get emitted when we are sure
445 * no other channel's data can arrive any more.
446 */
447
448static struct vcd_queue_item *queue_alloc_item(struct context *ctx, uint64_t snum)
449{
450 GSList *node;
451 struct vcd_queue_item *item;
452
453 /* Get an item from the free list if available. */
454 node = ctx->free_list;
455 if (node) {
456 ctx->reused++;
457
458 /* Unlink GSList node from the free list. */
459 ctx->free_list = node->next;
460 node->next = NULL;
461 item = node->data;
462 node->data = NULL;
463
464 /* Setup content of the item. */
465 item->samplenum = snum;
466 if (!item->values)
467 item->values = g_string_sized_new(32);
468 else
469 g_string_truncate(item->values, 0);
470
471 /* Keep GSList node in the used list (avoid free/alloc). */
472 node->next = ctx->used_list;
473 ctx->used_list = node;
474
475 return item;
476 }
477
478 /* Dynamic allocation of an item. */
479 ctx->alloced++;
480 item = g_malloc0(sizeof(*item));
481 if (!item)
482 return NULL;
483 item->samplenum = snum;
484 item->values = g_string_sized_new(32);
485
486 /* Create a used list item, to later move to the free list. */
487 ctx->used_list = g_slist_prepend(ctx->used_list, item);
488
489 return item;
490}
491
492static void queue_free_item(struct context *ctx, struct vcd_queue_item *item)
493{
494 GSList *node;
495
496 /*
497 * Put item back into the free list. We can assume to find a
498 * used list node, it got allocated when the item was acquired.
499 */
500 node = ctx->used_list;
501 if (node) {
502 ctx->pooled++;
503
504 ctx->used_list = node->next;
505 node->next = NULL;
506 node->data = item;
507
508 item->samplenum = 0;
509 g_string_truncate(item->values, 0);
510
511 node->next = ctx->free_list;
512 ctx->free_list = node;
513
514 return;
515 }
516
517 /*
518 * Release dynamically allocated resources. Could also be used
519 * to release free list items when the use list is empty.
520 */
521 ctx->freed++;
522 if (item->values)
523 g_string_free(item->values, TRUE);
524 g_free(item);
525}
526
527static void queue_drain_pool_cb(gpointer data, gpointer cb_data)
528{
529 struct context *ctx;
530 struct vcd_queue_item *item;
531
532 item = data;
533 ctx = cb_data;
534 queue_free_item(ctx, item);
535}
536
537static void queue_drain_pool(struct context *ctx)
538{
539 GSList *list;
540
541 /*
542 * Grab the list and "empty" the context member. Then
543 * iterate over the items, have dymamic memory released.
544 * Then free the GSList nodes (but not their data parts).
545 * Do this for the used and the free lists.
546 */
547 list = ctx->used_list;
548 ctx->used_list = NULL;
549 g_slist_foreach(list, queue_drain_pool_cb, ctx);
550 g_slist_free(list);
551
552 list = ctx->free_list;
553 ctx->free_list = NULL;
554 g_slist_foreach(list, queue_drain_pool_cb, ctx);
555 g_slist_free(list);
556}
557
558static int cmp_snum(gconstpointer l, gconstpointer d)
559{
560 const struct vcd_queue_item *list_item;
561 const uint64_t *snum_ptr;
562
563 list_item = l;
564 snum_ptr = d;
565 if (list_item->samplenum > *snum_ptr)
566 return +1;
567 if (list_item->samplenum < *snum_ptr)
568 return -1;
569 return 0;
570}
571
572static int cmp_items(gconstpointer a, gconstpointer b)
573{
574 const struct vcd_queue_item *item_a, *item_b;
575
576 item_a = a;
577 item_b = b;
578 if (item_a->samplenum > item_b->samplenum)
579 return +1;
580 if (item_a->samplenum < item_b->samplenum)
581 return -1;
582 return 0;
583}
584
585/*
586 * Position the current pointer of the VCD value queue to a specific
587 * sample number. Create a new queue item when needed. The logic assumes
588 * a specific use pattern: Reception of striped sample data for channels
589 * and processing in strict order of sample numbers within a channel.
590 * Lower sample numbers near the start of the queue when channels change
591 * between session feed packets, before another linear sequence follows.
592 *
593 * Naive use of convenience glib routines would severely lose performance.
594 * That's why custom code is used, which is as complex as it needs to be,
595 * yet shall execute faster than a simpler implementation. For trivial
596 * cases (logic only, one analog channel only) this queue is bypassed.
597 */
598static int queue_samplenum(struct context *ctx, uint64_t snum)
599{
600 struct vcd_queue_item *item, *add_item;
601 GList *walk_list, *after_snum, *before_snum, *add_list;
602 GList *last;
603 gboolean add_after_last, do_search;
604
605 /* Already at that position? */
606 item = ctx->vcd_queue_last ? ctx->vcd_queue_last->data : NULL;
607 if (item && item->samplenum == snum)
608 return SR_OK;
609
610 /*
611 * Search after the current position in the remaining queue. The
612 * custom code uses the queue's being sorted by sample number.
613 * Narrow down a later insert position as much as possible. This
614 * avoids linear search in huge spaces later on.
615 */
616 last = NULL;
617 add_after_last = FALSE;
618 after_snum = NULL;
619 before_snum = NULL;
620 walk_list = ctx->vcd_queue_last;
621 while (walk_list) {
622 item = walk_list->data;
623 if (!item)
624 break;
625 if (item->samplenum == snum) {
626 ctx->vcd_queue_last = walk_list;
627 return SR_OK;
628 }
629 last = walk_list;
630 if (item->samplenum < snum)
631 before_snum = walk_list;
632 if (item->samplenum > snum) {
633 after_snum = walk_list;
634 break;
6b5e3cee 635 }
8c5bd3d9
GS
636 if (!walk_list->next)
637 add_after_last = TRUE;
638 walk_list = walk_list->next;
6cb45d96 639 }
8c5bd3d9
GS
640
641 /*
642 * No exact match at or beyond the current position. Run another
643 * search from the start of the queue, again restrict the space
644 * which is searched, and narrow down the insert position when
645 * no match is found.
646 *
647 * If the searched sample number is larger than any we have seen
648 * before, or was in the above covered range but was not found,
649 * then we know that another queue item needs to get added, and
650 * where to put it. In that case we need not iterate the earlier
651 * list items.
652 */
653 walk_list = ctx->vcd_queue_list;
654 do_search = TRUE;
655 if (add_after_last)
656 do_search = FALSE;
657 if (before_snum)
658 do_search = FALSE;
659 while (do_search && walk_list && walk_list != ctx->vcd_queue_last) {
660 item = walk_list->data;
661 if (!item)
662 break;
663 if (item->samplenum == snum) {
664 ctx->vcd_queue_last = walk_list;
665 return SR_OK;
666 }
667 if (item->samplenum < snum)
668 before_snum = walk_list;
669 if (item->samplenum > snum) {
670 after_snum = walk_list;
671 break;
672 }
673 walk_list = walk_list->next;
7aae7462 674 }
4c9ffa83 675
8c5bd3d9
GS
676 /*
677 * The complete existing queue was exhausted, no exact match was
678 * found. A new queue item must get inserted. Identify a good
679 * position where to start searching for the exact position to
680 * link the new item to the list. Assume that the combination of
681 * the glib routine's list traversal and the sample number check
682 * in the callback is expensive, reduce the amount of work done.
683 *
684 * If we have seen an item with a larger sample number than the
685 * wanted, check its immediate predecessor. If this has a smaller
686 * sample number, then we found a perfect location to insert the
687 * new item. If we know that the new item must be inserted after
688 * the last traversed queue item, start there.
689 */
690 if (!before_snum) do {
691 if (add_after_last)
692 break;
693 if (!after_snum)
694 break;
695 walk_list = after_snum->prev;
696 if (!walk_list)
697 break;
698 item = walk_list->data;
699 if (!item)
700 break;
701 if (item->samplenum == snum) {
702 ctx->vcd_queue_last = walk_list;
703 return SR_OK;
704 }
705 if (item->samplenum < snum)
706 before_snum = walk_list;
707 } while (0);
708 add_list = add_after_last ? last : before_snum;
709 if (!add_list) {
710 walk_list = ctx->vcd_queue_list;
711 while (walk_list) {
712 item = walk_list->data;
713 if (!item)
714 break;
715 if (item->samplenum == snum) {
716 ctx->vcd_queue_last = walk_list;
717 return SR_OK;
718 }
719 if (item->samplenum > snum) {
720 after_snum = walk_list;
721 break;
722 }
723 add_list = walk_list;
724 walk_list = walk_list->next;
725 }
726 }
727 if (add_list && (item = add_list->data) && item->samplenum == snum) {
728 ctx->vcd_queue_last = add_list;
729 return SR_OK;
730 }
fbf1ff5d 731
8c5bd3d9
GS
732 /*
733 * Create a new queue item for the so far untracked sample
734 * number. Immediately search for the inserted position (is
735 * unfortunately not returned from the insert call), and
736 * cache that position for subsequent lookups.
737 */
738 if (with_queue_stats)
739 sr_dbg("%s(), queue nr %" PRIu64, __func__, snum);
740 add_item = queue_alloc_item(ctx, snum);
741 if (!add_item)
742 return SR_ERR_MALLOC;
743 if (!add_list)
744 add_list = ctx->vcd_queue_list;
745 if (add_list && add_list->prev)
746 add_list = add_list->prev;
747 walk_list = g_list_insert_sorted(add_list, add_item, cmp_items);
748 if (!walk_list->prev)
749 ctx->vcd_queue_list = walk_list;
750 walk_list = g_list_find_custom(walk_list, &snum, cmp_snum);
751 item = walk_list ? walk_list->data : NULL;
752 if (item && item->samplenum == snum) {
753 ctx->vcd_queue_last = walk_list;
754 }
755 return SR_OK;
756}
fbf1ff5d 757
8c5bd3d9
GS
758/*
759 * Prepare to append another text fragment for a value change to the
760 * queue item which corresponds to the current sample number. Return
761 * the GString which the caller then will append to.
762 */
763static GString *queue_value_text_prep(struct context *ctx)
764{
765 struct vcd_queue_item *item;
766 GString *buff;
767
768 /* Cope with not-yet-positioned write pointers. */
769 item = ctx->vcd_queue_last ? ctx->vcd_queue_last->data : NULL;
770 if (!item)
771 return NULL;
772
773 /* Create a GString if not done already. */
774 buff = item->values;
775 if (!buff) {
776 buff = g_string_sized_new(20);
777 item->values = buff;
778 }
779
780 /* Separate items with spaces (if previous content is present). */
781 if (buff->len)
782 g_string_append_c(buff, ' ');
783
784 return buff;
785}
786
787static double snum_to_ts(struct context *ctx, uint64_t snum)
788{
789 double ts;
790
791 ts = (double)snum;
792 ts /= ctx->samplerate;
793 ts *= ctx->period;
794
795 return ts;
796}
797
798/*
799 * Unqueue one item of the VCD values queue which corresponds to one
800 * sample number. Append all of the text to the passed in GString.
801 */
802static int unqueue_item(struct context *ctx,
803 struct vcd_queue_item *item, GString *s)
804{
805 double ts;
806 GString *buff;
807 gboolean is_empty;
808
809 /*
810 * Start the sample number's string with the timestamp. Append
811 * all value changes. Terminate lines for items which have a
812 * timestamp but no value changes, assuming this is the last
813 * entry which corresponds to SR_DF_END.
814 */
815 ts = snum_to_ts(ctx, item->samplenum);
816 buff = item->values;
817 is_empty = !buff || !buff->len || !buff->str || !*buff->str;
818 append_vcd_timestamp(s, ts, is_empty);
819 if (!is_empty)
820 g_string_append(s, buff->str);
821
822 return SR_OK;
823}
824
825/*
826 * Get the last sample number which logic data was received for. This
827 * implementation assumes that all logic channels get received within
828 * exactly one packet of corresponding unitsize.
829 */
830static uint64_t get_last_snum_logic(struct context *ctx)
831{
832 size_t i;
833 struct vcd_channel_desc *desc;
834
835 for (i = 0; i < ctx->enabled_count; i++) {
836 desc = &ctx->channels[i];
837 if (desc->type != SR_CHANNEL_LOGIC)
3699a8a1 838 continue;
8c5bd3d9
GS
839 return desc->last_rcvd_snum;
840 }
841
842 return 0;
843}
844
845/*
846 * Update the last sample number which logic data was received for.
847 */
848static void upd_last_snum_logic(struct context *ctx, uint64_t inc)
849{
850 size_t i;
851 struct vcd_channel_desc *desc;
852
853 for (i = 0; i < ctx->enabled_count; i++) {
854 desc = &ctx->channels[i];
855 if (desc->type != SR_CHANNEL_LOGIC)
d601c0e9 856 continue;
8c5bd3d9 857 desc->last_rcvd_snum += inc;
4c9ffa83 858 }
8c5bd3d9 859}
4c9ffa83 860
8c5bd3d9
GS
861/*
862 * Get and update the last sample number which analog data was received
863 * for on a specific channel (which the caller already has identified).
864 */
4c9ffa83 865
8c5bd3d9
GS
866static uint64_t get_last_snum_analog(struct vcd_channel_desc *desc)
867{
868
869 return desc->last_rcvd_snum;
870}
871
872static void upd_last_snum_analog(struct vcd_channel_desc *desc, uint64_t inc)
873{
874
875 if (!desc)
876 return;
877 desc->last_rcvd_snum += inc;
878}
879
880/*
881 * Determine the maximum sample number which data from all involved
882 * channels was received for.
883 */
884static uint64_t get_max_snum_export(struct context *ctx)
885{
886 uint64_t snum;
887 size_t i;
888 struct vcd_channel_desc *desc;
889
890 snum = ~UINT64_C(0);
891 for (i = 0; i < ctx->enabled_count; i++) {
892 desc = &ctx->channels[i];
893 if (snum > desc->last_rcvd_snum)
894 snum = desc->last_rcvd_snum;
895 }
896
897 return snum;
898}
899
900/*
901 * Determine the maximum sample number of any channel we may have
902 * received data for. Then pretend we had seen that number of samples
903 * on all channels. Such that the next export can flush all previously
904 * queued data up to and including the final number, which serves as
905 * some kind of termination of the VCD output data.
906 */
907static uint64_t get_max_snum_flush(struct context *ctx)
908{
909 uint64_t snum;
910 size_t i;
911 struct vcd_channel_desc *desc;
912
913 /* Determine the maximum sample number. */
914 snum = 0;
915 for (i = 0; i < ctx->enabled_count; i++) {
916 desc = &ctx->channels[i];
917 if (snum < desc->last_rcvd_snum)
918 snum = desc->last_rcvd_snum;
919 }
920
921 /* Record that number as "seen" with all channels. */
922 for (i = 0; i < ctx->enabled_count; i++) {
923 desc = &ctx->channels[i];
924 desc->last_rcvd_snum = snum + 1;
925 }
926
927 return snum;
928}
929
930/*
931 * Pass all queued value changes when we are certain we have received
932 * data from all channels.
933 */
934static int write_completed_changes(struct context *ctx, GString *out)
935{
936 uint64_t upto_snum;
937 GList **listref, *node;
938 struct vcd_queue_item *item;
939 int rc;
940 size_t dumped;
941
942 /* Determine the number which all data was received for so far. */
943 upto_snum = get_max_snum_export(ctx);
944 if (with_queue_stats)
945 sr_spew("%s(), check up to %" PRIu64, __func__, upto_snum);
946
947 /*
948 * Forward and consume those items from the head of the list
949 * which we completely have accumulated and are certain about.
950 */
951 dumped = 0;
952 listref = &ctx->vcd_queue_list;
953 while (*listref) {
954 /* Find items before the targetted sample number. */
955 node = *listref;
956 item = node->data;
957 if (!item)
958 break;
959 if (item->samplenum >= upto_snum)
960 break;
961
962 /*
963 * Unlink the item from the list. Void cached positions.
964 * Append its timestamp and values to the caller's text.
965 */
966 dumped++;
967 if (with_queue_stats)
968 sr_dbg("%s(), dump nr %" PRIu64,
969 __func__, item->samplenum);
970 if (ctx->vcd_queue_last == node)
971 ctx->vcd_queue_last = NULL;
972 *listref = g_list_remove_link(*listref, node);
973 rc = unqueue_item(ctx, item, out);
974 queue_free_item(ctx, item);
975 if (rc != SR_OK)
976 return rc;
977 }
978
979 return SR_OK;
4c9ffa83
UH
980}
981
8c5bd3d9
GS
982/* Get packets from the session feed, generate output text. */
983static int receive(const struct sr_output *o,
984 const struct sr_datafeed_packet *packet, GString **out)
4c9ffa83 985{
8c5bd3d9 986 struct context *ctx;
6cb45d96 987 const struct sr_datafeed_meta *meta;
d5585e32 988 const struct sr_datafeed_logic *logic;
8c5bd3d9 989 const struct sr_datafeed_analog *analog;
6cb45d96
BV
990 const struct sr_config *src;
991 GSList *l;
8c5bd3d9
GS
992 struct vcd_channel_desc *desc;
993 uint64_t snum_curr;
994 size_t count, index, p, unit_size;
995 gboolean changed;
996 GString *s_val;
997 uint8_t *sample, *last_logic, prevbit, curbit;
998 GSList *channels;
999 struct sr_channel *channel;
1000 int rc;
1001 float *floats, value;
1002 double ts;
4c9ffa83 1003
17f63de6 1004 *out = NULL;
d686c5ec 1005 if (!o || !o->priv)
191c7e5f 1006 return SR_ERR_BUG;
d686c5ec 1007 ctx = o->priv;
d5585e32 1008
6cb45d96
BV
1009 switch (packet->type) {
1010 case SR_DF_META:
1011 meta = packet->payload;
1012 for (l = meta->config; l; l = l->next) {
1013 src = l->data;
1014 if (src->key != SR_CONF_SAMPLERATE)
1015 continue;
1016 ctx->samplerate = g_variant_get_uint64(src->data);
1017 }
1018 break;
1019 case SR_DF_LOGIC:
8c5bd3d9 1020 *out = chk_header(o);
6cb45d96 1021
8c5bd3d9
GS
1022 logic = packet->payload;
1023 sample = logic->data;
1024 unit_size = logic->unitsize;
1025 count = logic->length / unit_size;
1026 snum_curr = get_last_snum_logic(ctx);
1027 upd_last_snum_logic(ctx, count);
2b78ffea 1028
8c5bd3d9
GS
1029 last_logic = ctx->last_logic;
1030 while (count--) {
1031 /* Check whether any logic value has changed. */
1032 changed = memcmp(last_logic, sample, unit_size) != 0;
1033 changed |= snum_curr == 0;
1034 if (changed)
1035 memcpy(last_logic, sample, unit_size);
2b78ffea 1036
8c5bd3d9
GS
1037 /*
1038 * Start or continue tracking that sample number.
1039 * Avoid string copies for logic-only setups.
1040 */
1041 if (changed) {
1042 if (ctx->immediate_write) {
1043 ts = snum_to_ts(ctx, snum_curr);
1044 append_vcd_timestamp(*out, ts, FALSE);
1045 } else {
1046 queue_samplenum(ctx, snum_curr);
1047 }
1048 }
08b488b8 1049
8c5bd3d9
GS
1050 /* Iterate over individual logic channels. */
1051 for (p = 0; changed && p < ctx->enabled_count; p++) {
aa097948
GS
1052 /*
1053 * TODO Check whether the mapping from
1054 * data image positions to channel numbers
1055 * is required. Experiments suggest that
1056 * the data image "is dense", and packs
1057 * bits of enabled channels, and leaves no
1058 * room for positions of disabled channels.
1059 */
8c5bd3d9
GS
1060 desc = &ctx->channels[p];
1061 if (desc->type != SR_CHANNEL_LOGIC)
1062 continue;
1063 index = desc->index;
1064 prevbit = desc->last.logic;
fbe2f794 1065
8c5bd3d9
GS
1066 /* Skip over unchanged values. */
1067 curbit = sample[index / 8];
1068 curbit = (curbit & (1 << (index % 8))) ? 1 : 0;
1069 if (snum_curr != 0 && prevbit == curbit)
6cb45d96 1070 continue;
8c5bd3d9 1071 desc->last.logic = curbit;
4c9ffa83 1072
8c5bd3d9
GS
1073 /*
1074 * Queue, or immediately emit the text for
1075 * the observed value change.
1076 */
1077 if (ctx->immediate_write) {
1078 g_string_append_c(*out, ' ');
1079 s_val = *out;
1080 } else {
1081 s_val = queue_value_text_prep(ctx);
1082 if (!s_val)
1083 break;
1084 }
1085 format_vcd_value_bit(s_val, curbit, desc->name);
1086 }
2b6363b4 1087
8c5bd3d9
GS
1088 /* Advance to next set of logic samples. */
1089 snum_curr++;
1090 sample += unit_size;
1091 }
1092 write_completed_changes(ctx, *out);
1093 break;
1094 case SR_DF_ANALOG:
1095 *out = chk_header(o);
2b6363b4 1096
8c5bd3d9
GS
1097 /*
1098 * This implementation expects one analog packet per
1099 * individual channel, with a number of samples each.
1100 * Lookup the VCD output channel description.
1101 */
1102 analog = packet->payload;
1103 count = analog->num_samples;
1104 channels = analog->meaning->channels;
1105 if (g_slist_length(channels) != 1) {
1106 sr_err("Analog packets must be single-channel.");
1107 return SR_ERR_ARG;
1108 }
1109 channel = g_slist_nth_data(channels, 0);
1110 desc = NULL;
1111 for (index = 0; index < ctx->enabled_count; index++) {
1112 desc = &ctx->channels[index];
1113 if ((int)desc->index == channel->index)
1114 break;
1115 }
1116 if (!desc)
1117 return SR_OK;
1118 if (desc->type != SR_CHANNEL_ANALOG)
1119 return SR_ERR;
1120 snum_curr = get_last_snum_analog(desc);
1121 upd_last_snum_analog(desc, count);
08b488b8 1122
8c5bd3d9
GS
1123 /*
1124 * Convert incoming data to an array of single precision
1125 * floating point values.
1126 */
1127 floats = g_try_malloc(sizeof(*floats) * analog->num_samples);
1128 if (!floats)
1129 return SR_ERR_MALLOC;
1130 rc = sr_analog_to_float(analog, floats);
1131 if (rc != SR_OK) {
1132 g_free(floats);
1133 return rc;
1134 }
2b6363b4 1135
8c5bd3d9
GS
1136 /*
1137 * Check for changes in the channel's values. Have the
1138 * sample number's timestamp and new value printed when
1139 * the value has changed.
1140 */
1141 for (index = 0; index < count; index++) {
1142 /* Check for changes in the channel's values. */
1143 value = floats[index];
1144 changed = value != desc->last.real;
1145 changed |= snum_curr + index == 0;
1146 if (!changed)
1147 continue;
1148 desc->last.real = value;
1149
1150 /* Queue, or emit the timestamp and the new value. */
1151 if (ctx->immediate_write) {
1152 ts = snum_to_ts(ctx, snum_curr + index);
1153 append_vcd_timestamp(*out, ts, FALSE);
1154 s_val = *out;
1155 } else {
1156 queue_samplenum(ctx, snum_curr + index);
1157 s_val = queue_value_text_prep(ctx);
1158 }
1159 format_vcd_value_real(s_val, value, desc->name);
6cb45d96 1160 }
8c5bd3d9
GS
1161
1162 g_free(floats);
1163 write_completed_changes(ctx, *out);
6cb45d96
BV
1164 break;
1165 case SR_DF_END:
8c5bd3d9
GS
1166 *out = chk_header(o);
1167 /* Push the final timestamp as length indicator. */
1168 snum_curr = get_max_snum_flush(ctx);
1169 queue_samplenum(ctx, snum_curr);
1170 /* Flush previously queued value changes. */
1171 write_completed_changes(ctx, *out);
6cb45d96 1172 break;
4c9ffa83
UH
1173 }
1174
17f63de6 1175 return SR_OK;
d5585e32
BV
1176}
1177
1178static int cleanup(struct sr_output *o)
1179{
1180 struct context *ctx;
8c5bd3d9 1181 struct vcd_channel_desc *desc;
d5585e32 1182
d686c5ec 1183 if (!o || !o->priv)
d5585e32
BV
1184 return SR_ERR_ARG;
1185
d686c5ec 1186 ctx = o->priv;
8c5bd3d9
GS
1187
1188 if (with_pool_stats)
1189 sr_info("STATS: alloc/reuse %zu/%zu, pool/free %zu/%zu",
1190 ctx->alloced, ctx->reused, ctx->pooled, ctx->freed);
1191 queue_drain_pool(ctx);
1192 if (with_pool_stats)
1193 sr_info("STATS: alloc/reuse %zu/%zu, pool/free %zu/%zu",
1194 ctx->alloced, ctx->reused, ctx->pooled, ctx->freed);
1195
1196 while (ctx->enabled_count--) {
1197 desc = &ctx->channels[ctx->enabled_count];
1198 g_string_free(desc->name, TRUE);
1199 }
1200 g_free(ctx->channels);
d5585e32 1201 g_free(ctx);
4c9ffa83 1202
e46b8fb1 1203 return SR_OK;
4c9ffa83
UH
1204}
1205
a755b0e1 1206struct sr_output_module output_vcd = {
cdb3573c 1207 .id = "vcd",
a755b0e1 1208 .name = "VCD",
b20eb520 1209 .desc = "Value Change Dump data",
8a174d23 1210 .exts = (const char*[]){"vcd", NULL},
3cd4b381 1211 .flags = 0,
a755b0e1 1212 .options = NULL,
d494a4aa 1213 .init = init,
17f63de6 1214 .receive = receive,
bbe6e336 1215 .cleanup = cleanup,
4c9ffa83 1216};