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/>.
29 #include "sim_vcd_file.h"
33 void _avr_vcd_notify(struct avr_irq_t * irq, uint32_t value, void * param);
35 int avr_vcd_init(struct avr_t * avr, const char * filename, avr_vcd_t * vcd, uint32_t period)
37 memset(vcd, 0, sizeof(avr_vcd_t));
39 strncpy(vcd->filename, filename, sizeof(vcd->filename));
40 vcd->period = avr_usec_to_cycles(vcd->avr, period);
42 for (int i = 0; i < AVR_VCD_MAX_SIGNALS; i++) {
43 avr_init_irq(&avr->irq_pool, &vcd->signal[i].irq, i, 1, NULL /* TODO IRQ name */);
44 avr_irq_register_notify(&vcd->signal[i].irq, _avr_vcd_notify, vcd);
50 void avr_vcd_close(avr_vcd_t * vcd)
55 void _avr_vcd_notify(struct avr_irq_t * irq, uint32_t value, void * param)
57 avr_vcd_t * vcd = (avr_vcd_t *)param;
62 * buffer starts empty, the first trace will resize it to AVR_VCD_LOG_CHUNK_SIZE,
63 * further growth will resize it accordingly. There's a bit of
65 if (vcd->logindex >= vcd->logsize) {
66 vcd->logsize += AVR_VCD_LOG_CHUNK_SIZE;
67 vcd->log = (avr_vcd_log_p)realloc(vcd->log, vcd->logsize * sizeof(vcd->log[0]));
68 AVR_LOG(vcd->avr, LOG_TRACE, "%s trace buffer resized to %d\n",
69 __func__, (int)vcd->logsize);
70 if ((vcd->logsize / AVR_VCD_LOG_CHUNK_SIZE) == 5) {
71 AVR_LOG(vcd->avr, LOG_WARNING, "%s log size runnaway (%d) flush problem?\n",
72 __func__, (int)vcd->logsize);
75 AVR_LOG(vcd->avr, LOG_ERROR, "%s log resizing, out of memory (%d)!\n",
76 __func__, (int)vcd->logsize);
81 avr_vcd_signal_t * s = (avr_vcd_signal_t*)irq;
82 avr_vcd_log_t *l = &vcd->log[vcd->logindex++];
84 l->when = vcd->avr->cycle;
88 static char * _avr_vcd_get_signal_text(avr_vcd_signal_t * s, char * out, uint32_t value)
95 for (int i = s->size; i > 0; i--)
96 *dst++ = value & (1 << (i-1)) ? '1' : '0';
104 static void avr_vcd_flush_log(avr_vcd_t * vcd)
106 #if AVR_VCD_MAX_SIGNALS > 32
111 uint64_t oldbase = 0; // make sure it's different
116 // printf("avr_vcd_flush_log %d\n", vcd->logindex);
119 for (uint32_t li = 0; li < vcd->logindex; li++) {
120 avr_vcd_log_t *l = &vcd->log[li];
121 uint64_t base = avr_cycles_to_nsec(vcd->avr, l->when - vcd->start); // 1ns base
123 // if that trace was seen in this nsec already, we fudge the base time
124 // to make sure the new value is offset by one nsec, to make sure we get
125 // at least a small pulse on the waveform
126 // This is a bit of a fudge, but it is the only way to represent very
127 // short"pulses" that are still visible on the waveform.
128 if (base == oldbase && seen & (1 << l->signal->irq.irq))
129 base++; // this forces a new timestamp
131 if (base > oldbase || li == 0) {
133 fprintf(vcd->output, "#%" PRIu64 "\n", base);
136 seen |= (1 << l->signal->irq.irq); // mark this trace as seen for this timestamp
137 fprintf(vcd->output, "%s\n", _avr_vcd_get_signal_text(l->signal, out, l->value));
142 static avr_cycle_count_t _avr_vcd_timer(struct avr_t * avr, avr_cycle_count_t when, void * param)
144 avr_vcd_t * vcd = param;
145 avr_vcd_flush_log(vcd);
146 return when + vcd->period;
149 int avr_vcd_add_signal(avr_vcd_t * vcd,
150 avr_irq_t * signal_irq,
154 if (vcd->signal_count == AVR_VCD_MAX_SIGNALS)
156 avr_vcd_signal_t * s = &vcd->signal[vcd->signal_count++];
157 strncpy(s->name, name, sizeof(s->name));
158 s->size = signal_bit_size;
159 s->alias = ' ' + vcd->signal_count ;
160 avr_connect_irq(signal_irq, &s->irq);
165 int avr_vcd_start(avr_vcd_t * vcd)
169 vcd->output = fopen(vcd->filename, "w");
170 if (vcd->output == NULL) {
171 perror(vcd->filename);
175 fprintf(vcd->output, "$timescale 1ns $end\n"); // 1ns base
176 fprintf(vcd->output, "$scope module logic $end\n");
178 for (int i = 0; i < vcd->signal_count; i++) {
179 fprintf(vcd->output, "$var wire %d %c %s $end\n",
180 vcd->signal[i].size, vcd->signal[i].alias, vcd->signal[i].name);
183 fprintf(vcd->output, "$upscope $end\n");
184 fprintf(vcd->output, "$enddefinitions $end\n");
186 fprintf(vcd->output, "$dumpvars\n");
187 for (int i = 0; i < vcd->signal_count; i++) {
188 avr_vcd_signal_t * s = &vcd->signal[i];
190 fprintf(vcd->output, "%s\n", _avr_vcd_get_signal_text(s, out, s->irq.value));
192 fprintf(vcd->output, "$end\n");
193 vcd->start = vcd->avr->cycle;
194 avr_cycle_timer_register(vcd->avr, vcd->period, _avr_vcd_timer, vcd);
198 int avr_vcd_stop(avr_vcd_t * vcd)
200 avr_cycle_timer_cancel(vcd->avr, _avr_vcd_timer, vcd);
202 avr_vcd_flush_log(vcd);