~arcachofo/simulide/1.1.0

« back to all changes in this revision

Viewing changes to src/simavr/sim/sim_vcd_file.c

  • Committer: arcachofo
  • Date: 2021-01-01 14:23:42 UTC
  • Revision ID: arcachofo@simulide.com-20210101142342-ozfljnll44g5lbl3
Initial Commit 0.5.15-RC3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
        sim_vcd_file.c
 
3
 
 
4
        Implements a Value Change Dump file outout to generate
 
5
        traces & curves and display them in gtkwave.
 
6
 
 
7
        Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
 
8
 
 
9
        This file is part of simavr.
 
10
 
 
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.
 
15
 
 
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.
 
20
 
 
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/>.
 
23
 */
 
24
 
 
25
#include <stdio.h>
 
26
#include <string.h>
 
27
#include <stdlib.h>
 
28
#include <inttypes.h>
 
29
#include <ctype.h>
 
30
#include "sim_vcd_file.h"
 
31
#include "sim_avr.h"
 
32
#include "sim_time.h"
 
33
#include "sim_utils.h"
 
34
 
 
35
DEFINE_FIFO(avr_vcd_log_t, avr_vcd_fifo);
 
36
 
 
37
#define strdupa(__s) strcpy(alloca(strlen(__s)+1), __s)
 
38
 
 
39
static void
 
40
_avr_vcd_notify(
 
41
                struct avr_irq_t * irq,
 
42
                uint32_t value,
 
43
                void * param);
 
44
 
 
45
int
 
46
avr_vcd_init(
 
47
                struct avr_t * avr,
 
48
                const char * filename,
 
49
                avr_vcd_t * vcd,
 
50
                uint32_t period)
 
51
{
 
52
        memset(vcd, 0, sizeof(avr_vcd_t));
 
53
        vcd->avr = avr;
 
54
        vcd->filename = strdup(filename);
 
55
        vcd->period = avr_usec_to_cycles(vcd->avr, period);
 
56
 
 
57
        return 0;
 
58
}
 
59
 
 
60
/*
 
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]+
 
64
 * For example:
 
65
 * #1234 1' 0$
 
66
 * Or:
 
67
 * #1234
 
68
 * b1101x1 '
 
69
 * 0$
 
70
 *
 
71
 * This function tries to handle this transparently, and pushes the
 
72
 * signal/values into the FIFO for processing by the timer when
 
73
 * convenient.
 
74
 * NOTE: Add 'floating' support here. Also, FIX THE TIMING.
 
75
 */
 
76
static avr_cycle_count_t
 
77
avr_vcd_input_parse_line(
 
78
                avr_vcd_t * vcd,
 
79
                argv_p v )
 
80
{
 
81
        avr_cycle_count_t res = 0;
 
82
        int vi = 0;
 
83
 
 
84
        if (v->argc == 0)
 
85
                return res;
 
86
 
 
87
        if (v->argv[0][0] == '#') {
 
88
                res = atoll(v->argv[0] + 1) * vcd->vcd_to_us;
 
89
                vcd->start = vcd->period;
 
90
                vcd->period = res;
 
91
                vi++;
 
92
        }
 
93
        for (int i = vi; i < v->argc; i++) {
 
94
                char * a = v->argv[i];
 
95
                uint32_t val = 0;
 
96
                int floating = 0;
 
97
                char name = 0;
 
98
                int sigindex = -1;
 
99
 
 
100
                if (*a == 'b')
 
101
                        a++;
 
102
                while (*a) {
 
103
                        if (*a == 'x') {
 
104
                                val <<= 1;
 
105
                                floating |= (floating << 1) | 1;
 
106
                        } else if (*a == '0' || *a == '1') {
 
107
                                val = (val << 1) | (*a - '0');
 
108
                                floating <<= 1;
 
109
                        } else {
 
110
                                name = *a;
 
111
                                break;
 
112
                        }
 
113
                        a++;
 
114
                }
 
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
 
119
                                name = *n;
 
120
                                i++;    // skip that one
 
121
                        }
 
122
                }
 
123
                if (name) {
 
124
                        for (int si = 0;
 
125
                                                si < vcd->signal_count &&
 
126
                                                sigindex == -1; si++) {
 
127
                                if (vcd->signal[si].alias == name)
 
128
                                        sigindex = si;
 
129
                        }
 
130
                }
 
131
                if (sigindex == -1) {
 
132
                        printf("Signal name '%c' value %x not found\n",
 
133
                                        name? name : '?', val);
 
134
                        continue;
 
135
                }
 
