Merge pull request #12 from ponty/logger2
[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<inttypes.h>
29 #include "sim_vcd_file.h"
30 #include "sim_avr.h"
31 #include "sim_time.h"
32
33 void _avr_vcd_notify(struct avr_irq_t * irq, uint32_t value, void * param);
34
35 int avr_vcd_init(struct avr_t * avr, const char * filename, avr_vcd_t * vcd, uint32_t period)
36 {
37         memset(vcd, 0, sizeof(avr_vcd_t));
38         vcd->avr = avr;
39         strncpy(vcd->filename, filename, sizeof(vcd->filename));
40         vcd->period = avr_usec_to_cycles(vcd->avr, period);
41
42         // No longer initializes the IRQs here, they can be initialized on the
43         // fly when traces are added
44         
45         return 0;
46 }
47
48 void avr_vcd_close(avr_vcd_t * vcd)
49 {
50         avr_vcd_stop(vcd);
51 }
52
53 void _avr_vcd_notify(struct avr_irq_t * irq, uint32_t value, void * param)
54 {
55         avr_vcd_t * vcd = (avr_vcd_t *)param;
56         if (!vcd->output)
57                 return;
58
59         /*
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
62          */
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);
71                 }
72                 if (!vcd->log) {
73                         AVR_LOG(vcd->avr, LOG_ERROR, "%s log resizing, out of memory (%d)!\n",
74                                         __func__, (int)vcd->logsize);
75                         vcd->logsize = 0;
76                         return;
77                 }
78         }
79         avr_vcd_signal_t * s = (avr_vcd_signal_t*)irq;
80         avr_vcd_log_t *l = &vcd->log[vcd->logindex++];
81         l->signal = s;
82         l->when = vcd->avr->cycle;
83         l->value = value;
84 }
85
86 static char * _avr_vcd_get_signal_text(avr_vcd_signal_t * s, char * out, uint32_t value)
87 {
88         char * dst = out;
89                 
90         if (s->size > 1)
91                 *dst++ = 'b';
92         
93         for (int i = s->size; i > 0; i--)
94                 *dst++ = value & (1 << (i-1)) ? '1' : '0';
95         if (s->size > 1)
96                 *dst++ = ' ';
97         *dst++ = s->alias;
98         *dst = 0;
99         return out;
100 }
101
102 static void avr_vcd_flush_log(avr_vcd_t * vcd)
103 {
104 #if AVR_VCD_MAX_SIGNALS > 32
105         uint64_t seen = 0;
106 #else
107         uint32_t seen = 0;
108 #endif
109         uint64_t oldbase = 0;   // make sure it's different
110         char out[48];
111
112         if (!vcd->logindex)
113                 return;
114 //      printf("avr_vcd_flush_log %d\n", vcd->logindex);
115
116
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
120
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
128                         
129                 if (base > oldbase || li == 0) {
130                         seen = 0;
131                         fprintf(vcd->output, "#%" PRIu64  "\n", base);
132                         oldbase = base;
133                 }
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));
136         }
137         vcd->logindex = 0;
138 }
139
140 static avr_cycle_count_t _avr_vcd_timer(struct avr_t * avr, avr_cycle_count_t when, void * param)
141 {
142         avr_vcd_t * vcd = param;
143         avr_vcd_flush_log(vcd);
144         return when + vcd->period;
145 }
146
147 int avr_vcd_add_signal(avr_vcd_t * vcd, 
148         avr_irq_t * signal_irq,
149         int signal_bit_size,
150         const char * name )
151 {
152         if (vcd->signal_count == AVR_VCD_MAX_SIGNALS)
153                 return -1;
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 ;
159
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);
165         else
166                 sprintf(iname, ">vcd.%s", name);
167
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);
171
172         avr_connect_irq(signal_irq, &s->irq);
173         return 0;
174 }
175
176
177 int avr_vcd_start(avr_vcd_t * vcd)
178 {
179         if (vcd->output)
180                 avr_vcd_stop(vcd);
181         vcd->output = fopen(vcd->filename, "w");
182         if (vcd->output == NULL) {
183                 perror(vcd->filename);
184                 return -1;
185         }
186
187         fprintf(vcd->output, "$timescale 1ns $end\n");  // 1ns base
188         fprintf(vcd->output, "$scope module logic $end\n");
189
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);
193         }
194
195         fprintf(vcd->output, "$upscope $end\n");
196         fprintf(vcd->output, "$enddefinitions $end\n");
197         
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];
201                 char out[48];
202                 fprintf(vcd->output, "%s\n", _avr_vcd_get_signal_text(s, out, s->irq.value));
203         }
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);   
207         return 0;
208 }
209
210 int avr_vcd_stop(avr_vcd_t * vcd)
211 {
212         avr_cycle_timer_cancel(vcd->avr, _avr_vcd_timer, vcd);
213
214         avr_vcd_flush_log(vcd);
215         
216         if (vcd->output)
217                 fclose(vcd->output);
218         vcd->output = NULL;
219         return 0;
220 }
221
222