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 // No longer initializes the IRQs here, they can be initialized on the
43 // fly when traces are added
48 void avr_vcd_close(avr_vcd_t * vcd)
53 void _avr_vcd_notify(struct avr_irq_t * irq, uint32_t value, void * param)
55 avr_vcd_t * vcd = (avr_vcd_t *)param;
60 * buffer starts empty, the first trace will resize it to AVR_VCD_LOG_CHUNK_SIZE,
61 * further growth will resize it accordingly. There's a bit of
63 if (vcd->logindex >= vcd->logsize) {
64 vcd->logsize += AVR_VCD_LOG_CHUNK_SIZE;
65 vcd->log = (avr_vcd_log_p)realloc(vcd->log, vcd->logsize * sizeof(vcd->log[0]));
66 AVR_LOG(vcd->avr, LOG_TRACE, "%s trace buffer resized to %d\n",
67 __func__, (int)vcd->logsize);
68 if ((vcd->logsize / AVR_VCD_LOG_CHUNK_SIZE) == 5) {
69 AVR_LOG(vcd->avr, LOG_WARNING, "%s log size runnaway (%d) flush problem?\n",
70 __func__, (int)vcd->logsize);
73 AVR_LOG(vcd->avr, LOG_ERROR, "%s log resizing, out of memory (%d)!\n",
74 __func__, (int)vcd->logsize);
79 avr_vcd_signal_t * s = (avr_vcd_signal_t*)irq;
80 avr_vcd_log_t *l = &vcd->log[vcd->logindex++];
82 l->when = vcd->avr->cycle;
86 static char * _avr_vcd_get_signal_text(avr_vcd_signal_t * s, char * out, uint32_t value)
93 for (int i = s->size; i > 0; i--)
94 *dst++ = value & (1 << (i-1)) ? '1' : '0';
102 static void avr_vcd_flush_log(avr_vcd_t * vcd)
104 #if AVR_VCD_MAX_SIGNALS > 32
109 uint64_t oldbase = 0; // make sure it's different
114 // printf("avr_vcd_flush_log %d\n", vcd->logindex);
117 for (uint32_t li = 0; li < vcd->logindex; li++) {
118 avr_vcd_log_t *l = &vcd->log[li];
119 uint64_t base = avr_cycles_to_nsec(vcd->avr, l->when - vcd->start); // 1ns base
121 // if that trace was seen in this nsec already, we fudge the base time
122 // to make sure the new value is offset by one nsec, to make sure we get
123 // at least a small pulse on the waveform
124 // This is a bit of a fudge, but it is the only way to represent very
125 // short"pulses" that are still visible on the waveform.
126 if (base == oldbase && seen & (1 << l->signal->irq.irq))
127 base++; // this forces a new timestamp
129 if (base > oldbase || li == 0) {
131 fprintf(vcd->output, "#%" PRIu64 "\n", base);
134 seen |= (1 << l->signal->irq.irq); // mark this trace as seen for this timestamp
135 fprintf(vcd->output, "%s\n", _avr_vcd_get_signal_text(l->signal, out, l->value));
140 static avr_cycle_count_t _avr_vcd_timer(struct avr_t * avr, avr_cycle_count_t when, void * param)
142 avr_vcd_t * vcd = param;
143 avr_vcd_flush_log(vcd);
144 return when + vcd->period;
147 int avr_vcd_add_signal(avr_vcd_t * vcd,
148 avr_irq_t * signal_irq,
152 if (vcd->signal_count == AVR_VCD_MAX_SIGNALS)
154 int index = vcd->signal_count++;
155 avr_vcd_signal_t * s = &vcd->signal[index];
156 strncpy(s->name, name, sizeof(s->name));
157 s->size = signal_bit_size;
158 s->alias = ' ' + vcd->signal_count ;
160 /* manufacture a nice IRQ name */
161 int l = strlen(name);
162 char iname[10 + l + 1];
163 if (signal_bit_size > 1)
164 sprintf(iname, "%d>vcd.%s", signal_bit_size, name);
166 sprintf(iname, ">vcd.%s", name);
168 const char * names[1] = { iname };
169 avr_init_irq(&vcd->avr->irq_pool, &s->irq, index, 1, names);
170 avr_irq_register_notify(&s->irq, _avr_vcd_notify, vcd);
172 avr_connect_irq(signal_irq, &s->irq);
177 int avr_vcd_start(avr_vcd_t * vcd)
181 vcd->output = fopen(vcd->filename, "w");
182 if (vcd->output == NULL) {
183 perror(vcd->filename);
187 fprintf(vcd->output, "$timescale 1ns $end\n"); // 1ns base
188 fprintf(vcd->output, "$scope module logic $end\n");
190 for (int i = 0; i < vcd->signal_count; i++) {
191 fprintf(vcd->output, "$var wire %d %c %s $end\n",
192 vcd->signal[i].size, vcd->signal[i].alias, vcd->signal[i].name);
195 fprintf(vcd->output, "$upscope $end\n");
196 fprintf(vcd->output, "$enddefinitions $end\n");
198 fprintf(vcd->output, "$dumpvars\n");
199 for (int i = 0; i < vcd->signal_count; i++) {
200 avr_vcd_signal_t * s = &vcd->signal[i];
202 fprintf(vcd->output, "%s\n", _avr_vcd_get_signal_text(s, out, s->irq.value));
204 fprintf(vcd->output, "$end\n");
205 vcd->start = vcd->avr->cycle;
206 avr_cycle_timer_register(vcd->avr, vcd->period, _avr_vcd_timer, vcd);
210 int avr_vcd_stop(avr_vcd_t * vcd)
212 avr_cycle_timer_cancel(vcd->avr, _avr_vcd_timer, vcd);
214 avr_vcd_flush_log(vcd);