136
                avr_vcd_log_t e = {
 
137
                                .when = vcd->period,
 
138
                                .sigindex = sigindex,
 
139
                                .floating = !!floating,
 
140
                                .value = val,
 
141
                };
 
142
        //      printf("%10u %d\n", e.when, e.value);
 
143
                avr_vcd_fifo_write(&vcd->log, e);
 
144
        }
 
145
        return res;
 
146
}
 
147
 
 
148
/*
 
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
 
151
 */
 
152
static int
 
153
avr_vcd_input_read(
 
154
                avr_vcd_t * vcd )
 
155
{
 
156
        char line[1024];
 
157
 
 
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
 
161
                        continue;
 
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)
 
166
                        break;
 
167
        }
 
168
        return avr_vcd_fifo_isempty(&vcd->log);
 
169
}
 
170
 
 
171
/*
 
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
 
178
 */
 
179
static avr_cycle_count_t
 
180
_avr_vcd_input_timer(
 
181
                struct avr_t * avr,
 
182
                avr_cycle_count_t when,
 
183
                void * param)
 
184
{
 
185
        avr_vcd_t * vcd = param;
 
186
 
 
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);
 
190
 
 
191
        if (avr_vcd_fifo_isempty(&vcd->log)) {
 
192
                printf("%s DONE but why are we here?\n", __func__);
 
193
                return 0;
 
194
        }
 
195
 
 
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
 
201
                        break;
 
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);
 
206
        }
 
207
 
 
208
        if (avr_vcd_fifo_isempty(&vcd->log)) {
 
209
                AVR_LOG(vcd->avr, LOG_TRACE,
 
210
                                "%s Finished reading, ending simavr\n",
 
211
                                vcd->filename);
 
212
                avr->state = cpu_Done;
 
213
                return 0;
 
214
        }
 
215
        log = avr_vcd_fifo_read_at(&vcd->log, 0);
 
216
 
 
217
        when += avr_usec_to_cycles(avr, log.when - stamp);
 
218
 
 
219
        return when;
 
220
}
 
221
 
 
222
int
 
223
avr_vcd_init_input(
 
224
                struct avr_t * avr,
 
225
                const char * filename,  // filename to read
 
226
                avr_vcd_t * vcd )               // vcd struct to initialize
 
227
{
 
228
        memset(vcd, 0, sizeof(avr_vcd_t));
 
229
        vcd->avr = avr;
 
230
        vcd->filename = strdup(filename);
 
231
 
 
232
        vcd->input = fopen(vcd->filename, "r");
 
233
        if (!vcd->input) {
 
234
                perror(filename);
 
235
                return -1;
 
236
        }
 
237
        char line[1024];
 
238
        argv_p v = NULL;
 
239
 
 
240
        while (fgets(line, sizeof(line), vcd->input)) {
 
241
                if (!line[0])   // technically can't happen, but make sure next line works
 
242
                        continue;
 
243
                v = argv_parse(v, line);
 
244
 
 
245
                // we are done reading headers, got our first timestamp
 
246
                if (v->line[0] == '#') {
 
247
                        vcd->start = 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);
 
251
                        break;
 
252
                }
 
253
                // ignore multiline stuff
 
254
                if (v->line[0] != '$')
 
255
                        continue;
 
256
 
 
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];
 
260
 
 
261
                if (keyword == end)
 
262
                        keyword = NULL;
 
263
                if (!keyword)
 
264
                        continue;
 
265
 
 
266
                if (!strcmp(keyword, "$timescale")) {
 
267
                        double cnt = 0;
 
268
                        vcd->vcd_to_us = 1;
 
269
                        char *si = v->argv[1];
 
270
                        while (si && *si && isdigit(*si))
 
271
                                cnt = (cnt * 10) + (*si++ - '0');
 
272
                        while (*si == ' ')
 
273
                                si++;
 
274
                        if (!*si)
 
275
                                si = v->argv[2];
 
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];
 
281
 
 
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));
 
286
 
 
287
                        vcd->signal_count++;
 
288
                }
 
289
        }
 
290
        // reuse this one
 
291
        vcd->input_line = v;
 
