misc: Point to correct simavr include dirs
[simavr] / simavr / sim / sim_vcd_file.c
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 "sim_vcd_file.h"
29 #include "sim_avr.h"
30 #include "sim_time.h"
31
32 void _avr_vcd_notify(struct avr_irq_t * irq, uint32_t value, void * param);
33
34 int avr_vcd_init(struct avr_t * avr, const char * filename, avr_vcd_t * vcd, uint32_t period)
35 {
36         memset(vcd, 0, sizeof(avr_vcd_t));
37         vcd->avr = avr;
38         strncpy(vcd->filename, filename, sizeof(vcd->filename));
39         vcd->period = avr_usec_to_cycles(vcd->avr, period);
40         
41         for (int i = 0; i < AVR_VCD_MAX_SIGNALS; i++) {
42                 avr_init_irq(&avr->irq_pool, &vcd->signal[i].irq, i, 1, NULL /* TODO IRQ name */);
43                 avr_irq_register_notify(&vcd->signal[i].irq, _avr_vcd_notify, vcd);
44         }
45         
46         return 0;
47 }
48
49 void avr_vcd_close(avr_vcd_t * vcd)
50 {
51         avr_vcd_stop(vcd);
52 }
53
54 void _avr_vcd_notify(struct avr_irq_t * irq, uint32_t value, void * param)
55 {
56         avr_vcd_t * vcd = (avr_vcd_t *)param;
57         if (!vcd->output)
58                 return;
59
60         /*
61          * buffer starts empty, the first trace will resize it to AVR_VCD_LOG_CHUNK_SIZE,
62          * further growth will resize it accordingly. There's a bit of
63          */
64         if (vcd->logindex >= vcd->logsize) {
65                 vcd->logsize += AVR_VCD_LOG_CHUNK_SIZE;
66                 vcd->log = (avr_vcd_log_p)realloc(vcd->log, vcd->logsize * sizeof(vcd->log[0]));
67                 AVR_LOG(vcd->avr, LOG_TRACE, "%s trace buffer resized to %d\n",
68                                 __func__, (int)vcd->logsize);
69                 if ((vcd->logsize / AVR_VCD_LOG_CHUNK_SIZE) == 5) {
70                         AVR_LOG(vcd->avr, LOG_WARNING, "%s log size runnaway (%d) flush problem?\n",
71                                         __func__, (int)vcd->logsize);
72                 }
73                 if (!vcd->log) {
74                         AVR_LOG(vcd->avr, LOG_ERROR, "%s log resizing, out of memory (%d)!\n",
75                                         __func__, (int)vcd->logsize);
76                         vcd->logsize = 0;
77                         return;
78                 }
79         }
80         avr_vcd_signal_t * s = (avr_vcd_signal_t*)irq;
81         avr_vcd_log_t *l = &vcd->log[vcd->logindex++];
82         l->signal = s;
83         l->when = vcd->avr->cycle;
84         l->value = value;
85 }
86
87 static char * _avr_vcd_get_signal_text(avr_vcd_signal_t * s, char * out, uint32_t value)
88 {
89         char * dst = out;
90                 
91         if (s->size > 1)
92                 *dst++ = 'b';
93         
94         for (int i = s->size; i > 0; i--)
95                 *dst++ = value & (1 << (i-1)) ? '1' : '0';
96         if (s->size > 1)
97                 *dst++ = ' ';
98         *dst++ = s->alias;
99         *dst = 0;
100         return out;
101 }
102
103 static void avr_vcd_flush_log(avr_vcd_t * vcd)
104 {
105 #if AVR_VCD_MAX_SIGNALS > 32
106         uint64_t seen = 0;
107 #else
108         uint32_t seen = 0;
109 #endif
110         uint64_t oldbase = 0;   // make sure it's different
111         char out[48];
112
113         if (!vcd->logindex)
114                 return;
115 //      printf("avr_vcd_flush_log %d\n", vcd->logindex);
116
117
118         for (uint32_t li = 0; li < vcd->logindex; li++) {
119                 avr_vcd_log_t *l = &vcd->log[li];
120                 uint64_t base = avr_cycles_to_nsec(vcd->avr, l->when - vcd->start);     // 1ns base
121
122                 // if that trace was seen in this nsec already, we fudge the base time
123                 // to make sure the new value is offset by one nsec, to make sure we get
124                 // at least a small pulse on the waveform
125                 // This is a bit of a fudge, but it is the only way to represent very 
126                 // short"pulses" that are still visible on the waveform.
127                 if (base == oldbase && seen & (1 << l->signal->irq.irq))
128                         base++; // this forces a new timestamp
129                         
130                 if (base > oldbase || li == 0) {
131                         seen = 0;
132                         fprintf(vcd->output, "#%llu\n", (long long unsigned int)base);
133                         oldbase = base;
134                 }
135                 seen |= (1 << l->signal->irq.irq);      // mark this trace as seen for this timestamp
136                 fprintf(vcd->output, "%s\n", _avr_vcd_get_signal_text(l->signal, out, l->value));
137         }
138         vcd->logindex = 0;
139 }
140
141 static avr_cycle_count_t _avr_vcd_timer(struct avr_t * avr, avr_cycle_count_t when, void * param)
142 {
143         avr_vcd_t * vcd = param;
144         avr_vcd_flush_log(vcd);
145         return when + vcd->period;
146 }
147
148 int avr_vcd_add_signal(avr_vcd_t * vcd, 
149         avr_irq_t * signal_irq,
150         int signal_bit_size,
151         const char * name )
152 {
153         if (vcd->signal_count == AVR_VCD_MAX_SIGNALS)
154                 return -1;
155         avr_vcd_signal_t * s = &vcd->signal[vcd->signal_count++];
156         strncpy(s->name, name, sizeof(s->name));
157         s->size = signal_bit_size;
158         s->alias = ' ' + vcd->signal_count ;
159         avr_connect_irq(signal_irq, &s->irq);
160         return 0;
161 }
162
163
164 int avr_vcd_start(avr_vcd_t * vcd)
165 {
166         if (vcd->output)
167                 avr_vcd_stop(vcd);
168         vcd->output = fopen(vcd->filename, "w");
169         if (vcd->output == NULL) {
170                 perror(vcd->filename);
171                 return -1;
172         }
173
174         fprintf(vcd->output, "$timescale 1ns $end\n");  // 1ns base
175         fprintf(vcd->output, "$scope module logic $end\n");
176
177         for (int i = 0; i < vcd->signal_count; i++) {
178                 fprintf(vcd->output, "$var wire %d %c %s $end\n",
179                         vcd->signal[i].size, vcd->signal[i].alias, vcd->signal[i].name);
180         }
181
182         fprintf(vcd->output, "$upscope $end\n");
183         fprintf(vcd->output, "$enddefinitions $end\n");
184         
185         fprintf(vcd->output, "$dumpvars\n");
186         for (int i = 0; i < vcd->signal_count; i++) {
187                 avr_vcd_signal_t * s = &vcd->signal[i];
188                 char out[48];
189                 fprintf(vcd->output, "%s\n", _avr_vcd_get_signal_text(s, out, s->irq.value));
190         }
191         fprintf(vcd->output, "$end\n");
192         vcd->start = vcd->avr->cycle;
193         avr_cycle_timer_register(vcd->avr, vcd->period, _avr_vcd_timer, vcd);   
194         return 0;
195 }
196
197 int avr_vcd_stop(avr_vcd_t * vcd)
198 {
199         avr_cycle_timer_cancel(vcd->avr, _avr_vcd_timer, vcd);
200
201         avr_vcd_flush_log(vcd);
202         
203         if (vcd->output)
204                 fclose(vcd->output);
205         vcd->output = NULL;
206         return 0;
207 }
208
209