4
Implements a Value Change Dump file outout to generate
5
traces & curves and display them in gtkwave.
7
Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
9
This file is part of simavr.
11
simavr is free software: you can redistribute it and/or modify
12
it under the terms of the GNU General Public License as published by
13
the Free Software Foundation, either version 3 of the License, or
14
(at your option) any later version.
16
simavr is distributed in the hope that it will be useful,
17
but WITHOUT ANY WARRANTY; without even the implied warranty of
18
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
GNU General Public License for more details.
21
You should have received a copy of the GNU General Public License
22
along with simavr. If not, see <http://www.gnu.org/licenses/>.
30
#include "sim_vcd_file.h"
33
#include "sim_utils.h"
35
DEFINE_FIFO(avr_vcd_log_t, avr_vcd_fifo);
37
#define strdupa(__s) strcpy(alloca(strlen(__s)+1), __s)
41
struct avr_irq_t * irq,
48
const char * filename,
52
memset(vcd, 0, sizeof(avr_vcd_t));
54
vcd->filename = strdup(filename);
55
vcd->period = avr_usec_to_cycles(vcd->avr, period);
61
* Parse a VCD 'timing' line. The lines are assumed to be:
62
* #<absolute timestamp>[\n][<value x/0/1><signal alias character>|
63
* b[x/0/1]?<space><signal alias character]+
71
* This function tries to handle this transparently, and pushes the
72
* signal/values into the FIFO for processing by the timer when
74
* NOTE: Add 'floating' support here. Also, FIX THE TIMING.
76
static avr_cycle_count_t
77
avr_vcd_input_parse_line(
81
avr_cycle_count_t res = 0;
87
if (v->argv[0][0] == '#') {
88
res = atoll(v->argv[0] + 1) * vcd->vcd_to_us;
89
vcd->start = vcd->period;
93
for (int i = vi; i < v->argc; i++) {
94
char * a = v->argv[i];
105
floating |= (floating << 1) | 1;
106
} else if (*a == '0' || *a == '1') {
107
val = (val << 1) | (*a - '0');
115
if (!name && (i < v->argc - 1)) {
116
const char *n = v->argv[i+1];
117
if (strlen(n) == 1) {
118
// we've got a name, it was not attached
120
i++; // skip that one
125
si < vcd->signal_count &&
126
sigindex == -1; si++) {
127
if (vcd->signal[si].alias == name)
131
if (sigindex == -1) {
132
printf("Signal name '%c' value %x not found\n",
133
name? name : '?', val);
138
.sigindex = sigindex,
139
.floating = !!floating,
142
// printf("%10u %d\n", e.when, e.value);
143
avr_vcd_fifo_write(&vcd->log, e);
149
* Read some signals from the file and fill the FIFO with it, we read
150
* a completely arbitrary amount of stuff to fill the FIFO reasonably well
158
while (fgets(line, sizeof(line), vcd->input)) {
159
// printf("%s", line);
160
if (!line[0]) // technically can't happen, but make sure next line works
162
vcd->input_line = argv_parse(vcd->input_line, line);
163
avr_vcd_input_parse_line(vcd, vcd->input_line);
164
/* stop once the fifo is full enough */
165
if (avr_vcd_fifo_get_read_size(&vcd->log) >= 128)
168
return avr_vcd_fifo_isempty(&vcd->log);
172
* This is called when we need to change the state of one or more IRQ,
173
* so look in the FIFO to know 'our' stamp time, read as much as we can
174
* that is still on that same timestamp.
175
* When when the FIFO content has too far in the future, re-schedule the
176
* timer for that time and shoot of.
177
* Also try to top up the FIFO with new read stuff when it's drained
179
static avr_cycle_count_t
180
_avr_vcd_input_timer(
182
avr_cycle_count_t when,
185
avr_vcd_t * vcd = param;
187
// get some more if needed
188
if (avr_vcd_fifo_get_read_size(&vcd->log) < (vcd->signal_count * 16))
189
avr_vcd_input_read(vcd);
191
if (avr_vcd_fifo_isempty(&vcd->log)) {
192
printf("%s DONE but why are we here?\n", __func__);
196
avr_vcd_log_t log = avr_vcd_fifo_read_at(&vcd->log, 0);
197
uint64_t stamp = log.when;
198
while (!avr_vcd_fifo_isempty(&vcd->log)) {
199
log = avr_vcd_fifo_read_at(&vcd->log, 0);
200
if (log.when != stamp) // leave those in the FIFO
202
// we already have it
203
avr_vcd_fifo_read_offset(&vcd->log, 1);
204
avr_vcd_signal_p signal = &vcd->signal[log.sigindex];
205
avr_raise_irq_float(&signal->irq, log.value, log.floating);
208
if (avr_vcd_fifo_isempty(&vcd->log)) {
209
AVR_LOG(vcd->avr, LOG_TRACE,
210
"%s Finished reading, ending simavr\n",
212
avr->state = cpu_Done;
215
log = avr_vcd_fifo_read_at(&vcd->log, 0);
217
when += avr_usec_to_cycles(avr, log.when - stamp);
225
const char * filename, // filename to read
226
avr_vcd_t * vcd ) // vcd struct to initialize
228
memset(vcd, 0, sizeof(avr_vcd_t));
230
vcd->filename = strdup(filename);
232
vcd->input = fopen(vcd->filename, "r");
240
while (fgets(line, sizeof(line), vcd->input)) {
241
if (!line[0]) // technically can't happen, but make sure next line works
243
v = argv_parse(v, line);
245
// we are done reading headers, got our first timestamp
246
if (v->line[0] == '#') {
248
avr_vcd_input_parse_line(vcd, v);
249
avr_cycle_timer_register_usec(vcd->avr,
250
vcd->period, _avr_vcd_input_timer, vcd);
253
// ignore multiline stuff
254
if (v->line[0] != '$')
257
const char * end = !strcmp(v->argv[v->argc - 1], "$end") ?
258
v->argv[v->argc - 1] : NULL;
259
const char *keyword = v->argv[0];
266
if (!strcmp(keyword, "$timescale")) {
269
char *si = v->argv[1];
270
while (si && *si && isdigit(*si))
271
cnt = (cnt * 10) + (*si++ - '0');
276
// if (!strcmp(si, "ns")) // TODO: Check that,
277
// vcd->vcd_to_us = cnt;
278
// printf("cnt %dus; unit %s\n", (int)cnt, si);
279
} else if (!strcmp(keyword, "$var")) {
280
const char *name = v->argv[4];
282
vcd->signal[vcd->signal_count].alias = v->argv[3][0];
283
vcd->signal[vcd->signal_count].size = atoi(v->argv[2]);
284
strncpy(vcd->signal[vcd->signal_count].name, name,
285
sizeof(vcd->signal[0].name));
293
for (int i = 0; i < vcd->signal_count; i++) {
294
AVR_LOG(vcd->avr, LOG_TRACE, "%s %2d '%c' %s : size %d\n",
296
vcd->signal[i].alias, vcd->signal[i].name,
297
vcd->signal[i].size);
298
/* format is <four-character ioctl>[_<IRQ index>] */
299
/*if (strlen(vcd->signal[i].name) >= 4) {
300
char *dup = strdupa(vcd->signal[i].name);
301
char *ioctl = strsep(&dup, "_");
305
if (strlen(ioctl) == 4) {
306
uint32_t ioc = AVR_IOCTL_DEF(
307
ioctl[0], ioctl[1], ioctl[2], ioctl[3]);
308
avr_irq_t * irq = avr_io_getirq(vcd->avr, ioc, index);
310
vcd->signal[i].irq.flags = IRQ_FLAG_INIT;
311
avr_connect_irq(&vcd->signal[i].irq, irq);
313
AVR_LOG(vcd->avr, LOG_WARNING,
314
"%s IRQ was not found\n",
315
vcd->signal[i].name);
318
AVR_LOG(vcd->avr, LOG_WARNING,
319
"%s is an invalid IRQ format\n",
320
vcd->signal[i].name);
332
/* dispose of any link and hooks */
333
for (int i = 0; i < vcd->signal_count; i++) {
334
avr_vcd_signal_t * s = &vcd->signal[i];
336
avr_free_irq(&s->irq, 1);
341
vcd->filename = NULL;
346
_avr_vcd_get_float_signal_text(
347
avr_vcd_signal_t * s,
355
for (int i = s->size; i > 0; i--)
365
_avr_vcd_get_signal_text(
366
avr_vcd_signal_t * s,
375
for (int i = s->size; i > 0; i--)
376
*dst++ = value & (1 << (i-1)) ? '1' : '0';
388
#if AVR_VCD_MAX_SIGNALS > 32
393
uint64_t oldbase = 0; // make sure it's different
396
if (avr_vcd_fifo_isempty(&vcd->log) || !vcd->output)
399
while (!avr_vcd_fifo_isempty(&vcd->log)) {
400
avr_vcd_log_t l = avr_vcd_fifo_read(&vcd->log);
401
// 10ns base -- 100MHz should be enough
402
uint64_t base = avr_cycles_to_nsec(vcd->avr, l.when - vcd->start) / 10;
405
* if that trace was seen in this nsec already, we fudge the
406
* base time to make sure the new value is offset by one nsec,
407
* to make sure we get at least a small pulse on the waveform.
409
* This is a bit of a fudge, but it is the only way to represent
410
* very short "pulses" that are still visible on the waveform.
412
if (base == oldbase &&
413
(seen & (1 << l.sigindex)))
414
base++; // this forces a new timestamp
416
if (base > oldbase || !seen) {
418
fprintf(vcd->output, "#%" PRIu64 "\n", base);
421
// mark this trace as seen for this timestamp
422
seen |= (1 << l.sigindex);
423
fprintf(vcd->output, "%s\n",
425
_avr_vcd_get_float_signal_text(
426
&vcd->signal[l.sigindex],
428
_avr_vcd_get_signal_text(
429
&vcd->signal[l.sigindex],
434
static avr_cycle_count_t
437
avr_cycle_count_t when,
440
avr_vcd_t * vcd = param;
441
avr_vcd_flush_log(vcd);
442
return when + vcd->period;
447
struct avr_irq_t * irq,
451
avr_vcd_t * vcd = (avr_vcd_t *)param;
456
avr_vcd_signal_t * s = (avr_vcd_signal_t*)irq;
458
.sigindex = s->irq.irq,
459
.when = vcd->avr->cycle,
461
.floating = !!(avr_irq_get_flags(irq) & IRQ_FLAG_FLOATING),
463
if (avr_vcd_fifo_isfull(&vcd->log)) {
464
AVR_LOG(vcd->avr, LOG_WARNING,
465
"%s FIFO Overload, flushing!\n",
467
/* Decrease period by a quarter, for next time */
468
vcd->period -= vcd->period >> 2;
469
avr_vcd_flush_log(vcd);
471
avr_vcd_fifo_write(&vcd->log, l);
477
avr_irq_t * signal_irq,
481
if (vcd->signal_count == AVR_VCD_MAX_SIGNALS)
483
int index = vcd->signal_count++;
484
avr_vcd_signal_t * s = &vcd->signal[index];
485
strncpy(s->name, name, sizeof(s->name));
486
s->size = signal_bit_size;
487
s->alias = ' ' + vcd->signal_count ;
489
/* manufacture a nice IRQ name */
490
int l = strlen(name);
491
char iname[10 + l + 1];
492
if (signal_bit_size > 1)
493
sprintf(iname, "%d>vcd.%s", signal_bit_size, name);
495
sprintf(iname, ">vcd.%s", name);
497
const char * names[1] = { iname };
498
avr_init_irq(&vcd->avr->irq_pool, &s->irq, index, 1, names);
499
avr_irq_register_notify(&s->irq, _avr_vcd_notify, vcd);
501
avr_connect_irq(signal_irq, &s->irq);
510
vcd->start = vcd->avr->cycle;
511
avr_vcd_fifo_reset(&vcd->log);
515
* nothing to do here, the first cycle timer will take care
522
vcd->output = fopen(vcd->filename, "w");
523
if (vcd->output == NULL) {
524
perror(vcd->filename);
528
fprintf(vcd->output, "$timescale 10ns $end\n"); // 10ns base, aka 100MHz
529
fprintf(vcd->output, "$scope module logic $end\n");
531
for (int i = 0; i < vcd->signal_count; i++) {
532
fprintf(vcd->output, "$var wire %d %c %s $end\n",
533
vcd->signal[i].size, vcd->signal[i].alias, vcd->signal[i].name);
536
fprintf(vcd->output, "$upscope $end\n");
537
fprintf(vcd->output, "$enddefinitions $end\n");
539
fprintf(vcd->output, "$dumpvars\n");
540
for (int i = 0; i < vcd->signal_count; i++) {
541
avr_vcd_signal_t * s = &vcd->signal[i];
543
fprintf(vcd->output, "%s\n",
544
_avr_vcd_get_float_signal_text(s, out));
546
fprintf(vcd->output, "$end\n");
547
avr_cycle_timer_register(vcd->avr, vcd->period, _avr_vcd_timer, vcd);
555
avr_cycle_timer_cancel(vcd->avr, _avr_vcd_timer, vcd);
556
avr_cycle_timer_cancel(vcd->avr, _avr_vcd_input_timer, vcd);
558
avr_vcd_flush_log(vcd);
561
free(vcd->input_line);
562
vcd->input_line = NULL;