292
 
 
293
        for (int i = 0; i < vcd->signal_count; i++) {
 
294
                AVR_LOG(vcd->avr, LOG_TRACE, "%s %2d '%c' %s : size %d\n",
 
295
                                __func__, i,
 
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, "_");
 
302
                        int index = 0;
 
303
                        if (dup)
 
304
                                index = atoi(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);
 
309
                                if (irq) {
 
310
                                        vcd->signal[i].irq.flags = IRQ_FLAG_INIT;
 
311
                                        avr_connect_irq(&vcd->signal[i].irq, irq);
 
312
                                } else
 
313
                                        AVR_LOG(vcd->avr, LOG_WARNING,
 
314
                                                        "%s IRQ was not found\n",
 
315
                                                        vcd->signal[i].name);
 
316
                                continue;
 
317
                        }
 
318
                        AVR_LOG(vcd->avr, LOG_WARNING,
 
319
                                        "%s is an invalid IRQ format\n",
 
320
                                        vcd->signal[i].name);
 
321
                }*/
 
322
        }
 
323
        return 0;
 
324
}
 
325
 
 
326
void
 
327
avr_vcd_close(
 
328
                avr_vcd_t * vcd)
 
329
{
 
330
        avr_vcd_stop(vcd);
 
331
 
 
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];
 
335
 
 
336
                avr_free_irq(&s->irq, 1);
 
337
        }
 
338
 
 
339
        if (vcd->filename) {
 
340
                free(vcd->filename);
 
341
                vcd->filename = NULL;
 
342
        }
 
343
}
 
344
 
 
345
static char *
 
346
_avr_vcd_get_float_signal_text(
 
347
                avr_vcd_signal_t * s,
 
348
                char * out)
 
349
{
 
350
        char * dst = out;
 
351
 
 
352
        if (s->size > 1)
 
353
                *dst++ = 'b';
 
354
 
 
355
        for (int i = s->size; i > 0; i--)
 
356
                *dst++ = 'x';
 
357
        if (s->size > 1)
 
358
                *dst++ = ' ';
 
359
        *dst++ = s->alias;
 
360
        *dst = 0;
 
361
        return out;
 
362
}
 
363
 
 
364
static char *
 
365
_avr_vcd_get_signal_text(
 
366
                avr_vcd_signal_t * s,
 
367
                char * out,
 
368
                uint32_t value)
 
369
{
 
370
        char * dst = out;
 
371
 
 
372
        if (s->size > 1)
 
373
                *dst++ = 'b';
 
374
 
 
375
        for (int i = s->size; i > 0; i--)
 
376
                *dst++ = value & (1 << (i-1)) ? '1' : '0';
 
377
        if (s->size > 1)
 
378
                *dst++ = ' ';
 
379
        *dst++ = s->alias;
 
380
        *dst = 0;
 
381
        return out;
 
382
}
 
383
 
 
384
static void
 
385
avr_vcd_flush_log(
 
386
                avr_vcd_t * vcd)
 
387
{
 
388
#if AVR_VCD_MAX_SIGNALS > 32
 
389
        uint64_t seen = 0;
 
390
#else
 
391
        uint32_t seen = 0;
 
392
#endif
 
393
        uint64_t oldbase = 0;   // make sure it's different
 
394
        char out[48];
 
395
 
 
396
        if (avr_vcd_fifo_isempty(&vcd->log) || !vcd->output)
 
397
                return;
 
398
 
 
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;
 
403
 
 
404
                /*
 
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.
 
408
                 *
 
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.
 
411
                 */
 
412
                if (base == oldbase &&
 
413
                                (seen & (1 << l.sigindex)))
 
414
                        base++; // this forces a new timestamp
 
415
 
 
416
                if (base > oldbase || !seen) {
 
417
                        seen = 0;
 
418
                        fprintf(vcd->output, "#%" PRIu64  "\n", base);
 
419
                        oldbase = base;
 
420
                }
 
421
                // mark this trace as seen for this timestamp
 
422
                seen |= (1 << l.sigindex);
 
423
                fprintf(vcd->output, "%s\n",
 
424
                                l.floating ?
 
425
                                        _avr_vcd_get_float_signal_text(
 
426
                                                        &vcd->signal[l.sigindex],
 
427
                                                        out) :
 
428
                                        _avr_vcd_get_signal_text(
 
429
                                                        &vcd->signal[l.sigindex],
 
430
                                                        out, l.value));
 
431
        }
 
432
}
 
433
 
 
434
static avr_cycle_count_t
 
435
_avr_vcd_timer(
 
436
                struct avr_t * avr,
 
437
                avr_cycle_count_t when,
 
438
                void * param)
 
439
{
 
440
        avr_vcd_t * vcd = param;
 
441
        avr_vcd_flush_log(vcd);
 
442
        return when + vcd->period;
 
443
}
 
444
 
 
445
static void
 
446
_avr_vcd_notify(
 
447
                struct avr_irq_t * irq,
 
448
                uint32_t value,
 
449
                void * param)
 
450
{
 
451
        avr_vcd_t * vcd = (avr_vcd_t *)param;
 
452
 
 
453
        if (!vcd->output)
 
454
                return;
 
455
 
 
456
        avr_vcd_signal_t * s = (avr_vcd_signal_t*)irq;
 
457
        avr_vcd_log_t l = {
 
458
                .sigindex = s->irq.irq,
 
459
                .when = vcd->avr->cycle,
 
460
                .value = value,
 
461
                .floating = !!(avr_irq_get_flags(irq) & IRQ_FLAG_FLOATING),
 
462
        };
 
463
        if (avr_vcd_fifo_isfull(&vcd->log)) {
 
464
                AVR_LOG(vcd->avr, LOG_WARNING,
 
465
                                "%s FIFO Overload, flushing!\n",
 
466
                                __func__);
 
467
                /* Decrease period by a quarter, for next time */
 
468
                vcd->period -= vcd->period >> 2;
 
469
                avr_vcd_flush_log(vcd);
 
470
        }
 
471
        avr_vcd_fifo_write(&vcd->log, l);
 
472
}
 
473
 
 
474
int
 
475
avr_vcd_add_signal(
 
476
                avr_vcd_t * vcd,
 
477
                avr_irq_t * signal_irq,
 
478
                int signal_bit_size,
 
479
                const char * name )
 
480
{
 
481
        if (vcd->signal_count == AVR_VCD_MAX_SIGNALS)
 
482
                return -1;
 
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 ;
 
488
 
 
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);
 
494
        else
 
495
                sprintf(iname, ">vcd.%s", name);
 
496
 
 
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);
 
500
 
 
501
        avr_connect_irq(signal_irq, &s->irq);
 
502
        return 0;
 
503
}
 
504
 
 
505
 
 
506
int
 
507
avr_vcd_start(
 
508
                avr_vcd_t * vcd)
 
509
{
 
510
        vcd->start = vcd->avr->cycle;
 
511
        avr_vcd_fifo_reset(&vcd->log);
 
512
 
 
513
        if (vcd->input) {
 
514
                /*
 
515
                 * nothing to do here, the first cycle timer will take care
 
516
                 * if it.
 
517
                 */
 
518
                return 0;
 
519
        }
 
520
        if (vcd->output)
 
521
                avr_vcd_stop(vcd);
 
522
        vcd->output = fopen(vcd->filename, "w");
 
523
        if (vcd->output == NULL) {
 
524
                perror(vcd->filename);
 
525
                return -1;
 
526
        }
 
527
 
 
528
        fprintf(vcd->output, "$timescale 10ns $end\n"); // 10ns base, aka 100MHz
 
529
        fprintf(vcd->output, "$scope module logic $end\n");
 
530
 
 
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);
 
534
        }
 
535
 
 
536
        fprintf(vcd->output, "$upscope $end\n");
 
537
        fprintf(vcd->output, "$enddefinitions $end\n");
 
538
 
 
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];
 
542
                char out[48];
 
543
                fprintf(vcd->output, "%s\n",
 
544
                                _avr_vcd_get_float_signal_text(s, out));
 
545
        }
 
546
        fprintf(vcd->output, "$end\n");
 
547
        avr_cycle_timer_register(vcd->avr, vcd->period, _avr_vcd_timer, vcd);
 
548
        return 0;
 
549
}
 
550
 
 
551
int
 
552
avr_vcd_stop(
 
553
                avr_vcd_t * vcd)
 
554
{
 
555
        avr_cycle_timer_cancel(vcd->avr, _avr_vcd_timer, vcd);
 
556
        avr_cycle_timer_cancel(vcd->avr, _avr_vcd_input_timer, vcd);
 
557
 
 
558
        avr_vcd_flush_log(vcd);
 
559
 
 
560
        if (vcd->input_line)
 
561
                free(vcd->input_line);
 
562
        vcd->input_line = NULL;
 
563
        if (vcd->input)
 
564
                fclose(vcd->input);
 
565
        vcd->input = NULL;
 
566
        if (vcd->output)
 
567
                fclose(vcd->output);
 
568
        vcd->output = NULL;
 
569
        return 0;
 
570
}
 
571
 
 
572